Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ Use the following resources to learn about NeMo Fabric:

- [Example Notebooks](examples/notebooks/README.md) provide a guided tour of the Python SDK.
- The [Python SDK guide](docs/sdk/python.mdx) covers typed configuration,
planning, diagnostics, requests, multi-turn runtimes, streaming, parallelism,
results, and errors.
planning, diagnostics, requests, multi-turn runtimes, bounded runtime health
checks, streaming, parallelism, results, and errors.
- The [Experimentation CLI guide](docs/experimentation/cli.mdx) covers presets,
maintained examples, and editable application scaffolds.
- The [getting started overview](docs/about-nemo-fabric/overview.mdx) explains
Expand Down
4 changes: 4 additions & 0 deletions adapter-contract/python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ An adapter descriptor opts into the southbound configuration with
`config.input=agent_config`. Python adapters using the optional common
lifecycle host pass `AgentConfig.from_mapping` as the `config_loader`.

The models module also exports `AdapterHealthRequest`, `AdapterHealthResult`,
`AdapterReadiness`, and `HealthCheck` for optional bounded health hooks used by
the maintained local lifecycle hosts.

## Install

Install the package directly when developing a Python adapter:
Expand Down
4 changes: 4 additions & 0 deletions adapter-contract/python/pypi.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ NeMo Fabric delivers `AgentConfig` as the southbound configuration. Python
adapters using the optional common lifecycle host pass
`AgentConfig.from_mapping` as the `config_loader`.

The models module also exports `AdapterHealthRequest`, `AdapterHealthResult`,
`AdapterReadiness`, and `HealthCheck` for optional bounded health hooks used by
the maintained local lifecycle hosts.

## Install

Install the package directly when developing a Python adapter:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,79 @@ def _validate(self) -> None:
raise ContractValidationError("succeeded result must not include an error")


class RuntimeReadiness(StrEnum):
"""Whether an adapter knows its runtime can currently accept work."""

READY = "ready"
NOT_READY = "not_ready"
UNKNOWN = "unknown"


class HealthCheckStatus(StrEnum):
"""Outcome of one adapter health check."""

OK = "ok"
FAILED = "failed"
UNKNOWN = "unknown"
UNSUPPORTED = "unsupported"


@dataclass(slots=True, kw_only=True)
class HealthCheck(ContractModel):
"""One timestamped adapter or dependency health observation."""

name: str
status: HealthCheckStatus
reason_code: str
observed_at_millis: int
age_millis: int
message: str | None = _optional()
metadata: dict[str, JsonValue] = _json_dict()

def _validate(self) -> None:
_nonblank(self.name, "name")
_nonblank(self.reason_code, "reason_code")
_bounded_int(self.observed_at_millis, "observed_at_millis", (1 << 128) - 1)
_bounded_int(self.age_millis, "age_millis", (1 << 64) - 1)
if self.message is not None:
_nonblank(self.message, "message")


@dataclass(slots=True, kw_only=True)
class AdapterHealthRequest(ContractModel):
"""Budget passed to an optional adapter health hook."""

runtime_id: str
timeout_millis: int

def _validate(self) -> None:
_nonblank(self.runtime_id, "runtime_id")
_bounded_int(self.timeout_millis, "timeout_millis", (1 << 64) - 1)
if self.timeout_millis == 0:
raise ContractValidationError(
"must be greater than zero", path=("timeout_millis",)
)


@dataclass(slots=True, kw_only=True)
class AdapterReadiness(ContractModel):
"""Adapter-owned readiness observation."""

state: RuntimeReadiness
reason_code: str

def _validate(self) -> None:
_nonblank(self.reason_code, "reason_code")


@dataclass(slots=True, kw_only=True)
class AdapterHealthResult(ContractModel):
"""Optional adapter-specific contribution to runtime health."""

readiness: AdapterReadiness | None = _optional()
checks: list[HealthCheck] = _empty_list()


class ControlLocation(StrEnum):
"""Where Fabric control code runs relative to the task environment."""

Expand Down
6 changes: 6 additions & 0 deletions adapter-contract/typescript/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ adapter-contract package:

