NVIDIA NeMo Fabric Python SDK
The Python SDK is the application-facing interface for NeMo Fabric. Use it to configure an agent harness, inspect the resolved plan, run one request or a multi-turn runtime, and collect normalized results, events, artifacts, and telemetry references.
The SDK is config-first. Applications should construct a Pydantic
FabricConfig from their own job, deployment, or evaluation config.
Examples, CI jobs, and applications construct the typed config directly and
can use ordinary Python functions to create variants.
Generated API reference pages remain the source of truth for exact signatures. This guide explains how the pieces are intended to fit together.
For installation and a package-backed example, start with the NeMo Fabric overview.
Start With One Run
This example uses an NVIDIA-hosted model. Export your NVIDIA API key before running it:
Construct a typed config, then pass it to Fabric.run(...). The SDK starts a
runtime, invokes it once, collects the result, and stops the runtime.
Use base_dir to resolve relative paths in an in-memory config. Before running
in a new environment, call plan(...) to inspect adapter selection and
doctor(...) to check runtime requirements.
The remaining examples reuse this config value.
Execution Model
NeMo Fabric separates configuration, planning, runtime lifecycle, and individual invocations. It does not expose a separate portable session layer.
Most application code works with four objects:
Fabric is a lightweight, reusable SDK facade. It resolves configuration,
creates plans and runtimes, and provides a single-invocation convenience API,
but it does not represent a started execution and does not require cleanup. A
Runtime owns stateful execution and shutdown, so it is the object used as an
async context manager.
RuntimeHandle and InvocationHandle carry lifecycle identity across the
native boundary. Most Python callers use their runtime_id and
invocation_id through Runtime and RunResult rather than manipulating the
handles directly.
A runtime is a logical execution boundary, not necessarily an operating-system process. An adapter may use an in-process SDK, a process, or shared service infrastructure while preserving isolated state for each NeMo Fabric runtime.
Runtime hosting is selected by the adapter kind, not by a public FabricConfig
setting. Every local Process or Python adapter implements the persistent
local-host wire protocol. NeMo Fabric starts one host during
start_runtime(...), reuses its adapter-owned native resources across ordered
invocations, and attempts to release them during stop(). The lifecycle start
operation carries the resolved configuration and capability plan. Each
subsequent AdapterInvocation carries only runtime_context and request.
Third-party local adapters must implement the persistent local-host contract. NeMo Fabric does not currently define a remote-service adapter contract. A crashed persistent host is terminal for that runtime. The same applies when the host exceeds the protocol response timeout. NeMo Fabric does not silently respawn the host or replay a request.
Harness-native threads, sessions, and conversations remain adapter-owned state associated with the NeMo Fabric runtime. They are not additional NeMo Fabric lifecycle objects.
NeMo Fabric provides the runtime contract. Applications own scheduling, queues, retries, worker scaling, and the number of runtimes to run.
Configure Agents In Code
Normalized Configuration Compatibility
NeMo Fabric-owned fields such as workspace, environment variables, and the invocation deadline apply to every local adapter. Adapter-translated fields are validated against the selected adapter descriptor during planning.
Core means NeMo Fabric owns the behavior and applies it uniformly before or
around adapter execution. Yes means the adapter translates the normalized
field into its harness. No means an explicitly configured value fails
planning instead of being ignored. The following table groups provider-specific
Relay subfields and additive extension maps because their support does not vary
by adapter:
Model selection is deterministic: the default role wins; otherwise a single
named role is selected. More than one role without default fails planning.
If a normalized field has no complete mapping, plan(...) and runtime start
fail with a configuration compatibility error naming the adapter and field.
doctor(...) retains the incompatibility and returns a failed diagnostic check
so callers can inspect all preflight findings. NeMo Fabric does not silently
drop the field. runtime.max_turns is optional, so a config that omits it
remains portable across adapters with different native turn-limit support.
Build the complete nested FabricConfig directly, or start with a base
config and use helpers to add capabilities. For example, extend the config from
the first example with skills, MCP, and telemetry:
Tool names are adapter-native selectors. NeMo Fabric does not define a portable catalog that translates names between harnesses. Configure an allowlist, a blocklist, or both:
tools.enabled=None preserves the harness default. An empty list disables every
executable tool. A tool cannot appear in both lists. The selected adapter must
enforce the complete configured policy or planning fails. Hermes interprets
these adapter-native selectors as Hermes toolset names.
Config helpers edit the typed config before planning or starting a runtime.
They do not modify already-started runtimes. Use block_tools(...),
remove_mcp_server(name), and remove_skill_path(path) to edit a copied config.
Telemetry is enabled by adding entries to telemetry.providers; settings
specific to NeMo Relay live in the top-level relay block.
For evaluation or deployment variations, use ordinary Python functions and copies of the typed config. Supply the complete final config to NeMo Fabric.
NeMo Relay observability is represented directly in the SDK config’s top-level
relay block. ATOF uses NeMo Relay 0.6 file and stream sinks:
Additional NeMo Relay plugin components can be supplied generically with
RelayComponentConfig when their component package is available in the runtime
environment.
The repository’s code-review example uses this pattern for complete Hermes Agent, Codex, Deep Agents, environment, MCP, and telemetry variants.
If a config contains relative paths, pass a base_dir to plan(...),
doctor(...), run(...), or start_runtime(...). The base
directory anchors skills, workspaces, artifacts, and other relative paths to
the caller’s package or job layout.
API Inventory
Create Fabric() as the primary SDK entrypoint. It is a regular Python object,
not a lifecycle context manager, and may be reused to plan, diagnose, or start
multiple independent runtimes.
The following table summarizes the SDK entry points and runtime methods:
Single-Invocation Runs
Use run(...) when the application has one input and does not need to preserve
runtime state after the result is collected.
Use input=... for the common case. Use request=RunRequest(...) for structured
invocation metadata. Applications read files themselves and pass either the
loaded input or a validated request to NeMo Fabric.
NeMo Fabric generates runtime and invocation IDs for lifecycle correlation. An application may include its own identifiers in opaque request metadata, but NeMo Fabric does not interpret them as job, session, scheduling, or resume state.
Multi-Turn Runtimes
Use start_runtime(...) when the selected harness should keep state across
turns. Every call creates a new logical NeMo Fabric runtime; callers reuse the
returned object rather than selecting it with a job or session ID. The runtime
stops when its async context exits.
The adapter reuses its native state between calls. Codex maps the calls to turns on one live thread, Deep Agents invokes one compiled graph and checkpointer, Hermes Agent reuses one agent and session database, and Claude keeps one connected SDK client. Harness-native identifiers remain adapter-internal.
NeMo Relay Streaming
Enable NeMo Relay before starting a runtime to consume raw ATOF records while a
turn runs. Runtime.supports_streaming reports whether this NeMo Relay path is
available. It is separate from RuntimeCapabilities.streaming, which describes
adapter-native progressive output. This separation is intentional:
Runtime.invoke_stream() exposes only ATOF records generated by NeMo Relay. A future
normalized NeMo Fabric streaming contract will address adapter-native progressive
output, such as Codex app-server message and item deltas.
The following example streams one invocation and collects its terminal result:
invoke_stream(...) is synchronous and starts the invocation in the background.
The returned InvokeStream is an async iterator of raw NeMo Relay ATOF
dictionaries. The terminal RunResult stays out of band and is available only
through await stream.result(). Treat that terminal result as authoritative:
replace any provisional rendering instead of appending it. Calling
stream.result() does not finalize unread stream records. Calling
Runtime.stop() after the result completes finalizes the stream and discards
those unread records.
Streaming has the following v0.1 constraints:
- Streaming requires two explicit settings: enable NeMo Relay in
FabricConfigand passstreaming=Truetostart_runtime(...). Thestreaming=Trueflag does not enable NeMo Relay by itself; without NeMo Relay telemetry,start_runtime(...)raisesFabricConfigError. With both settings, the SDK binds its HTTP listener and injects the reserved ATOF stream sink. - The default
streaming=Falseleaves the existing NeMo Relay configuration unchanged; it neither enables nor disables ATOF. It also avoids the additional listener HTTP and JSON parsing cost for runtimes that use only ATIF, OpenTelemetry, OpenInference, or application-configured ATOF sinks. - One invocation can be active on a runtime. Fully consume the stream or call
await stream.aclose()before starting another turn. - Breaking an
async forloop does not finalize the stream. Callawait stream.aclose()explicitly. It waits for the invocation to finish and discards unread records; it does not cancel the harness turn. - The end of async iteration only means that no more ATOF records are available.
It does not indicate invocation success. Always await
stream.result(). Invocation exceptions raise from that call, while harness-reported failures are represented by the returned result’s status and error fields. - The SDK limits its queue to 1,024 records and 16 MiB of encoded record data. It rejects individual records larger than 1 MiB and applies TCP backpressure. NeMo Relay can drop records if a consumer stalls longer than its delivery timeout.
- ATOF granularity depends on the NeMo Relay integration. Gateway harnesses expose per-delta event structure, but current ATOF records keep token text in the terminal scope. In-process harnesses expose scope-level progress.
- Reconstruct nested and parallel work with
uuidandparent_uuid. Stream order alone does not define the Deep Agents scope tree. - The listener correlates records to one NeMo Relay scope tree. Deep Agents and
Hermes roots carry the NeMo Fabric request ID. For Claude and Codex, the SDK
matches NeMo Relay turn scopes by their role and 1-based turn index. Records
outside the matched root and its descendants are discarded, so delayed
records from another turn do not enter the active stream. If the gateway
turn sequence does not align with the NeMo Fabric invocation sequence, the SDK
yields no uncorrelated records and emits a
RuntimeWarningafter natural stream exhaustion. A short drain window only collects late records from the matched tree; it does not define turn ownership. - The SDK listener binds to the address in
NEMO_FABRIC_STREAMING_HOST, which defaults to127.0.0.1. Override it when a Claude or Codex gateway must reach the SDK through another network interface. Because the listener accepts ATOF HTTP posts, restrict access to the configured interface. If async iteration reaches the post-turn drain timeout without a NeMo Relay connection, or if NeMo Relay sends data but no record matches the active turn, the SDK emits oneRuntimeWarningfor that failure mode. It also warns if a NeMo Relay upload terminates before completing its chunked request body because the yielded stream can be incomplete. A caller that only awaitsstream.result()still receives the terminal result and does not run this warning check.
Claude and Codex streaming use the NeMo Relay nemo-relay gateway CLI and require a
stream-sink-capable release from 0.6.0 up to, but not including, 0.7.0. Follow
the NeMo Relay CLI installation instructions
to provision it. Hermes Agent and Deep Agents use their in-process NeMo Relay
integrations.
Application-Owned Parallelism
Applications create independent runtimes when they want parallel work. NeMo Fabric
does not own a queue, worker pool, semaphore, retry policy, timeout policy, or
numeric concurrency limit. Each Runtime accepts one invocation at a time so
its ordered harness state cannot be changed by two calls at once. If an
application overlaps calls on the same Runtime, the second call raises
FabricStateError. To perform work in parallel, start independent runtimes;
the application decides how many to run.
Async lifecycle calls run blocking native work outside the Python event loop, so independent runtimes can make progress concurrently. This does not add a NeMo Fabric concurrency limit or scheduler.
For example, each Harbor job starts an independent NeMo Fabric runtime. Harbor owns job IDs and concurrency policy; NeMo Fabric does not use a job ID to select or resume runtime state.
Unified Run Results
Every invocation that reaches the adapter boundary returns a normalized
RunResult, even when the harness invocation itself failed. Inspect status,
error, events, and artifacts first, then process output when the status
is successful.
Important fields:
These are structured correlation fields, not interchangeable metadata:
runtime_id identifies the runtime lifecycle, invocation_id identifies one
invocation within that runtime, and request_id correlates the caller’s
request. NeMo Fabric-generated values use type-specific prefixes such as runtime-,
invocation-, and request-; callers may provide their own request_id.
Consumers should store and log each field separately and otherwise treat its
value as opaque rather than parsing the identifier encoding.
If NeMo Fabric cannot resolve config, start a runtime, or obtain a normalized result,
the SDK raises a FabricError subclass instead of returning a partial
RunResult.
Events
Each RunResult includes the normalized events collected for that invocation.
Event kinds and detail may vary by adapter, but their lifecycle and correlation
fields use the common NeMo Fabric contract.
Events are useful for:
- rendering invocation history in application or service UIs;
- forwarding logs and status to evaluation harnesses;
- correlating runtime, invocation, adapter, and telemetry IDs;
- reporting structured failures alongside the terminal result.
Feature Support Across Harness Adapters
The SDK presents one consistent shape across adapters, but adapters differ in their runtime requirements, accepted configuration, and optional capabilities.
Use plan(...) and doctor(...) before relying on optional features:
Use the plan to confirm adapter selection and capability routing. Use the doctor report to catch unresolved adapter descriptors, unsupported normalized capabilities, missing declared requirements, and environment problems before starting a runtime.
Capability routes assign execution ownership; they do not describe network
routing. harness_native means the selected adapter executes the capability
through its harness. fabric_managed means NeMo Fabric executes it outside the
harness-native surface. unsupported means neither side can execute it.
Routes apply to tools, skills, and MCP servers. Scalar fields such as
instructions.system and runtime.max_turns are validated separately against
the adapter descriptor.
Install And Runtime Responsibilities
In production, the consumer or execution environment is responsible for installing NeMo Fabric, the selected harness, adapter dependencies, model access, credentials, and any required native tools. NeMo Fabric validates and diagnoses the runtime assumptions, but it does not silently install harnesses or credentials at invocation time.
Development environments may use extras, virtual environments, or local source checkouts to make iteration easy. Production environments should prefer explicit images, preinstalled dependencies, or managed deployment packages.
Runtime compatibility checks should validate:
- NeMo Fabric SDK and native extension versions;
- selected adapter version;
- selected harness version or version range;
- required environment variables or secret references;
- optional capability support such as NeMo Relay, MCP, or tool exposure.
Custom Fields And Adapter Settings
Use normalized NeMo Fabric fields for portable behavior: models, system instructions, turn limit, runtime, environment, skills, MCP, telemetry, tools, and artifacts.
Supply request context through RunRequest.context for each invocation. Request
context is not part of FabricConfig.
Use harness.settings only for stable, harness-native behavior declared by the
selected descriptor. Each bundled adapter declares a closed settings schema.
Executable paths, Relay command discovery, state directories, and similar
launch mechanics are runtime implementation details rather than public adapter
settings.
Use FabricConfig.metadata for human-readable agent identity and caller-owned
annotations. NeMo Fabric preserves these values in the resolved configuration
but does not copy them into RunResult.metadata. Use request_id and
RunRequest.context for per-invocation correlation. RunResult.metadata
contains adapter-specific result details.
Adapter settings are not portable by default. The exact descriptor selected
during planning owns their JSON Schema. Planning and doctor reject settings
that do not satisfy that schema. A descriptor without settings_schema accepts
an empty settings map and rejects a non-empty map. Validation does not apply
schema defaults or otherwise change the settings preserved in RunPlan.
Validation errors identify the adapter, descriptor source and path, and
offending settings path.
Errors
All public SDK errors inherit from FabricError.
Consumers own job-level retries and rollout-level failure policy.
Single-invocation runs attempt to stop the runtime before returning. A Runtime
used with async with also attempts cleanup after an invocation error; if cleanup
fails, that failure is attached to the original exception rather than replacing
it. NeMo Fabric records structured error metadata when possible and returns enough
detail for the consumer to decide what to do next.
SDK Contract Boundaries
NeMo Fabric keeps a narrow execution contract. Applications own product behavior around that contract.
Schemas And Python Models
The SDK’s Pydantic models are maintained against the Rust-generated public schemas. The schemas describe the typed cross-language contracts. Use the generated API reference for exact Python signatures.
Versioning
NeMo Fabric uses explicit contract versions where persisted or independently maintained artifacts cross package boundaries:
schema_versionidentifies the typed NeMo Fabric config contract transported across SDK and language boundaries.contract_versionidentifies the adapter descriptor contract implemented by afabric-adapter.jsonfile.- Python and Rust package versions identify the installed SDK/core release.
NeMo Fabric versions top-level typed contracts and independently maintained adapter
metadata. It does not version each config subsection independently. For
example, MCP, skills, models, telemetry, and runtime fields evolve under the
enclosing schema_version.
NeMo Fabric validates adapter descriptor contract versions during planning. Package semver identifies the installed implementation, but it is not the compatibility contract for independently maintained adapters.
Config Extensibility
The public schema has typed fields for stable NeMo Fabric concepts and controlled
extension points for adapter- or application-owned data. Use known fields for
portable concepts such as harness selection, models, runtime, skills, MCP,
telemetry, and artifacts. Use adapter-owned harness.settings for settings
validated by the selected descriptor and interpreted by its adapter. Use
metadata or preserved extension fields for data NeMo Fabric should carry but
not interpret.
Additive optional fields may be introduced within the existing document schema version when old configs remain valid. Required fields, removed fields, or semantic changes that alter how existing configs are interpreted require a new enclosing document schema version or an explicit compatibility path.
Unknown data is not the same as supported behavior. An adapter must advertise and implement a capability before NeMo Fabric treats it as runnable.
Resilience
NeMo Fabric reports lifecycle failures; applications own recovery policy. If a runtime process dies, a connection is permanently lost, or an adapter cannot complete an invocation, NeMo Fabric marks the relevant runtime/invocation as failed and returns structured error metadata when possible.
Transient I/O failures may be marked with retryable error metadata, but NeMo Fabric does not perform job-level retries by default. Consumers decide whether to retry the request, start a replacement runtime, fail the job, or escalate to a user.
Capacity And Backpressure
If a harness reports capacity pressure, an adapter should surface it as a structured error or event such as busy, rate limited, capacity exceeded, or backpressure. The consumer decides whether to wait, retry, scale out, or fail.
Next Steps
- Review the examples in the GitHub repo
examples/.