diff --git a/.agents/skills/README.md b/.agents/skills/README.md new file mode 100644 index 000000000..de4f455f5 --- /dev/null +++ b/.agents/skills/README.md @@ -0,0 +1,51 @@ + + +# NeMo Fabric Maintainer Skills + +This directory is the maintainer skill set for developing NeMo Fabric itself. +Use these skills for repository work such as: + +- Contributing or changing public API surfaces across Rust, CLI, Python, schema, + and adapters. +- Writing and validating Python tests and choosing the right validation matrix. +- Maintaining CI workflows, packaging, versions, and release surfaces. +- Reviewing documentation for NVIDIA style and preparing pull requests. + +These skills may reference repository internals, build and test commands, and +contribution workflows. + +Consumer-facing integration skills live in the top-level +[`skills/`](../../skills/README.md) directory so they can be exported separately +for application developers and integrators. Keep external usage guidance out of +this set. + +## Skills + +This table lists the maintainer skills in this set and what each covers. + +| Skill | Purpose | +|---|---| +| [`contribute-api`](contribute-api/SKILL.md) | Add a public API surface with Rust, CLI, Python, schema, adapter, and documentation parity. | +| [`small-fix`](small-fix/SKILL.md) | Make a small, reviewable bug fix without widening scope. | +| [`contribute-docs`](contribute-docs/SKILL.md) | Change documentation or examples in step with public behavior. | +| [`review-doc-style`](review-doc-style/SKILL.md) | Review documentation and public text for NVIDIA technical-writing style. | +| [`validate-change`](validate-change/SKILL.md) | Choose and run the right validation matrix for a change. | +| [`python-tests`](python-tests/SKILL.md) | Write Python tests for Fabric. | +| [`maintain-ci`](maintain-ci/SKILL.md) | Maintain GitHub Actions workflows with pinned actions and local validation. | +| [`maintain-packaging`](maintain-packaging/SKILL.md) | Maintain package metadata, native artifacts, lockfiles, and release surfaces. | +| [`update-project-version`](update-project-version/SKILL.md) | Bump and synchronize release versions across packaging. | +| [`prepare-pr`](prepare-pr/SKILL.md) | Prepare, open, or edit a pull request with the right scope and review handoff. | +| [`karpathy-guidelines`](karpathy-guidelines/SKILL.md) | Behavioral coding guidelines; use as a companion to the others. | + +## Discovery And Conventions + +- Coding agents auto-discover this set from `.agents/skills/`. For Claude Code, + `.claude/skills` is a symlink to this directory, exposing the same maintainer + set without mixing in consumer skills. +- **Naming:** maintainer skills use descriptive, task-based names (for example + `contribute-api`, `validate-change`). +- **Frontmatter:** each `SKILL.md` begins with YAML frontmatter containing at + least `name` and `description`. diff --git a/.agents/skills/validate-change/SKILL.md b/.agents/skills/validate-change/SKILL.md index 104d47932..38c73e908 100644 --- a/.agents/skills/validate-change/SKILL.md +++ b/.agents/skills/validate-change/SKILL.md @@ -37,8 +37,10 @@ surfaces touched by a change. - If documentation or examples changed, run `just docs` when practical and verify documented commands against the current repository. - If code changes alter APIs, commands, paths, packaging behavior, telemetry - semantics, or documented best practices, update dependent maintainer skills - in the same branch. + semantics, or documented best practices, update dependent maintainer skills in + the same branch. Because the consumer skills under `skills/` restate SDK guide, + Pydantic model, and Rust type details, update them in parity whenever those + surfaces change. ## Start With the Change Shape diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..63ac66c30 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,57 @@ + + +# AGENTS.md + +## Agent Skills + +NeMo Fabric ships two tiers of agent skills. Use the tier that matches your task: + +- **Consumer skills — `skills/`.** For integrating NeMo Fabric into an external + application, service, evaluation harness, or platform through the public Python + SDK. They are self-contained and exportable, depending only on supported public + SDK contracts and the published documentation, never on repository internals. + Start with [`skills/README.md`](skills/README.md). +- **Maintainer skills — `.agents/skills/`.** For contributing to NeMo Fabric + itself: API, documentation, testing, CI, packaging, release, and PR + preparation. They may reference repository internals and contribution + commands. Start with [`.agents/skills/README.md`](.agents/skills/README.md). + +Keep the tiers separate: do not add consumer integration guidance to the +maintainer set, or repository-internal contribution steps to the consumer set. +Coding agents that auto-discover skills read the maintainer set from +`.agents/skills/` (also exposed at `.claude/skills` for Claude Code); the +consumer set under `skills/` is meant to be exported into a consumer's own +project. + +Key conventions for the two tiers: + +- **Discovery wiring:** `.claude/skills` symlinks to `.agents/skills/` (the + maintainer set) for Claude Code; consumer skills under `skills/` are copied + into a consumer's own project rather than auto-discovered from this repository + (see [`skills/README.md`](skills/README.md)). +- **Portability strategy:** consumer skills link to public GitHub documentation + and example URLs rather than a docs-site domain, so they keep working when + exported. +- **Supported consumer surface:** the consumer tier currently ships one skill, + `nemo-fabric-integrate`, for typed Python SDK integration. + +## Documentation And Contribution Workflow + +These workflow notes keep public documentation, examples, and PR preparation aligned +with repository expectations. + +- Update user-facing entry points when public behavior, the `nemo-fabric` package (imported as `nemo_fabric`), examples, or supported bindings change: `README.md`, the Fern docs under `docs/` (navigation in `docs/index.yml`, site config in `fern/docs.yml`), and the adapter/integration READMEs (`adapters/*/README.md`, `python/src/nemo_fabric/integrations/*/README.md`, `examples/README.md`). +- Keep the Python/Rust binding contract current when the public API changes: `docs/sdk/python.mdx`, the JSON Schema notes in `schemas/SCHEMA.md`, the generated references under `docs/reference/api/`, and the consumer skills under `skills/` (which restate SDK guide, model, and type details and must be kept in parity). Regenerate docs with `just docs` after changing the docs site. +- Keep release- and packaging-process details in maintainer surfaces (currently the `maintain-packaging` skill at `.agents/skills/maintain-packaging/SKILL.md`). Do not move release-history policy into user-facing docs. There is no `RELEASING.md` or `CHANGELOG.md` yet; add release-history policy there if those files are introduced rather than into user docs. +- Keep the stable public wrapper `scripts/generate_api_docs.sh` at the `scripts/` root in docs and examples. Reference namespaced helper paths under `scripts/docs/` only when documenting internal maintenance work. +- Use branch prefixes for your work: `feat/`, `fix/`, `docs/`, `test/`, or `refactor/`. +- Name branches after the work, never the Linear ticket. Do not embed ticket IDs or slugs in the branch name (e.g. use `feat/notebooks-onboarding`, not `feat/fabric-70-notebooks-onboarding`). This rule has historically been overlooked, so double-check the branch name before pushing or opening a PR. +- Use Conventional Commit PR titles (`: `) as required by `.coderabbit.yaml` and the `prepare-pr` skill; reserve `fix` for actual product bugs, not CI, docs, or chores. +- Use signed-off commits for PR work: `git commit -s`. +- When creating a pull request from the current branch, target the upstream repository rather than a fork. +- Before creating, opening, publishing, or editing a pull request, read `.github/pull_request_template.md` and use it as the PR body skeleton (or `gh pr create --template .github/pull_request_template.md`). Preserve its visible headings (`Overview`, `Where should the reviewer start?`, `Related Issues`) and its contribution checkboxes; fill the sections instead of replacing them with a generic summary. +- If repo-local PR guidance such as the `prepare-pr` skill conflicts with generic GitHub connector or plugin guidance, follow the repo-local PR guidance for PR body format and review handoff details. +- PR descriptions should include what changed, why, how it was tested, and any breaking changes within the repository template format. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 000000000..47dc3e3d8 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file diff --git a/README.md b/README.md index b498cd675..964e0b662 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,8 @@ the [Python SDK guide](docs/sdk/python.mdx). Exact signatures are in the - [Python SDK guide](docs/sdk/python.mdx): typed configuration, planning, diagnostics, requests, multi-turn runtimes, parallelism, results, and errors. +- [Consumer integration skills](skills/README.md): repository-local coding-agent + skills for integrating Fabric into an application through the Python SDK. - [Getting Started overview](docs/getting-started/overview.mdx): interface selection and the end-to-end Fabric workflow. - [Harbor examples](examples/harbor/README.md): validate the integration with a diff --git a/TODO.md b/TODO.md index 8f3433689..25ed111bb 100644 --- a/TODO.md +++ b/TODO.md @@ -26,3 +26,19 @@ cleanup validation. - **Cleanup:** Replace the pinned source-install instructions with the released CLI installation, run the Codex Relay end-to-end test, update both affected documentation files, and remove this entry. + +## NeMo Fabric PyPI Availability + +- **Status:** Waiting for a published release +- **Added:** July 16, 2026 +- **Affected documentation:** `skills/nemo-fabric-integrate/SKILL.md` (install + section) +- **Reason:** `nemo-fabric` is not yet published on PyPI, so the consumer + integration skill installs from a source checkout (`just build-all`) or locally + built wheels (`just wheels` plus `uv pip install --find-links`). +- **Upstream resolution:** NeMo Fabric's own first PyPI release (internal + milestone; no external dependency). +- **Removal condition:** `nemo-fabric` and its adapter extras are published on + PyPI in a version the skill can target. +- **Cleanup:** Replace the source and wheel install steps in the skill with the + published `pip install "nemo-fabric[...]"` instructions and remove this entry. diff --git a/skills/README.md b/skills/README.md new file mode 100644 index 000000000..0bbb3f74b --- /dev/null +++ b/skills/README.md @@ -0,0 +1,63 @@ + + +# NeMo Fabric Consumer Skills + +These are user-facing skills for integrating NeMo Fabric into your own +application, service, evaluation harness, or platform through the public Python +SDK. They are intended for external application developers and integrators — not +for developing NeMo Fabric itself. + +If you are contributing to NeMo Fabric — changing core, bindings, adapters, +documentation, CI, or packaging — use the +[maintainer skills](../.agents/skills/README.md) in `.agents/skills/` instead. + +## Portability + +Consumer skills are self-contained and exportable. Each skill depends only on +supported public interfaces (the `nemo_fabric` Python package) and public +documentation URLs, never on repository-internal paths. + +- Cross-links point to the published documentation and public example URLs on + GitHub, not to files inside this checkout. Skill-specific material is bundled + under each skill's own `references/`. +- Skills do not depend on repository internals — their links are absolute or + bundled, so they resolve when copied out. A skill may still mention Fabric + build commands (`just …`, needed while Fabric is unpublished) or the maintainer + tier as guidance for contributors. + +## Using A Consumer Skill In Your Project + +Copy the skill directory — for example `nemo-fabric-integrate/`, including its +`references/` — into the place your coding agent discovers skills **in your own +project**. Do not rely on this repository's maintainer wiring (its `.claude/skills` +symlink or `.agents/skills/` set); those serve Fabric's own contributors. + +- **Claude Code:** place it at `.claude/skills/nemo-fabric-integrate/` in your + project, or `~/.claude/skills/nemo-fabric-integrate/` to use it across + projects. Claude Code discovers `SKILL.md` files under those directories. +- **OpenAI Codex:** place it at + `/.agents/skills/nemo-fabric-integrate/` in your project, or + `$CODEX_HOME/skills/nemo-fabric-integrate/` to use it across projects. +- **Other agents:** each skill is a portable `SKILL.md` bundle — put it wherever + your agent loads skills, or reference its `SKILL.md` directly from your agent + instructions. Confirm discovery with a prompt that should trigger the skill. + +## Start Here + +| Skill | Use it when | +|---|---| +| [`nemo-fabric-integrate`](nemo-fabric-integrate/SKILL.md) | You are adding NeMo Fabric to a consumer application, service, evaluation harness, or platform through the typed Python SDK — building an in-memory `FabricConfig`, choosing one-shot versus stateful-runtime execution, validating with `plan`/`doctor`, and consuming normalized results. | + +## Conventions + +- **Naming:** consumer skills are prefixed with the product name, + `nemo-fabric-`. +- **Frontmatter:** each `SKILL.md` begins with YAML frontmatter containing at + least `name` and `description`. `SKILL.md` files do not carry an SPDX header; + every other file, including this README and bundled `references/`, does. +- **Self-containment:** keep a skill usable outside this repository. Link to + public documentation and example URLs, and bundle any skill-specific reference + material under the skill's own `references/`. diff --git a/skills/nemo-fabric-integrate/SKILL.md b/skills/nemo-fabric-integrate/SKILL.md new file mode 100644 index 000000000..523cc5f21 --- /dev/null +++ b/skills/nemo-fabric-integrate/SKILL.md @@ -0,0 +1,272 @@ +--- +name: nemo-fabric-integrate +description: Use this skill when integrating NeMo Fabric into a consumer application, service, evaluation harness, or platform through the typed Python SDK — translating the consumer's own application, job, or deployment config into an in-memory FabricConfig, choosing one-shot run versus a stateful runtime, validating with plan and doctor, and consuming normalized results, artifacts, and telemetry. +license: Apache-2.0 +metadata: + author: NVIDIA Corporation and Affiliates +--- + +# Integrate NeMo Fabric Through The Python SDK + +Use this skill when a consumer codebase — an application, service, evaluation +harness, or platform — needs to run agent harnesses through NeMo Fabric's typed +Python SDK. The consumer owns its own configuration object and translates it +into an in-memory `FabricConfig`; Fabric owns adapter selection, the runtime +lifecycle, and normalized results. + +Do not use this skill to author or modify Fabric adapters, change Fabric core or +its bindings, or maintain repository infrastructure. If you are contributing to +Fabric itself, use the maintainer skills in `.agents/skills/` instead. + +## Integration Boundary + +Stay on the public, in-memory contract. These rules keep a consumer integration +supported and upgrade-safe: + +- Import only from the public `nemo_fabric` package. Never import `_native`, + `_config_sources`, or any adapter-internal module. +- Build configuration as a typed `FabricConfig` in memory and pass it directly to + Fabric. Create every deployment or evaluation variant with ordinary Python + functions and `model_copy(deep=True)`. (A platform integration such as Harbor + may bake a config file into a task image at its deployment boundary — that is a + deployment mechanic, not the in-memory consumer pattern here.) +- Let Fabric own harness control. Do not reimplement start, invoke, or stop + logic, and do not manage adapter threads, sessions, or processes directly. +- Treat `runtime_id`, `invocation_id`, and `request_id` as opaque correlation + strings, not parsable or reusable state. + +See [config-mapping.md](references/config-mapping.md) for how to translate a +consumer config object into `FabricConfig`, and for the full list of mechanics +that stay hidden behind this boundary. + +## Install And Set Up The Environment + +The consumer or its execution environment owns installation; Fabric validates +runtime assumptions but never installs harnesses or credentials at run time. + +- Fabric is not published on PyPI yet. From a source checkout, `just build-all` + builds the native extension and installs the SDK. To install into another + environment, build wheels with `just wheels`, then + `uv pip install --find-links "nemo-fabric[runtime]"` (add the + `harbor` extra for the Harbor integration). See the + [installation guide](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/getting-started/install.mdx). +- Select a harness adapter — the `adapter_id` set in `HarnessConfig`, for example + `nvidia.fabric.hermes` — and install its extra the same way, for example + `uv pip install --find-links "nemo-fabric[adapters-hermes]"` + (available extras: `adapters-hermes`, `adapters-codex`, + `adapters-deepagents`, `adapters-claude`), plus the adapter's own harness + binaries and dependencies. +- Provide model credentials through environment variables named by the config + (`ModelConfig.api_key_env`), never as literals in code. +- Confirm the native extension is importable; SDK calls raise + `FabricNativeUnavailableError` when it is missing. + +## Build The Typed Config From Consumer Config + +Map the consumer's application, job, or deployment object into a `FabricConfig` +with the public models and helper methods: + +```python +from nemo_fabric import ( + FabricConfig, + HarnessConfig, + MetadataConfig, + ModelConfig, + RuntimeConfig, +) + + +def to_fabric_config(job) -> FabricConfig: + config = FabricConfig( + metadata=MetadataConfig(name=job.name), + harness=HarnessConfig(adapter_id=job.adapter_id, resolution="preinstalled"), + models={ + "default": ModelConfig( + provider=job.provider, + model=job.model, + api_key_env=job.api_key_env, + ) + }, + runtime=RuntimeConfig(input_schema="chat", output_schema="message"), + ) + config.add_skill_path(job.skill_dir) + config.add_mcp_server( + "github", + transport="streamable-http", + url="${GITHUB_MCP_URL}", + exposure="harness_native", + ) + return config +``` + +- Shape capabilities with `add_skill_path`, `remove_skill_path`, + `add_mcp_server`, `remove_mcp_server`, and `enable_relay`. +- Create deployment or evaluation variants with `model_copy(deep=True)` and + ordinary Python functions; each copy plans and runs independently. +- Pass `base_dir=...` to any `Fabric` call when the config uses relative paths, + so skills, workspaces, and artifacts anchor to the consumer's own layout. + +The repository [`code_review_agent` example](https://github.com/NVIDIA/NeMo-Fabric/tree/main/examples/code_review_agent) +shows this pattern end to end with complete Hermes, Codex, Deep Agents, +environment, MCP, and telemetry variants. Reuse it rather than duplicating config +construction. + +## Choose A Lifecycle + +Pick the smallest lifecycle the consumer needs: + +- **One-shot** — one input, no retained state. `await Fabric().run(config, input=...)` + runs the full start, invoke, and stop cycle and returns a `RunResult`. Pass + `request=RunRequest(...)` instead of `input=...` when the invocation needs a + caller-owned request ID or context (the two are mutually exclusive). +- **Stateful runtime** — ordered turns over one live harness. Start it with + `start_runtime(...)` and use the returned `Runtime` as an async context + manager so cleanup runs on exit — shutdown is attempted, not guaranteed + (`stop()` can raise `FabricRuntimeError`; see Consume Results And Handle + Errors). A runtime accepts one active invocation at a time; overlapping calls + raise `FabricStateError`. + +The example below shows both lifecycles as a complete, runnable program; the +shorter async snippets elsewhere in this skill are fragments that assume the same +async context: + +```python +import asyncio + +from nemo_fabric import Fabric + + +async def main() -> None: + fabric = Fabric() + + # One-shot + result = await fabric.run(config, base_dir=base, input="Review the changes.") + + # Multi-turn + async with await fabric.start_runtime(config, base_dir=base) as runtime: + first = await runtime.invoke(input="Inspect the repository") + second = await runtime.invoke(input="Now review the latest patch") + + +asyncio.run(main()) +``` + +Fabric owns no queue, worker pool, retry policy, or concurrency limit. For +parallel work, start independent runtimes and let the consumer decide how many. + +## Validate Before Running + +Resolve and diagnose before spending work on a runtime, especially in a new +environment or before relying on an optional capability: + +```python +fabric = Fabric() +plan = fabric.plan(config, base_dir=base) # sync: adapter + capabilities +report = await fabric.doctor(config, base_dir=base) # async: preflight checks + +print(plan.adapter.adapter_id, report.status) +``` + +- Use `plan(...)` to confirm adapter selection and capability routing before + running. +- Use `doctor(...)` to check adapter availability, resolution, environment + context, and declared requirements such as required environment variables. Its + aggregate `status` is `pass`, `warn`, or `fail`. It does **not** validate the + contents of `harness.settings`: an unknown or misspelled adapter setting still + passes and is silently ignored unless the adapter reads it, so validate + settings against the adapter's own docs and your integration tests. +- Use `resolve(...)` when only the normalized effective config is needed, with + no adapter resolution. + +## Consume Results And Handle Errors + +Every invocation that reaches the adapter boundary returns a normalized +`RunResult`, even when the harness invocation itself failed. Inspect the failure +fields before reading output: + +```python +result = await fabric.run(config, base_dir=base, input="Review the changes.") + +if result.status == "succeeded": + use_output(result.output, result.artifacts, result.telemetry) +else: + handle_failure(result.status, result.error, result.events) # failed, cancelled, ... +``` + +- Treat `status == "succeeded"` as the only success. Other terminal values + (`failed`, `cancelled`) are unsuccessful, and `error` may be `None` even then, + so branch on `status`, not on `error`. Read `status`, `error`, and `events` + before processing `output`. +- Capture `artifacts` and `telemetry` references as the returned evidence for + platforms and evaluations. Store and log `runtime_id`, `invocation_id`, and + `request_id` separately as opaque strings. +- Catch `FabricError` subclasses for lifecycle failures that prevent a + normalized result: `FabricConfigError`, `FabricCapabilityError`, + `FabricRuntimeError`, `FabricStateError`, and `FabricNativeUnavailableError`. +- The consumer owns retries and failure policy; Fabric does not retry by + default. `run(...)` and `async with` runtimes attempt cleanup automatically, + so prefer them over manual `stop()` — but shutdown is not guaranteed: `stop()`, + including the automatic call when an `async with` block exits, can raise + `FabricRuntimeError`. On a normal exit that error propagates; after an + invocation error the cleanup failure is attached to the original exception. Be + ready to handle a shutdown failure. + +See [results-and-errors.md](references/results-and-errors.md) for the full +result-field and error inventory, and +[sdk-api-inventory.md](references/sdk-api-inventory.md) for when to use each +`Fabric` and `Runtime` method. + +## Test And Validate The Integration + +- Write focused integration tests that build the consumer's `FabricConfig`, + assert `plan(...)` selects the expected adapter and capabilities, and — where + a harness and credentials are available — run one invocation and assert the + `RunResult` status and evidence. +- `plan(...)` is credential-free — use it as the CI gate that validates adapter + selection and capability routing without a model or secrets. `doctor(...)` also + runs without calling a model, but it checks declared environment requirements + (such as required API-key variables) and returns `fail` when they are unset, so + run it where the environment is provisioned and read its per-check results. +- Run the consumer project's own build and test commands. For a source checkout + of Fabric, `just build-all` rebuilds the native extension and + `just test-python` runs the Python suite. +- Confirm no config files were written and no non-public imports were added. + +## Checklist + +- [ ] The consumer config object is translated directly into an in-memory `FabricConfig`. +- [ ] Only public `nemo_fabric` symbols are imported; no `_native`, `_config_sources`, or adapter internals. +- [ ] The consumer config is built in memory and passed directly to Fabric, with no intermediate config file. +- [ ] The right lifecycle is chosen: `run(...)` for one-shot, `start_runtime(...)` with `async with` for multi-turn. +- [ ] `plan(...)` and `doctor(...)` validate adapter selection, capabilities, and environment before execution. +- [ ] Installation, adapter dependencies, and credentials are owned by the environment, not consumer code. +- [ ] `RunResult` status, error, and events are inspected before output; artifacts and telemetry are captured. +- [ ] `FabricError` subclasses are handled, including a `FabricRuntimeError` raised by shutdown; cleanup is delegated to `run(...)` or `async with` (attempted, not guaranteed). +- [ ] Correlation IDs are stored and logged as opaque strings. +- [ ] Focused integration tests pass and Fabric validation (`plan`/`doctor`, tests) succeeds. + +## Related Documentation + +Link to these canonical sources instead of duplicating them: + +- [Python SDK guide](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/sdk/python.mdx) +- [Getting started](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/getting-started/overview.mdx) and + [installation guide](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/getting-started/install.mdx) +- Generated API reference (public API index; the installed `nemo_fabric` type + stubs are authoritative for exact signatures, fields, and defaults): + [client](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.client.md), + [runtime](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.runtime.md), + [models](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.models.md), + [types](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.types.md), + [errors](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.errors.md) +- Canonical in-memory config example: + [examples/code_review_agent](https://github.com/NVIDIA/NeMo-Fabric/tree/main/examples/code_review_agent) +- Platform and evaluation-harness integration: + [examples/harbor](https://github.com/NVIDIA/NeMo-Fabric/tree/main/examples/harbor) and + [nemo_fabric.integrations.harbor](https://github.com/NVIDIA/NeMo-Fabric/tree/main/python/src/nemo_fabric/integrations/harbor). + Note the difference: Harbor bakes a config into the task image and passes it as + a file-backed `fabric_config_path` (YAML) at the container boundary. That is a + Harbor deployment mechanic, not the in-memory `FabricConfig` pattern this skill + teaches — follow the code-review example for consumer integration code, and + treat Harbor as platform context where a config crosses into a task container + as a file. diff --git a/skills/nemo-fabric-integrate/references/config-mapping.md b/skills/nemo-fabric-integrate/references/config-mapping.md new file mode 100644 index 000000000..2d2bea345 --- /dev/null +++ b/skills/nemo-fabric-integrate/references/config-mapping.md @@ -0,0 +1,100 @@ + + +# Mapping Consumer Config To FabricConfig + +Translate the consumer's own application, job, or deployment object into a typed +`FabricConfig` in memory. The consumer keeps owning its configuration model; +Fabric only receives the validated slice it needs. + +## Public Config Models + +Import these from the top-level `nemo_fabric` package: + +| Model | Purpose | +| --- | --- | +| `FabricConfig` | Root config passed to every `Fabric` call. | +| `MetadataConfig` | Agent name and description. | +| `HarnessConfig` | `adapter_id`, `resolution`, and adapter-owned `settings`. | +| `ModelConfig` | Provider, model, credentials (`api_key_env`), and sampling. | +| `RuntimeConfig` | `input_schema`, `output_schema`, and artifact locations. | +| `EnvironmentConfig` | Execution environment (`local`, sandbox, control location). | +| `McpConfig` / `McpServerConfig` | MCP servers and exposure. | +| `SkillConfig` | Skill directories. | +| `TelemetryConfig` | Telemetry providers. | +| `RelayConfig` and `Relay*Config` | Relay observability under the top-level `relay` block. | + +The [models reference](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.models.md) +indexes the public config models. The generated pages omit constructor fields and +defaults, so read the installed `nemo_fabric` models (they ship `py.typed`) for +exact field names and defaults. + +## Build And Shape + +Construct the nested config directly, then adjust capabilities with helper +methods that edit the typed config in place and return it: + +- `add_skill_path(path)` / `remove_skill_path(path)` +- `add_mcp_server(name, *, transport, url, exposure, ...)` / `remove_mcp_server(name)` +- `enable_relay(...)` for Relay observability in the `relay` block + +```python +config = FabricConfig( + metadata=MetadataConfig(name=job.name), + harness=HarnessConfig(adapter_id=job.adapter_id, resolution="preinstalled"), + models={"default": ModelConfig(provider=job.provider, model=job.model, api_key_env=job.api_key_env)}, + runtime=RuntimeConfig(input_schema="chat", output_schema="message"), +) +config.add_skill_path(job.skill_dir) +``` + +## Variants Without Files + +Create deployment or evaluation variants with deep copies and plain functions. +Each copy resolves, plans, and runs independently. + +```python +def with_relay(base: FabricConfig) -> FabricConfig: + config = base.model_copy(deep=True) + config.enable_relay(output_dir="./artifacts/relay") + return config +``` + +Use this function-and-copy pattern for every variant; keep all variation in +ordinary Python. + +## Relative Paths + +If the config uses relative paths for skills, workspaces, or artifacts, pass +`base_dir=...` to `resolve(...)`, `plan(...)`, `doctor(...)`, `run(...)`, or +`start_runtime(...)`. The base directory anchors those paths to the consumer's +package or job layout, so nothing depends on the process working directory. + +## Adapter-Owned And Caller-Owned Data + +- Use normalized fields for portable behavior: models, runtime, environment, + skills, MCP, telemetry, and request context. +- Use `harness.settings` for adapter-owned configuration the selected adapter + understands (for example Hermes launch options or Codex settings). Adapter + settings are not portable, and `doctor(...)` does not validate their contents — + an unknown or misspelled key still passes and is silently ignored unless the + adapter reads it. Validate settings against the adapter's docs and your + integration tests. +- Use `metadata` and extension fields for caller-owned annotations Fabric carries + but does not interpret. Config `metadata` is not echoed into + `RunResult.metadata`: the name surfaces as `RunResult.agent_name`, and for + caller-owned correlation on a specific invocation set `RunRequest.request_id`, + which is returned as `RunResult.request_id`. + +## Stays Hidden Behind The Boundary + +Do not surface these mechanics in the consumer-facing integration: + +- Serializing `FabricConfig` to disk as the integration path (`to_mapping()` is + for inspection and logging, not a required file step). +- Importing `nemo_fabric._native`, `nemo_fabric._config_sources`, or + adapter-internal modules. +- Reimplementing harness start, invoke, or stop logic, or managing adapter + threads, sessions, or processes. diff --git a/skills/nemo-fabric-integrate/references/results-and-errors.md b/skills/nemo-fabric-integrate/references/results-and-errors.md new file mode 100644 index 000000000..80f194cda --- /dev/null +++ b/skills/nemo-fabric-integrate/references/results-and-errors.md @@ -0,0 +1,70 @@ + + +# Results, Evidence, And Errors + +## RunResult Fields + +Every invocation that reaches the adapter boundary returns a normalized +`RunResult`. Inspect `status` before reading output. Generated reference: the +[types reference](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.types.md). + +| Field | Meaning | +| --- | --- | +| `status` | Terminal invocation status: `succeeded`, `failed`, or `cancelled`. Branch on this. | +| `error` | Structured `ErrorInfo`, or `None` — may be `None` even when `status` is not `succeeded`, so do not use it as the success signal. | +| `output` | Harness output normalized to the configured output schema. | +| `artifacts` | Output files, logs, patches, and other materialized references. | +| `telemetry` | References to Relay or other telemetry streams from the run. | +| `events` | Ordered normalized lifecycle and invocation events. | +| `metadata` | Result-specific structured metadata. | +| `runtime_id`, `invocation_id`, `request_id` | Correlation IDs across runtimes, logs, telemetry, and artifacts. | + +Handle a result with this status-first pattern: + +```python +if result.status == "succeeded": + use_output(result.output, result.artifacts, result.telemetry) +else: + handle_failure(result.status, result.error, result.events) # failed, cancelled, ... +``` + +## Correlation IDs + +`runtime_id` identifies the runtime lifecycle, `invocation_id` identifies one +invocation within it, and `request_id` correlates the caller's request. +Fabric-generated values use type-specific prefixes such as `runtime-`, +`invocation-`, and `request-`; a caller may supply its own `request_id`. Store +and log each field separately and treat every value as opaque — do not parse or +reuse the encoding. + +## Error Hierarchy + +All public SDK errors inherit from `FabricError`. Fabric raises these when it +cannot return a normalized result; it does not return a partial `RunResult`. See +the [errors reference](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.errors.md). + +| Error | Meaning | +| --- | --- | +| `FabricConfigError` | Invalid config, request, or override. | +| `FabricCapabilityError` | Selected adapter does not support the requested operation. | +| `FabricRuntimeError` | Startup, invocation, or shutdown failed before a normalized result. | +| `FabricStateError` | Invalid runtime state transition (invoking after stop, overlapping invocations). | +| `FabricNativeUnavailableError` | Native extension is not installed or importable. | + +## Cleanup And Resilience + +- Prefer `run(...)` and `async with` runtimes: both attempt cleanup + automatically. Shutdown is attempted, not guaranteed — `stop()`, including the + automatic call at `async with` exit, can raise `FabricRuntimeError`. On a + normal block exit that error propagates; when an invocation already failed, the + cleanup failure is attached to the original exception rather than replacing it. +- The consumer owns job-level retries and rollout failure policy. Fabric marks a + runtime or invocation failed and returns structured error metadata when + possible, but does not retry by default. +- Transient failures may carry retryable error metadata. Capacity pressure + surfaces as a structured error or event (busy, rate limited, backpressure). + The consumer decides whether to wait, retry, start a replacement runtime, or + escalate. diff --git a/skills/nemo-fabric-integrate/references/sdk-api-inventory.md b/skills/nemo-fabric-integrate/references/sdk-api-inventory.md new file mode 100644 index 000000000..ef9498b67 --- /dev/null +++ b/skills/nemo-fabric-integrate/references/sdk-api-inventory.md @@ -0,0 +1,71 @@ + + +# SDK API Inventory + +`Fabric()` is the primary entrypoint. It is a plain, reusable object — not a +lifecycle context manager — and can plan, diagnose, or start multiple +independent runtimes. The generated +[client reference](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.client.md) +and [runtime reference](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/reference/api/python-library-reference/nemo_fabric.runtime.md) +document the public methods, but they omit `async` and keyword-only markers — +this inventory records those, and the installed `nemo_fabric` package ships type +information (`py.typed`) for exact signatures. + +## Fabric Methods + +The following table lists the `Fabric` methods and when to use each. + +| Method | Async | Use When | Returns | +| --- | --- | --- | --- | +| `resolve(config, *, base_dir=...)` | No | You need the normalized effective config, without resolving an adapter. | `EffectiveConfig` | +| `plan(config, *, base_dir=...)` | No | You need the selected adapter, capability routing, and runtime capabilities before running. | `RunPlan` | +| `doctor(config, *, base_dir=...)` | Yes | You need preflight diagnostics for adapter availability, config support, and environment assumptions. | `DoctorReport` | +| `run(config, *, base_dir=..., input=... \| request=...)` | Yes | You need one complete start, invoke, result, and stop cycle. | `RunResult` | +| `start_runtime(config, *, base_dir=..., overrides=...)` | Yes | You need state across multiple ordered invocations. | `Runtime` | + +`input` and `request` on `run(...)` are mutually exclusive. Use `input=...` for +the common case; use `request=RunRequest(...)` when the invocation needs a +caller-owned `request_id`, `context`, or overrides. + +## Runtime Methods + +The following table lists the `Runtime` members for driving a stateful runtime. + +| Member | Async | Notes | +| --- | --- | --- | +| `invoke(*, input=... \| request=...)` | Yes | One turn on an active runtime. One active invocation at a time; overlap raises `FabricStateError`. | +| `stop()` | Yes | Stop the runtime. Called automatically by `async with`. | +| `status` | No | `RuntimeStatus`: `ACTIVE`, `STOPPED`, or `FAILED`. | +| `runtime_id` | No | Opaque identifier for this runtime lifecycle. | +| `messages` / `invocations` | No | Copied harness history and per-turn IDs. | + +Always use a runtime as an async context manager so cleanup runs on exit. +Shutdown is attempted, not guaranteed — `stop()`, including the automatic call at +`async with` exit, can raise `FabricRuntimeError`: + +```python +async with await fabric.start_runtime(config, base_dir=base) as runtime: + result = await runtime.invoke(input="…") +``` + +## Execution Model + +Fabric separates configuration, planning, runtime lifecycle, and individual +invocations: + +```text +FabricConfig -> plan() -> RunPlan -> start_runtime() -> Runtime -> invoke() -> RunResult +``` + +- `Fabric` is a lightweight facade; it holds no started state and needs no + cleanup. +- A `Runtime` owns stateful execution and shutdown, so it is the object used + with `async with`. +- A runtime is a logical execution boundary, not necessarily an operating-system + process. Harness-native threads, sessions, and conversations remain + adapter-owned state associated with the runtime. +- The application owns scheduling, queues, retries, and how many runtimes to + run. Fabric provides only the runtime contract.