```typescript
import type {
AdapterHealthRequest,
AdapterHealthResult,
AgentRunRequest,
AgentRunResult,
} from "nemo-fabric-adapter-contract";
Expand All @@ -74,6 +76,10 @@ Fabric normalizes the result. Token counts originate from JSON Schema `uint64`
values but are represented as JavaScript `number`; values greater than
`Number.MAX_SAFE_INTEGER` cannot be represented exactly.

Use `AdapterHealthRequest` and `AdapterHealthResult` only for an optional,
bounded health method served by a compatible local lifecycle host. Health
checks must not invoke the agent or mutate runtime state.

## JSON Schemas

The package bundles byte-identical copies of the canonical schemas. Consumers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@
"description": "Whether an in-flight invocation can be cancelled.",
"type": "boolean"
},
"health": {
"default": false,
"description": "Whether the selected runtime exposes bounded health observations.",
"type": "boolean"
},
"metadata": {
"additionalProperties": true,
"description": "Additional adapter-specific capability metadata.",
Expand Down Expand Up @@ -296,6 +301,7 @@
"$ref": "#/$defs/RuntimeCapabilities",
"default": {
"cancellation": false,
"health": false,
"service": false,
"streaming": false,
"updates": false
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Request passed to an optional adapter health hook.",
"properties": {
"runtime_id": {
"description": "Runtime being inspected.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"timeout_millis": {
"description": "Remaining health budget in milliseconds.",
"format": "uint64",
"minimum": 1,
"type": "integer"
}
},
"required": [
"runtime_id",
"timeout_millis"
],
"title": "AdapterHealthRequest",
"type": "object"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{
"$defs": {
"AdapterReadiness": {
"additionalProperties": false,
"description": "Adapter-owned readiness observation.",
"properties": {
"reason_code": {
"description": "Stable machine-readable reason.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"state": {
"$ref": "#/$defs/RuntimeReadiness",
"description": "Adapter readiness state."
}
},
"required": [
"state",
"reason_code"
],
"type": "object"
},
"HealthCheck": {
"additionalProperties": false,
"description": "One timestamped runtime or adapter health observation.",
"properties": {
"age_millis": {
"description": "Age of the evidence when this report was assembled.",
"format": "uint64",
"minimum": 0,
"type": "integer"
},
"message": {
"description": "Optional human-readable diagnostic detail.",
"minLength": 1,
"pattern": "\\S",
"type": [
"string",
"null"
]
},
"metadata": {
"additionalProperties": true,
"description": "Additional non-sensitive check metadata.",
"type": "object"
},
"name": {
"description": "Stable, namespaced check name.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"observed_at_millis": {
"description": "Unix timestamp in milliseconds when the evidence was observed.",
"format": "uint128",
"minimum": 0,
"type": "integer"
},
"reason_code": {
"description": "Stable machine-readable reason.",
"minLength": 1,
"pattern": "\\S",
"type": "string"
},
"status": {
"$ref": "#/$defs/HealthCheckStatus",
"description": "Structured check outcome."
}
},
"required": [
"name",
"status",
"reason_code",
"observed_at_millis",
"age_millis"
],
"type": "object"
},
"HealthCheckStatus": {
"description": "Outcome of one runtime health check.",
"oneOf": [
{
"const": "ok",
"description": "The check passed.",
"type": "string"
},
{
"const": "failed",
"description": "The check observed a failure.",
"type": "string"
},
{
"const": "unknown",
"description": "The check could not establish a result.",
"type": "string"
},
{
"const": "unsupported",
"description": "The selected adapter does not implement the check.",
"type": "string"
}
]
},
"RuntimeReadiness": {
"description": "Whether Fabric knows the runtime can currently accept work.",
"oneOf": [
{
"const": "ready",
"description": "The runtime can accept work under its current invocation policy.",
"type": "string"
},
{
"const": "not_ready",
"description": "The runtime is known not to accept work.",
"type": "string"
},
{
"const": "unknown",
"description": "Fabric lacks enough fresh evidence to decide.",
"type": "string"
}
]
}
},
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"description": "Optional adapter-specific contribution to a runtime health report.",
"properties": {
"checks": {
"description": "Adapter or dependency checks.",
"items": {
"$ref": "#/$defs/HealthCheck"
},
"type": "array"
},
"readiness": {
"anyOf": [
{
"$ref": "#/$defs/AdapterReadiness"
},
{
"type": "null"
}
],
"description": "Adapter-owned readiness override when one is known."
}
},
"title": "AdapterHealthResult",
"type": "object"
}
6 changes: 6 additions & 0 deletions adapter-contract/typescript/scripts/check-package.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ try {
"dist/generated/agent-run-request.js",
"dist/generated/agent-run-result.d.ts",
"dist/generated/agent-run-result.js",
"dist/generated/adapter-health-request.d.ts",
"dist/generated/adapter-health-request.js",
"dist/generated/adapter-health-result.d.ts",
"dist/generated/adapter-health-result.js",
"dist/generated/runtime-context.d.ts",
"dist/generated/runtime-context.js",
"dist/index.d.ts",
Expand All @@ -56,6 +60,8 @@ try {
"schemas/agent-config.schema.json",
"schemas/agent-run-request.schema.json",
"schemas/agent-run-result.schema.json",
"schemas/adapter-health-request.schema.json",
"schemas/adapter-health-result.schema.json",
"schemas/runtime-context.schema.json",
]);
const missingFiles = [...expectedFiles].filter(
Expand Down
2 changes: 2 additions & 0 deletions adapter-contract/typescript/scripts/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ const schemaSpecs = [
output: "agent-run-result.ts",
generate: generateRunResult,
},
{ name: "adapter-health-request", output: "adapter-health-request.ts" },
{ name: "adapter-health-result", output: "adapter-health-result.ts" },
{ name: "runtime-context", output: "runtime-context.ts" },
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ export type RuntimeCapabilities = {
* Whether an in-flight invocation can be cancelled.
*/
cancellation?: boolean;
/**
* Whether the selected runtime exposes bounded health observations.
*/
health?: boolean;
/**
* Additional adapter-specific capability metadata.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

// This file is generated from the canonical adapter-contract JSON Schemas.
// Do not edit it directly; run `npm run generate` instead.

/**
* Request passed to an optional adapter health hook.
*/
export interface AdapterHealthRequest {
/**
* Runtime being inspected.
*/
runtime_id: string;
/**
* Remaining health budget in milliseconds.
*/
timeout_millis: number;
}
Loading
Loading