Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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 << 64) - 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": "uint64",
"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