diff --git a/README.md b/README.md index ac7de763c..b57b3bdaf 100644 --- a/README.md +++ b/README.md @@ -93,16 +93,19 @@ from nemo_fabric import ( HarnessConfig, MetadataConfig, ModelConfig, + RuntimeConfig, ) config = FabricConfig( metadata=MetadataConfig(name="quickstart-agent"), harness=HarnessConfig(adapter_id="nvidia.fabric.hermes"), + runtime=RuntimeConfig(max_turns=1), models={ "default": ModelConfig( provider="nvidia", model="nvidia/nemotron-3-nano-30b-a3b", api_key_env="NVIDIA_API_KEY", + base_url="https://integrate.api.nvidia.com/v1", ) }, ) @@ -130,7 +133,7 @@ harness inside an isolated task container. Refer to the [Harbor execution model](examples/harbor/README.md#execution-model) for details. NeMo Fabric can also operate with the NeMo Fabric runtime and the agent harness -in separate Python environments. This setup can match existing deployment +in separate Python environments. This setup can match existing deployment boundaries and isolate their dependencies. Create an environment for the NeMo Fabric runtime: diff --git a/adapters/README.md b/adapters/README.md index abe4fc13e..c74d3ee57 100644 --- a/adapters/README.md +++ b/adapters/README.md @@ -43,19 +43,69 @@ provider should expose more precise provenance. ## Configuration Compatibility -| Agent Harness | Models | Tools / Blocked Tools | MCP | Skills | Subagents | +| Agent Harness | Models | Tool Policy | MCP | Skills | Subagents | | --- | --- | --- | --- | --- | --- | -| [Claude](claude/README.md) | Anthropic and NVIDIA-hosted Anthropic Messages-compatible models | `allowed_tools` adapter setting / normalized `tools.blocked` | Normalized: stdio, HTTP, streamable HTTP, and SSE | Normalized `skills.paths` | Not exposed | -| [Codex](codex/README.md) | OpenAI; NVIDIA Responses-compatible models without Relay | Codex-native tools / configuring `tools.blocked` is unsupported and raises `UnsupportedToolsPolicy` | Normalized: stdio, HTTP, and streamable HTTP | Normalized `SKILL.md` directories | Not exposed | -| [LangChain Deep Agents](deepagents/README.md) | LangChain model providers | Built-ins and MCP / normalized middleware block list | Normalized through `langchain-mcp-adapters` | Normalized | Constrained declarative local delegation | -| [Hermes Agent](hermes/README.md) | Normalized provider, model, and base URL | Toolsets / normalized disabled toolsets | Normalized | Normalized | Not exposed | +| [Claude](claude/README.md) | Native Anthropic or a configured Anthropic Messages-compatible provider | `tools.enabled` selects built-ins; a pre-tool hook enforces enabled and blocked names across built-in, MCP, and plugin tools | Normalized: stdio, HTTP, streamable HTTP, and SSE | Normalized `skills.paths` | Not exposed | +| [Codex](codex/README.md) | Native OpenAI or a configured Responses-compatible provider | `tools.enabled` and `tools.blocked` unsupported | Normalized: stdio, HTTP, and streamable HTTP | Normalized `SKILL.md` directories | Not exposed | +| [LangChain Deep Agents](deepagents/README.md) | LangChain model providers | Middleware enforces `tools.enabled` and `tools.blocked` across built-ins, MCP, and local subagents | Normalized through `langchain-mcp-adapters` | Normalized | Constrained declarative local delegation | +| [Hermes Agent](hermes/README.md) | Configurable provider, model, and base URL | `tools.enabled` and `tools.blocked` map to Hermes native toolset selectors | Normalized | Normalized | Not exposed | "Normalized" means that the adapter accepts the corresponding `FabricConfig` field. "Not exposed" does not mean that the underlying harness lacks the -feature; it means that NeMo Fabric does not provide a portable configuration surface -for it. NeMo Fabric normalizes a blocked-tool list, not a portable tool-definition -catalog. Deep Agents subagents are limited to declarative local subagents that -inherit the parent agent's capabilities. +feature; it means that NeMo Fabric does not provide a portable configuration +surface for it. Tool values are adapter-native selectors; NeMo Fabric does not +define a cross-harness tool-name catalog. Planning fails when the selected +adapter cannot enforce a configured policy. Deep Agents subagents are limited +to declarative local subagents that inherit the parent agent's capabilities. + +`RunPlan.capability_plan.routes` records execution ownership, not network +routing. `harness_native` assigns a capability to the selected adapter, +`fabric_managed` assigns it to NeMo Fabric, and `unsupported` means neither can +execute it. Scalar fields are validated separately against +`adapter_descriptor.config.accepts`. + +### Complete FabricConfig Support + +`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: + +| `FabricConfig` Field | Claude | Codex | Deep Agents | Hermes Agent | +| --- | --- | --- | --- | --- | +| `schema_version` | Core | Core | Core | Core | +| `metadata.name`, `.description` | Core | Core | Core | Core | +| `harness.adapter_id`, `.resolution` | Core | Core | Core | Core | +| `harness.settings` | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch | +| `models..provider` | `anthropic` uses native auth; custom names require an Anthropic Messages-compatible `base_url` and `api_key_env` | `openai` uses native auth; custom names require a Responses-compatible `base_url` and `api_key_env` | Dynamic LangChain provider; custom OpenAI-compatible endpoints require `base_url` and `api_key_env` | Dynamic Hermes provider | +| `models..model` | Yes | Yes | Yes | Yes | +| `models..api_key_env` | Yes | Yes | Yes | Yes | +| `models..base_url` | Yes | Yes | Yes | Yes | +| `models..temperature` | No | No | Yes | Yes | +| `models..settings.` | No keys declared | No keys declared | No keys declared | No keys declared | +| `instructions.system` | Yes | Yes; base instructions | Yes | Yes | +| `runtime.input_schema`, `.output_schema` | Core | Core | Core | Core | +| `runtime.artifacts`, `.timeout_seconds` | Core | Core | Core | Core | +| `runtime.max_turns` | Yes | No | No | Yes; iteration limit | +| `environment.provider`, `.control_location`, `.ownership` | Core | Core | Core | Core | +| `environment.workspace`, `.artifacts`, `.env` | Core | Core | Core | Core | +| `environment.connection`, `.metadata`, `.settings` | Environment-provider-owned | Environment-provider-owned | Environment-provider-owned | Environment-provider-owned | +| `tools.enabled`, `.blocked` | Yes | No | Yes | Yes; native selectors are Hermes toolset names | +| `skills.paths` | Yes | Yes | Yes | Yes | +| `mcp.servers..transport`, `.url` with `harness_native` exposure | Yes | Yes | Yes | Yes | +| `mcp.servers..exposure = "fabric_managed"` | No; not implemented | No; not implemented | No; not implemented | No; not implemented | +| `telemetry.providers.relay` | Yes | Yes | Yes | Yes | +| `telemetry.providers.native` | No | Yes; OpenTelemetry | Yes; OpenTelemetry and OpenInference | No | +| `telemetry.providers..config` | Declared-provider pass-through | Declared-provider pass-through | Declared-provider pass-through | Declared-provider pass-through | +| `relay.project`, `.output_dir`, `.observability` | Yes | Yes | Yes | Yes | +| `relay.components`, `.policy` | Yes | Yes | Yes | Yes | +| Additive `extensions` on typed config objects | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | + +The selected model role is `default`, or the sole configured role when no +`default` exists. More than one role without `default` fails planning. +`runtime.max_turns` is optional; omitting it preserves adapter-native defaults +without creating a compatibility requirement. ## Runtime and Observability Compatibility diff --git a/adapters/claude/README.md b/adapters/claude/README.md index e7e97c7c0..f7950528f 100644 --- a/adapters/claude/README.md +++ b/adapters/claude/README.md @@ -9,9 +9,8 @@ The `nvidia.fabric.claude` adapter uses the official Claude Agent SDK for Python behind NeMo Fabric's normalized invocation contract. The SDK is an implementation detail; consumers select the Claude harness by adapter ID. -This adapter pins `claude-agent-sdk==0.2.120`. The SDK supplies its compatible -Claude Code runtime unless `harness.settings.cli_path` explicitly selects -another executable. +This adapter pins `claude-agent-sdk==0.2.120`. The SDK supplies and selects its +compatible Claude Code runtime. ## Install @@ -35,12 +34,13 @@ bearer credential, `ANTHROPIC_API_KEY` for a static API credential, or Anthropic Workload Identity Federation (WIF) for production and CI workloads that should not store a long-lived API key. -When `models.default.provider` is `nvidia`, the adapter reads the selected -model's credential from `api_key_env` (default: `NVIDIA_API_KEY`) and translates -the configured NVIDIA `/v1` endpoint into the host URL expected by Claude Code. -Set the endpoint in `models.default.settings.base_url` or -`NVIDIA_FRONTIER_BASE_URL`; the adapter does not assume a default frontier -endpoint. This request-scoped mapping does not change the parent environment. +The native `anthropic` provider can use any Claude authentication mode above +without an explicit endpoint. For another provider name, configure both +`models..api_key_env` and `models..base_url`. The endpoint must +implement the Anthropic Messages protocol; the adapter maps the named +credential and endpoint into the environment expected by Claude Code. Provider +names identify configuration; the adapter does not maintain a provider +allowlist. The runtime-scoped mapping does not change the parent environment. The adapter forwards the Anthropic profile and federation environment variables that Claude Code and the Claude Agent SDK consume. This includes @@ -59,7 +59,6 @@ for mode selection, required WIF variables, and the Relay boundary. Package installation is verified by the adapter wheel and module-entrypoint tests. Relay-enabled runs also require the external `nemo-relay` CLI. Refer to the [NeMo Relay CLI](https://docs.nvidia.com/nemo/fabric/getting-started/install#nemo-relay-cli) install guide for instructions on installing the CLI tool. -``` The Python `nemo-relay` package does not install this executable. Refer to the [NeMo Relay installation guide](https://docs.nvidia.com/nemo/relay/getting-started/installation) @@ -84,12 +83,18 @@ hosting is adapter-declared; consumers do not configure a runtime strategy in Configure portable capabilities through the normalized `FabricConfig` fields: -- `models` selects the Claude model. The adapter accepts the native `anthropic` - provider and NVIDIA-hosted Anthropic Messages-compatible models through the - `nvidia` provider. -- `environment.workspace` sets the Claude working directory. -- `tools.blocked` maps to Claude `disallowed_tools` using Claude-native tool - names. +- `models` selects the Claude model. The native `anthropic` provider retains + Claude authentication and endpoint discovery. Any other provider name must + configure an Anthropic Messages-compatible `base_url` and `api_key_env`. +- `instructions.system` supplies the Claude system instructions. +- `runtime.max_turns` sets the Claude turn limit. +- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline. +- `environment.workspace` sets the Claude working directory, and + `environment.env` supplies explicit harness-visible variables. +- `tools.enabled` selects Claude built-in tools. `None` preserves the Claude + default, while an empty list disables every tool. +- `tools.blocked` maps to Claude `disallowed_tools`. A pre-tool hook enforces + both lists across built-in, MCP, and plugin tools. - `mcp` configures stdio, HTTP, streamable HTTP, or SSE servers. For stdio, NeMo Fabric parses `url` as a command plus arguments. - `skills.paths` names skill directories that contain `SKILL.md`. The adapter @@ -97,20 +102,13 @@ Configure portable capabilities through the normalized `FabricConfig` fields: Only Claude-specific controls belong in `harness.settings`: -- `system_prompt`, `allowed_tools`, and `permission_mode` -- `max_turns`, `max_budget_usd`, and `timeout_seconds` +- `allowed_tools` and `permission_mode` +- `max_budget_usd` - `setting_sources` (defaults to `[]` for deterministic isolation) -- `cli_path` for testing or an explicitly installed Claude Code executable -- `nemo_relay_command` for an explicitly installed NeMo Relay CLI executable -- `env` for variables explicitly forwarded to Claude Code - -Putting `model_name`, `cwd`, `tools`, `disallowed_tools`, `mcp_servers`, or -`skills` in `harness.settings` is an error. Use the corresponding normalized -field so the same consumer configuration can compose with other adapters. The adapter filters the inherited environment before launching Claude Code. It retains portable OS/config variables, the selected model's `api_key_env`, -and explicitly configured `settings.env` values. Raw Claude stderr is consumed +and explicitly configured `environment.env` values. Raw Claude stderr is consumed by the SDK and is not persisted as a NeMo Fabric artifact. ## Relay Observability @@ -127,8 +125,9 @@ config.enable_relay( For each Relay-enabled Claude runtime, NeMo Fabric starts one `nemo-relay` gateway, waits for its health endpoint, and stops it with the runtime. NeMo Fabric passes the gateway URL to the connected Claude Code process through `ANTHROPIC_BASE_URL` -and `NEMO_RELAY_GATEWAY_URL`. It also stages a runtime-scoped Claude plugin that -forwards lifecycle hooks with `nemo-relay hook-forward claude`. +and `NEMO_RELAY_GATEWAY_URL`, and passes the selected explicit model endpoint to +the gateway as its Anthropic upstream. It also stages a runtime-scoped Claude +plugin that forwards lifecycle hooks with `nemo-relay hook-forward claude`. `Fabric.run(...)` starts the same runtime, invokes it once, and stops it, so the gateway has the same lifecycle as that single invocation. @@ -136,8 +135,7 @@ The NeMo Fabric result includes `relay_runtime.gateway_config_path`, `relay_runtime.gateway_log_path`, and the collected `relay_artifacts`. Relay startup failures return a stable adapter error and retain the gateway log for diagnosis. The default Claude Agent SDK dependency bundles a compatible Claude -Code executable. An executable supplied with `cli_path` must support the Relay -plugin's complete hook set, including `UserPromptExpansion`. +Code executable. ## Typed Configuration @@ -152,6 +150,8 @@ from nemo_fabric import ( Fabric, FabricConfig, HarnessConfig, + InstructionConfig, + InstructionsConfig, McpConfig, McpServerConfig, MetadataConfig, @@ -168,9 +168,7 @@ config = FabricConfig( adapter_id="nvidia.fabric.claude", resolution="preinstalled", settings={ - "system_prompt": "Review changes for correctness and regressions.", "permission_mode": "dontAsk", - "max_turns": 8, }, ), models={ @@ -180,9 +178,22 @@ config = FabricConfig( api_key_env="ANTHROPIC_API_KEY", ) }, - runtime=RuntimeConfig(artifacts="./artifacts"), + instructions=InstructionsConfig( + system=InstructionConfig( + content="Review changes for correctness and regressions.", + mode="replace", + ) + ), + runtime=RuntimeConfig( + artifacts="./artifacts", + timeout_seconds=600, + max_turns=8, + ), environment=EnvironmentConfig(provider="local", workspace="."), - tools=ToolsConfig(blocked=["WebFetch"]), + tools=ToolsConfig( + enabled=["Read", "Edit", "Bash"], + blocked=["WebFetch"], + ), mcp=McpConfig( servers={ "repo": McpServerConfig( diff --git a/adapters/claude/fabric-adapter.json b/adapters/claude/fabric-adapter.json index 9f8996f94..52b11a520 100644 --- a/adapters/claude/fabric-adapter.json +++ b/adapters/claude/fabric-adapter.json @@ -7,7 +7,16 @@ "module": "nemo_fabric_adapters.claude.adapter" }, "config": { - "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "instructions.system", + "runtime.max_turns", + "tools.enabled", + "tools.blocked", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py index a6867f2bb..81293ad31 100644 --- a/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py +++ b/adapters/claude/src/nemo_fabric_adapters/claude/adapter.py @@ -29,6 +29,7 @@ from claude_agent_sdk import Message from claude_agent_sdk import ProcessError from claude_agent_sdk import ResultMessage +from claude_agent_sdk import HookMatcher from claude_agent_sdk._errors import MessageParseError from nemo_fabric_adapters.common import lifecycle from nemo_fabric_adapters.common import relay_gateway @@ -46,14 +47,6 @@ "auto", } SETTING_SOURCES = {"user", "project", "local"} -NORMALIZED_SETTING_FIELDS = { - "model_name": "FabricConfig.models", - "cwd": "FabricConfig.environment.workspace", - "tools": "FabricConfig.tools", - "disallowed_tools": "FabricConfig.tools.blocked", - "mcp_servers": "FabricConfig.mcp", - "skills": "FabricConfig.skills", -} INHERITED_ENV_NAMES = { "ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN", @@ -190,25 +183,11 @@ def _settings(payload: dict[str, Any]) -> dict[str, Any]: return _mapping(common_utils.settings_payload(payload), name="harness.settings") -def _validate_settings_boundary(settings: dict[str, Any]) -> None: - for name, normalized_field in NORMALIZED_SETTING_FIELDS.items(): - if name in settings: - raise AdapterConfigError( - "claude_invalid_configuration", - f"harness.settings.{name} is not supported; use {normalized_field}", - ) - - -def _models(payload: dict[str, Any]) -> dict[str, Any]: - return _mapping(common_utils.models_payload(payload), name="models") - - def _selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: - models = _models(payload) - if not models: - return {} - selected = models.get("default") or next(iter(models.values())) - return _mapping(selected, name="selected model") + return _mapping( + common_utils.selected_model_config(payload), + name="selected model", + ) def _resolve_path(payload: dict[str, Any], value: str | Path) -> Path: @@ -230,10 +209,10 @@ def selected_model(payload: dict[str, Any]) -> str | None: if value is None: return None provider = model_config.get("provider") - if provider not in {"anthropic", "nvidia"}: + if not isinstance(provider, str) or not provider: raise AdapterConfigError( "claude_invalid_configuration", - "models.default.provider must be anthropic or nvidia for the Claude adapter", + "selected model provider must be a non-empty string", ) if not isinstance(value, str) or not value: raise AdapterConfigError( @@ -242,43 +221,65 @@ def selected_model(payload: dict[str, Any]) -> str | None: return value.removeprefix("anthropic/") if provider == "anthropic" else value -def _nvidia_environment(payload: dict[str, Any]) -> dict[str, str]: +def _anthropic_base_url(model: dict[str, Any]) -> str | None: + base_url = common_utils.get_base_url(model) + if base_url is None: + return None + if not isinstance(base_url, str) or not base_url: + raise AdapterConfigError( + "claude_invalid_configuration", + "selected model base_url must be a non-empty string", + ) + base_url = base_url.rstrip("/") + return ( + base_url.removesuffix("/v1") + if model.get("provider") != "anthropic" + else base_url + ) + + +def _model_environment( + payload: dict[str, Any], environment: dict[str, str] +) -> dict[str, str]: model = _selected_model_config(payload) - if model.get("provider") != "nvidia": - return {} - api_key_env = model.get("api_key_env") or "NVIDIA_API_KEY" + provider = model.get("provider") + api_key_env = model.get("api_key_env") if not isinstance(api_key_env, str) or not api_key_env: + if api_key_env is None: + api_key = None + else: + raise AdapterConfigError( + "claude_invalid_configuration", + "selected model api_key_env must be a non-empty string", + ) + else: + api_key = environment.get(api_key_env) or os.environ.get(api_key_env) + if provider != "anthropic" and api_key_env is None: raise AdapterConfigError( "claude_invalid_configuration", - "models.default.api_key_env must be a non-empty string", + "selected model api_key_env is required for a custom " + "Anthropic Messages-compatible provider", ) - api_key = os.environ.get(api_key_env) - if not api_key: + if api_key_env is not None and not api_key: raise AdapterConfigError( "claude_invalid_configuration", - f"{api_key_env} is required for the NVIDIA model provider", + f"{api_key_env} is required for the selected model provider", ) - settings = _settings(payload) - model_settings = _mapping(model.get("settings"), name="models.default.settings") - base_url = ( - settings.get("base_url") - or model_settings.get("base_url") - or os.environ.get("NVIDIA_FRONTIER_BASE_URL") - ) - if not isinstance(base_url, str) or not base_url: + base_url = _anthropic_base_url(model) + if provider != "anthropic" and not base_url: raise AdapterConfigError( "claude_invalid_configuration", - "models.default.settings.base_url or NVIDIA_FRONTIER_BASE_URL is required " - "for the NVIDIA model provider", + "selected model base_url is required for a custom " + "Anthropic Messages-compatible provider", ) - # Claude Code appends the Anthropic API version path itself, while Fabric's - # shared NVIDIA endpoint includes it for OpenAI-compatible clients. - claude_base_url = base_url.rstrip("/").removesuffix("/v1") - return { - "ANTHROPIC_API_KEY": api_key, - "ANTHROPIC_AUTH_TOKEN": "", - "ANTHROPIC_BASE_URL": claude_base_url, - } + values: dict[str, str] = {} + if api_key: + values["ANTHROPIC_API_KEY"] = api_key + if base_url: + values["ANTHROPIC_BASE_URL"] = base_url + if provider != "anthropic": + values["ANTHROPIC_AUTH_TOKEN"] = "" + return values def _mcp_servers(payload: dict[str, Any]) -> dict[str, Any]: @@ -416,13 +417,7 @@ def prepare_claude_relay(payload: dict[str, Any]) -> ClaudeRelaySettings | None: if not common_utils.relay_enabled(payload): return None - settings = _settings(payload) - command = settings.get("nemo_relay_command") or "nemo-relay" - if not isinstance(command, (str, Path)): - raise AdapterConfigError( - "claude_invalid_configuration", - "nemo_relay_command must be a path", - ) + command = os.environ.get("FABRIC_TEST_NEMO_RELAY_COMMAND", "nemo-relay") try: executable = relay_gateway.resolve_relay_command( Path(common_utils.base_dir(payload)).resolve(), @@ -466,6 +461,7 @@ def prepare_claude_relay(payload: dict[str, Any]) -> ClaudeRelaySettings | None: bind=gateway_bind, url=f"http://{gateway_bind}", log_path=config_path.parent / "gateway.log", + anthropic_base_url=_anthropic_base_url(_selected_model_config(payload)), ) plugin_path = config_path.parent / "claude-plugin" try: @@ -487,25 +483,51 @@ def discard_stderr(_: str) -> None: """Consume Claude Code stderr without exposing it through Fabric artifacts.""" +def tool_policy_hooks(payload: dict[str, Any]) -> dict[str, list[HookMatcher]] | None: + """Enforce the normalized tool policy across built-in, MCP, and plugin tools.""" + + enabled = common_utils.enabled_tools(payload) + blocked = set(common_utils.blocked_tools(payload)) + if enabled is None and not blocked: + return None + enabled_set = None if enabled is None else set(enabled) + + async def enforce_policy( + hook_input: dict[str, Any], + _tool_use_id: str | None, + _context: dict[str, Any], + ) -> dict[str, Any]: + tool_name = str(hook_input.get("tool_name") or "") + is_blocked = tool_name in blocked or ( + enabled_set is not None and tool_name not in enabled_set + ) + if not is_blocked: + return {} + return { + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "permissionDecision": "deny", + "permissionDecisionReason": ( + f"Tool '{tool_name}' is blocked by the configured tools policy." + ), + } + } + + return {"PreToolUse": [HookMatcher(hooks=[enforce_policy])]} + + def build_options( payload: dict[str, Any], *, relay: ClaudeRelaySettings | None = None, ) -> ClaudeAgentOptions: settings = _settings(payload) - _validate_settings_boundary(settings) permission_mode = settings.get("permission_mode") if permission_mode is not None and permission_mode not in PERMISSION_MODES: raise AdapterConfigError( "claude_invalid_configuration", "permission_mode is invalid" ) - max_turns = settings.get("max_turns") - if max_turns is not None and ( - isinstance(max_turns, bool) or not isinstance(max_turns, int) or max_turns <= 0 - ): - raise AdapterConfigError( - "claude_invalid_configuration", "max_turns must be positive" - ) + max_turns = common_utils.max_turns(payload) max_budget = settings.get("max_budget_usd") if max_budget is not None: max_budget = _positive_number(max_budget, name="max_budget_usd") @@ -515,17 +537,10 @@ def build_options( raise AdapterConfigError( "claude_invalid_configuration", "setting_sources is invalid" ) - cli_path = settings.get("cli_path") - if cli_path is not None and not isinstance(cli_path, (str, Path)): - raise AdapterConfigError( - "claude_invalid_configuration", "cli_path must be a path" - ) + cli_path = os.environ.get("FABRIC_TEST_CLAUDE_CLI_PATH") - system_prompt = settings.get("system_prompt") - if system_prompt is not None and not isinstance(system_prompt, (str, dict)): - raise AdapterConfigError( - "claude_invalid_configuration", "system_prompt is invalid" - ) + system_prompt = common_utils.system_instruction(payload) + enabled_tools = common_utils.enabled_tools(payload) plugins = _stage_skill_plugin(payload) has_skill_plugin = bool(plugins) if relay is not None: @@ -535,14 +550,15 @@ def build_options( cwd=resolve_cwd(payload), model=selected_model(payload), system_prompt=system_prompt, - tools=None, + tools=enabled_tools, allowed_tools=_string_list(settings.get("allowed_tools"), name="allowed_tools"), disallowed_tools=common_utils.blocked_tools(payload), + hooks=tool_policy_hooks(payload), permission_mode=permission_mode, max_turns=max_turns, max_budget_usd=max_budget, setting_sources=sources, - cli_path=_resolve_path(payload, cli_path) if cli_path is not None else None, + cli_path=_resolve_path(payload, cli_path) if cli_path else None, mcp_servers=_mcp_servers(payload), strict_mcp_config=True, skills="all" if has_skill_plugin else None, @@ -556,7 +572,7 @@ def build_options( def timeout_seconds(payload: dict[str, Any]) -> float: - value = _settings(payload).get("timeout_seconds", 1800) + value = common_utils.timeout_seconds(payload, default=1800) return _positive_number(value, name="timeout_seconds") @@ -679,16 +695,23 @@ def child_environment( api_key_env = model.get("api_key_env") if isinstance(api_key_env, str) and api_key_env in os.environ: values[api_key_env] = os.environ[api_key_env] - configured = _mapping(_settings(payload).get("env"), name="harness.settings.env") - if any( - not isinstance(key, str) or not isinstance(value, str) - for key, value in configured.items() - ): + configured = common_utils.environment_env(payload) + values.update(configured) + model_environment = _model_environment(payload, values) + conflicts = sorted( + name + for name, value in model_environment.items() + if name in configured and configured[name] != value + ) + if conflicts: + fields = ", ".join(f"environment.env.{name}" for name in conflicts) raise AdapterConfigError( - "claude_invalid_configuration", "harness.settings.env must contain strings" + "claude_invalid_configuration", + f"{fields} conflicts with the selected model configuration; " + "configure model credentials and endpoints through models., " + "or remove the duplicate environment.env values", ) - values.update(configured) - values.update(_nvidia_environment(payload)) + values.update(model_environment) if relay_gateway_url is not None: values["NEMO_RELAY_GATEWAY_URL"] = relay_gateway_url values["ANTHROPIC_BASE_URL"] = relay_gateway_url diff --git a/adapters/codex/README.md b/adapters/codex/README.md index 660af0ffd..b14b5e333 100644 --- a/adapters/codex/README.md +++ b/adapters/codex/README.md @@ -49,25 +49,20 @@ the SDK runtime. The current real-agent acceptance path validates an existing Codex login; it does not yet claim a raw environment variable as a complete login flow. -When `models.default.provider` is `nvidia`, the adapter defines a Codex model -provider for the configured NVIDIA Responses endpoint. `Fabric.run(...)` owns -that provider for one invocation, while `Fabric.start_runtime(...)` fixes it for -the lifetime of the persistent runtime. The adapter reads the credential from -`api_key_env` (default: `NVIDIA_API_KEY`) and isolates Codex state under the -NeMo Fabric artifact root, so execution does not depend on or modify a user's Codex -login. Set the endpoint in -`models.default.settings.base_url` or `NVIDIA_FRONTIER_BASE_URL`; the adapter -does not assume a default frontier endpoint. +The native `openai` provider retains Codex authentication and endpoint +discovery. For another provider name, configure both +`models..api_key_env` and `models..base_url`. The endpoint must +implement the OpenAI Responses protocol. The adapter defines a runtime-scoped +Codex model provider with that name and isolates its Codex state under the +NeMo Fabric artifact root, so execution does not depend on or modify a user's +Codex login. Provider names identify configuration; the adapter does not +maintain a provider allowlist. The adapter depends on the Codex SDK, which installs and selects its matching app-server runtime. NeMo Fabric does not declare the runtime package directly or treat it as a user-installed command or adapter descriptor requirement. -A `codex` command on `PATH` is not selected implicitly. To override the -SDK-selected runtime intentionally, set -`harness.settings.codex_bin` to an app-server path that is absolute or relative -to the explicit `base_dir`. NeMo Fabric passes the resolved path through -`CodexConfig.codex_bin`; the SDK remains the execution driver. +A `codex` command on `PATH` is not selected implicitly. ## Execution Model @@ -87,10 +82,13 @@ return codes, stdout, or stderr. Use normalized `FabricConfig` fields for portable configuration: -- `models` selects the Codex model. The adapter supports the built-in `openai` - provider and NVIDIA-hosted Responses-compatible models through the `nvidia` - provider. -- `environment.workspace` sets the working directory. +- `models` selects the Codex model. The native `openai` provider retains Codex + authentication and endpoint discovery. Any other provider name must configure + a Responses-compatible `base_url` and `api_key_env`. +- `instructions.system` maps to Codex base instructions. +- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline. +- `environment.workspace` sets the working directory, and `environment.env` + supplies explicit harness-visible variables. - `mcp` maps stdio, HTTP, and streamable HTTP servers into the Codex thread's `mcp_servers` configuration. For stdio, NeMo Fabric parses `url` as a command plus arguments. @@ -109,18 +107,16 @@ Codex-specific controls belong in `harness.settings`: - `sandbox`: `read-only`, `workspace-write`, or `danger-full-access` - `approval_mode`: `auto_review` or `deny_all` -- `base_instructions` and `developer_instructions` -- `personality`, `reasoning_effort`, `service_name`, and `service_tier` +- `developer_instructions` +- `personality`, `reasoning_effort`, and `service_tier` - `output_schema` for SDK-native structured output -- `codex_bin` for an explicit Codex app-server runtime override - `config_overrides` as dotted Codex configuration keys applied when the SDK runtime starts, such as Codex-only MCP timeout or required-server options -- `timeout_seconds`, defaulting to 1800 -- `env` for variables explicitly forwarded to the Codex runtime -- `nemo_relay_command` for the optional external Relay gateway executable -Set model selection through `models` and the working directory through -`environment.workspace`. +Set model selection and endpoints through `models`, system instructions through +`instructions.system`, the invocation deadline through +`runtime.timeout_seconds`, and the working directory and explicit environment +through `environment`. For `Fabric.start_runtime(...)`, the model provider, MCP configuration, skill roots, and `config_overrides` are fixed when the runtime starts and cannot vary @@ -130,9 +126,11 @@ same settings are scoped to that single invocation. The adapter filters the inherited environment. It retains portable OS and Codex state variables, the selected model's `api_key_env`, and explicit -`settings.env` values while clearing unrelated parent-process secrets. +`environment.env` values while clearing unrelated parent-process secrets. ## Relay Integration Relay-enabled runs also require the external `nemo-relay` CLI. Refer to the [NeMo Relay CLI](https://docs.nvidia.com/nemo/fabric/getting-started/install#nemo-relay-cli) install guide for instructions on installing the CLI tool. +NeMo Fabric routes the selected Responses-compatible provider through the +gateway and passes its explicit `base_url` to Relay as the upstream endpoint. diff --git a/adapters/codex/fabric-adapter.json b/adapters/codex/fabric-adapter.json index 251ab043f..a31540298 100644 --- a/adapters/codex/fabric-adapter.json +++ b/adapters/codex/fabric-adapter.json @@ -7,7 +7,13 @@ "module": "nemo_fabric_adapters.codex.adapter" }, "config": { - "accepts": ["models", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "instructions.system", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py index c69f69437..7a13fc9d7 100644 --- a/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py +++ b/adapters/codex/src/nemo_fabric_adapters/codex/adapter.py @@ -78,19 +78,6 @@ "https_proxy", "no_proxy", } -REMOVED_CLI_SETTINGS = { - "codex_args", - "codex_command", - "codex_profile", - "codex_state_dir", - "skip_git_repo_check", -} -NORMALIZED_SETTING_FIELDS = { - "cwd": "FabricConfig.environment.workspace", - "mcp_servers": "FabricConfig.mcp", - "model_name": "FabricConfig.models", - "skills": "FabricConfig.skills", -} LOGGER = logging.getLogger(__name__) @@ -144,22 +131,6 @@ def _settings(payload: dict[str, Any]) -> dict[str, Any]: return _mapping(common_utils.settings_payload(payload), name="harness.settings") -def _validate_settings_boundary(settings: dict[str, Any]) -> None: - removed = sorted(REMOVED_CLI_SETTINGS.intersection(settings)) - if removed: - names = ", ".join(f"harness.settings.{name}" for name in removed) - raise AdapterConfigError( - "codex_invalid_configuration", - f"Codex CLI-only settings are not supported by the SDK adapter: {names}", - ) - for name, normalized_field in NORMALIZED_SETTING_FIELDS.items(): - if name in settings: - raise AdapterConfigError( - "codex_invalid_configuration", - f"harness.settings.{name} is not supported; use {normalized_field}", - ) - - def runtime_id(payload: dict[str, Any]) -> str: value = common_utils.runtime_context(payload).get("runtime_id") if not isinstance(value, str) or not value: @@ -308,10 +279,10 @@ def resolve_cwd(payload: dict[str, Any]) -> Path: def _selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: - settings = _settings(payload) - models = _mapping(common_utils.models_payload(payload), name="models") - selected = models.get(settings.get("model", "default")) or {} - return _mapping(selected, name="selected model") + return _mapping( + common_utils.selected_model_config(payload), + name="selected model", + ) def selected_model(payload: dict[str, Any]) -> str | None: @@ -320,10 +291,10 @@ def selected_model(payload: dict[str, Any]) -> str | None: if value is None: return None provider = model_config.get("provider") - if provider not in {"openai", "nvidia"}: + if not isinstance(provider, str) or not provider: raise AdapterConfigError( "codex_invalid_configuration", - "selected model provider must be openai or nvidia for the Codex adapter", + "selected model provider must be a non-empty string", ) if not isinstance(value, str) or not value: raise AdapterConfigError( @@ -333,40 +304,46 @@ def selected_model(payload: dict[str, Any]) -> str | None: def selected_model_provider(payload: dict[str, Any]) -> str: - return str(_selected_model_config(payload).get("provider") or "openai") + provider = _selected_model_config(payload).get("provider") + if not isinstance(provider, str) or not provider: + raise AdapterConfigError( + "codex_invalid_configuration", + "selected model provider must be a non-empty string", + ) + return provider -def nvidia_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: +def custom_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: model_config = _selected_model_config(payload) - if model_config.get("provider") != "nvidia": + provider = selected_model_provider(payload) + if provider == "openai": return {} - api_key_env = model_config.get("api_key_env") or "NVIDIA_API_KEY" + api_key_env = model_config.get("api_key_env") if not isinstance(api_key_env, str) or not api_key_env: raise AdapterConfigError( "codex_invalid_configuration", - "models.default.api_key_env must be a non-empty string", + "selected model api_key_env is required for a custom " + "Responses-compatible provider", ) - if not os.environ.get(api_key_env): + if not ( + common_utils.environment_env(payload).get(api_key_env) + or os.environ.get(api_key_env) + ): raise AdapterConfigError( "codex_invalid_configuration", - f"{api_key_env} is required for the NVIDIA model provider", + f"{api_key_env} is required for the selected model provider", ) - model_settings = _mapping( - model_config.get("settings"), name="selected model settings" - ) - base_url = model_settings.get("base_url") or os.environ.get( - "NVIDIA_FRONTIER_BASE_URL" - ) + base_url = common_utils.get_base_url(model_config) if not isinstance(base_url, str) or not base_url: raise AdapterConfigError( "codex_invalid_configuration", - "models.default.settings.base_url or NVIDIA_FRONTIER_BASE_URL is required " - "for the NVIDIA model provider", + "selected model base_url is required for a custom " + "Responses-compatible provider", ) return { "model_providers": { - "nvidia": { - "name": "NVIDIA", + provider: { + "name": provider, "base_url": base_url.rstrip("/"), "env_key": api_key_env, "wire_api": "responses", @@ -375,6 +352,14 @@ def nvidia_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: } +def openai_model_provider_config(payload: dict[str, Any]) -> dict[str, Any]: + model_config = _selected_model_config(payload) + if model_config.get("provider") != "openai": + return {} + base_url = common_utils.get_base_url(model_config) + return {"openai_base_url": base_url.rstrip("/")} if base_url else {} + + def sandbox(payload: dict[str, Any]) -> Sandbox: value = _settings(payload).get("sandbox", "read-only") try: @@ -398,7 +383,7 @@ def approval_mode(payload: dict[str, Any]) -> ApprovalMode: def timeout_seconds(payload: dict[str, Any]) -> float: - value = _settings(payload).get("timeout_seconds", DEFAULT_TIMEOUT_SECONDS) + value = common_utils.timeout_seconds(payload, default=DEFAULT_TIMEOUT_SECONDS) if isinstance(value, bool) or not isinstance(value, (int, float)): raise AdapterConfigError( "codex_invalid_configuration", "timeout_seconds must be positive" @@ -453,18 +438,16 @@ def child_environment( api_key_env = model_config.get("api_key_env") if isinstance(api_key_env, str) and api_key_env in os.environ: values[api_key_env] = os.environ[api_key_env] - configured = _mapping(_settings(payload).get("env"), name="harness.settings.env") - if any( - not isinstance(key, str) or not isinstance(value, str) - for key, value in configured.items() - ): - raise AdapterConfigError( - "codex_invalid_configuration", - "harness.settings.env must contain strings", - ) + configured = common_utils.environment_env(payload) values.update(configured) - if selected_model_provider(payload) == "nvidia": - codex_home = state_dir(payload) / "nvidia-home" + if ( + selected_model_provider(payload) == "openai" + and isinstance(api_key_env, str) + and api_key_env in values + ): + values["OPENAI_API_KEY"] = values[api_key_env] + if selected_model_provider(payload) != "openai": + codex_home = state_dir(payload) / "custom-provider-home" values["CODEX_HOME"] = str(codex_home) # The SDK overlays this mapping on the parent environment. An empty # originator is still treated as an override by Codex and produces invalid @@ -582,11 +565,7 @@ def prepare_codex_relay(payload: dict[str, Any]) -> CodexRelaySettings | None: if not common_utils.relay_enabled(payload): return None - command = _settings(payload).get("nemo_relay_command") or "nemo-relay" - if not isinstance(command, (str, Path)): - raise AdapterConfigError( - "codex_invalid_configuration", "nemo_relay_command must be a path" - ) + command = os.environ.get("FABRIC_TEST_NEMO_RELAY_COMMAND", "nemo-relay") try: executable = relay_gateway.resolve_relay_command( Path(common_utils.base_dir(payload)).resolve(), command @@ -600,9 +579,7 @@ def prepare_codex_relay(payload: dict[str, Any]) -> CodexRelaySettings | None: relay_contract = relay_gateway.relay_cli_contract(executable) plugin_config = common_utils.load_relay_plugin_config(payload) config_path, plugin_config_path = common_utils.write_relay_configs( - # The SDK owns Codex execution. Relay needs only gateway defaults and - # the sibling plugins.toml; configuring an agent command would retain - # a misleading dependency on the removed Codex CLI launch path. + # Codex execution remains SDK-owned; Relay runs only as a gateway. relay_config={}, plugin_config=plugin_config, observability_version=relay_contract.observability_version, @@ -618,6 +595,12 @@ def prepare_codex_relay(payload: dict[str, Any]) -> CodexRelaySettings | None: "NeMo Relay runtime configuration is unavailable", ) + base_url = common_utils.get_base_url(_selected_model_config(payload)) + if base_url is not None and (not isinstance(base_url, str) or not base_url): + raise AdapterConfigError( + "codex_invalid_configuration", + "selected model base_url must be a non-empty string", + ) port = relay_gateway.find_available_tcp_port() bind = f"127.0.0.1:{port}" return CodexRelaySettings( @@ -627,6 +610,7 @@ def prepare_codex_relay(payload: dict[str, Any]) -> CodexRelaySettings | None: bind=bind, url=f"http://{bind}", log_path=config_path.parent / "gateway.log", + openai_base_url=base_url.rstrip("/") if base_url else None, ), plugin_config=plugin_config, ) @@ -638,7 +622,8 @@ def thread_config( """Build request-scoped Codex config without writing a user profile.""" config = native_codex_telemetry_config(payload) - _merge_config(config, nvidia_model_provider_config(payload)) + _merge_config(config, custom_model_provider_config(payload)) + _merge_config(config, openai_model_provider_config(payload)) mcp_servers = _native_mcp_servers(payload) if mcp_servers: config["mcp_servers"] = mcp_servers @@ -648,13 +633,22 @@ def thread_config( ) _apply_config_overrides(config, overrides) if relay is not None: + provider = selected_model_provider(payload) + transport_config = ( + {"openai_base_url": relay.gateway.url} + if provider == "openai" + else { + "model_providers": { + provider: { + "base_url": relay.gateway.url, + } + } + } + ) _merge_config( config, { - # Keep the SDK-selected built-in provider so Codex retains its - # native API-key and ChatGPT authentication behavior. Relay is - # only the transport endpoint for this invocation. - "openai_base_url": relay.gateway.url, + **transport_config, "features": { "hooks": True, # Relay disables delegated multi-agent execution because @@ -677,8 +671,8 @@ def thread_config( def sdk_config( payload: dict[str, Any], relay: CodexRelaySettings | None ) -> CodexConfig: - codex_bin = _optional_string(_settings(payload), "codex_bin") - if codex_bin is not None: + codex_bin = os.environ.get("FABRIC_TEST_CODEX_BIN") + if codex_bin: path = Path(codex_bin) if not path.is_absolute(): path = (Path(common_utils.base_dir(payload)) / path).resolve() @@ -728,7 +722,6 @@ def validate_runtime_payload(payload: dict[str, Any]) -> str: """Validate runtime-owned configuration before starting SDK or Relay processes.""" settings = _settings(payload) - _validate_settings_boundary(settings) _native_skill_paths(payload) fabric_runtime_id = runtime_id(payload) resolve_cwd(payload) @@ -737,23 +730,13 @@ def validate_runtime_payload(payload: dict[str, Any]) -> str: approval_mode(payload) timeout_seconds(payload) for name in ( - "base_instructions", "developer_instructions", - "service_name", "service_tier", ): _optional_string(settings, name) _personality(payload) _reasoning_effort(payload) _output_schema(payload) - if ( - common_utils.relay_enabled(payload) - and selected_model_provider(payload) != "openai" - ): - raise AdapterConfigError( - "codex_invalid_configuration", - "NeMo Relay requires the built-in openai model provider", - ) child_environment(payload) thread_config(payload, None) return fabric_runtime_id @@ -893,7 +876,7 @@ def _thread_options( settings = _settings(payload) return { "approval_mode": approval_mode(payload), - "base_instructions": _optional_string(settings, "base_instructions"), + "base_instructions": common_utils.system_instruction(payload), "config": thread_config(payload, relay) or None, "cwd": str(resolve_cwd(payload)), "developer_instructions": _optional_string(settings, "developer_instructions"), @@ -911,12 +894,8 @@ async def _open_thread( *, relay: CodexRelaySettings | None, ) -> Any: - settings = _settings(payload) options = _thread_options(payload, relay) - return await codex.thread_start( - **options, - service_name=_optional_string(settings, "service_name"), - ) + return await codex.thread_start(**options) async def _invoke_thread( @@ -1029,7 +1008,7 @@ async def start(self, payload: dict[str, Any]) -> None: self._relay = relay self._gateway_process = _start_relay_gateway(payload, relay) client_config = sdk_config(payload, relay) - if selected_model_provider(payload) == "nvidia": + if selected_model_provider(payload) != "openai": await asyncio.to_thread( Path(client_config.env["CODEX_HOME"]).mkdir, parents=True, diff --git a/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py b/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py index 6eb3bb4b2..ae7529c30 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py +++ b/adapters/common/src/nemo_fabric_adapters/common/relay_gateway.py @@ -37,6 +37,8 @@ class RelayGatewayLaunch: bind: str url: str log_path: Path + openai_base_url: str | None = None + anthropic_base_url: str | None = None @dataclass(frozen=True) @@ -155,16 +157,21 @@ def start_relay_gateway( if not launch.config_path.is_file(): raise RelayGatewayError("NeMo Relay gateway configuration was not generated") launch.log_path.parent.mkdir(parents=True, exist_ok=True) + command = [ + str(launch.executable), + "--config", + str(launch.config_path), + "--bind", + launch.bind, + ] + if launch.openai_base_url is not None: + command.extend(["--openai-base-url", launch.openai_base_url]) + if launch.anthropic_base_url is not None: + command.extend(["--anthropic-base-url", launch.anthropic_base_url]) try: with launch.log_path.open("wb") as log_stream: process = subprocess.Popen( - [ - str(launch.executable), - "--config", - str(launch.config_path), - "--bind", - launch.bind, - ], + command, cwd=cwd, stdout=log_stream, stderr=subprocess.STDOUT, diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index 567aad0e7..f4ccd5531 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -90,7 +90,12 @@ def runtime_state_directory(base: str | Path, payload: dict[str, Any]) -> Path: def environment_payload(payload: dict[str, Any]) -> dict[str, Any]: - return runtime_context(payload).get("environment") or payload.get("environment") or {} + return ( + runtime_context(payload).get("environment") + or fabric_config(payload).get("environment") + or payload.get("environment") + or {} + ) def settings_payload(payload: dict[str, Any]) -> dict[str, Any]: @@ -102,31 +107,54 @@ def models_payload(payload: dict[str, Any]) -> dict[str, Any]: return fabric_config(payload).get("models") or payload.get("models") or {} -def default_base_url(provider: str | None) -> str | None: - if provider == "nvidia": - return "https://integrate.api.nvidia.com/v1" - return None - +def get_base_url(model_config: dict[str, Any]) -> str | None: + """Return the explicitly configured model endpoint.""" -def get_base_url(settings: dict[str, Any], model_config: dict[str, Any]) -> str | None: - return ( - settings.get("base_url") - or (model_config.get("settings") or {}).get("base_url") - or default_base_url(model_config.get("provider")) - ) + return model_config.get("base_url") def selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: - settings = settings_payload(payload) models = models_payload(payload) - model_config = models.get(settings.get("model", "default"), {}) + model_config = models.get("default") + if model_config is None and len(models) == 1: + model_config = next(iter(models.values())) if not isinstance(model_config, dict): return {} return model_config +def system_instruction(payload: dict[str, Any]) -> str | None: + instructions = fabric_config(payload).get("instructions") or {} + system = instructions.get("system") or {} + value = system.get("content") + return value if isinstance(value, str) else None + + +def max_turns(payload: dict[str, Any]) -> int | None: + value = (fabric_config(payload).get("runtime") or {}).get("max_turns") + return value if isinstance(value, int) and not isinstance(value, bool) else None + + +def timeout_seconds(payload: dict[str, Any], *, default: float) -> float: + value = (fabric_config(payload).get("runtime") or {}).get("timeout_seconds") + return float(default if value is None else value) + + +def environment_env(payload: dict[str, Any]) -> dict[str, str]: + value = environment_payload(payload).get("env") or {} + if not isinstance(value, dict): + return {} + return { + str(name): str(item) + for name, item in value.items() + if isinstance(name, str) and isinstance(item, str) + } + + def telemetry_payload(payload: dict[str, Any]) -> dict[str, Any]: - telemetry = fabric_config(payload).get("telemetry") or payload.get("telemetry") or {} + telemetry = ( + fabric_config(payload).get("telemetry") or payload.get("telemetry") or {} + ) return telemetry if isinstance(telemetry, dict) else {} @@ -160,6 +188,13 @@ def tools_config(payload: dict[str, Any]) -> dict[str, Any]: return tools if isinstance(tools, dict) else {} +def enabled_tools(payload: dict[str, Any]) -> list[str] | None: + tools = tools_config(payload) + if "enabled" not in tools: + return None + return normalize_list(tools.get("enabled")) + + def blocked_tools(payload: dict[str, Any]) -> list[str]: blocked = tools_config(payload).get("blocked") return normalize_list(blocked) @@ -222,7 +257,9 @@ def load_relay_plugin_config(payload: dict[str, Any]) -> dict[str, Any]: return plugin_config -def normalize_relay_output_dirs(plugin_config: dict[str, Any], payload: dict[str, Any]) -> None: +def normalize_relay_output_dirs( + plugin_config: dict[str, Any], payload: dict[str, Any] +) -> None: base = Path(base_dir(payload)).resolve() runtime_id = runtime_context(payload)["runtime_id"] for component in plugin_config.get("components", []): @@ -311,7 +348,9 @@ def write_relay_configs( config_path = os.environ.get("FABRIC_RELAY_CONFIG_PATH") if not config_path: - raise RuntimeError("FABRIC_RELAY_CONFIG_PATH is required when Relay is enabled") + raise RuntimeError( + "FABRIC_RELAY_CONFIG_PATH is required when Relay is enabled" + ) config_path = Path(config_path) config_dir = config_path.parent / "relay-config" @@ -339,9 +378,5 @@ def write_relay_configs( raise RuntimeError("tomli_w is not installed") from e - def relay_model_name(payload: dict[str, Any]) -> str: - settings = settings_payload(payload) - models = models_payload(payload) - model_config = models.get(settings.get("model", "default"), {}) - return settings.get("model_name") or model_config.get("model") or "unknown" + return selected_model_config(payload).get("model") or "unknown" diff --git a/adapters/deepagents/README.md b/adapters/deepagents/README.md index 3b105e020..5985a530c 100644 --- a/adapters/deepagents/README.md +++ b/adapters/deepagents/README.md @@ -23,22 +23,20 @@ pip install "nemo-fabric[deepagents, runtime]" ## Model and Authentication -The adapter builds a LangChain chat model from NeMo Fabric's `models.default` config. -For `nvidia` (or an unspecified provider) it targets NVIDIA-hosted, -OpenAI-compatible endpoints (`https://integrate.api.nvidia.com/v1`) via -`ChatOpenAI`; `openai` and `openai-compatible` also use `ChatOpenAI` with the -provider's own default endpoint. Any other provider is constructed through -`langchain.chat_models.init_chat_model`, so additional backends can be added -without changing the adapter. - -`models.default.api_key_env` names the environment variable holding the API key, -and defaults **per provider** — `NVIDIA_API_KEY` for `nvidia` (or an unspecified -provider) and `OPENAI_API_KEY` for `openai`. Every other provider — including -`openai-compatible` and any `init_chat_model` backend — must set `api_key_env` -explicitly (a missing one is a normalized configuration failure), so a key is -never sent to the wrong endpoint. - -Because `models.default.api_key_env` is provider-specific, the adapter declares no +The adapter builds a LangChain chat model from the selected NeMo Fabric model +role: `models.default`, or the sole configured role when `default` is absent. +The `openai`, `nvidia`, and `openai-compatible` providers use `ChatOpenAI`; +`nvidia` and `openai-compatible` require an explicit compatible `base_url`. +Any other provider is constructed through +`langchain.chat_models.init_chat_model`, so LangChain-supported backends do not +require adapter-specific branches. + +`models..api_key_env` names the environment variable holding the API key, +and defaults to `OPENAI_API_KEY` only for the native `openai` provider. Every +other provider must set `api_key_env` explicitly (a missing one is a normalized +configuration failure), so a key is never sent to the wrong endpoint. + +Because `models..api_key_env` is provider-specific, the adapter declares no static env requirement; a runtime **preflight** verifies that the `deepagents` package is importable and the configured credential is set. A failed preflight fails runtime start with a stable lifecycle error. `fabric doctor` validates @@ -46,11 +44,10 @@ adapter resolution. NeMo Fabric maps the following into the harness: -- `models.default.model` / `harness.settings.model_name` selects the model. -- `models.default.provider` selects the client (`nvidia`/`openai` → OpenAI-compatible). -- `models.default.temperature` / `harness.settings.temperature` sets sampling. -- `harness.settings.base_url` overrides the model endpoint. -- `harness.settings.system_prompt` becomes the Deep Agents `system_prompt`. +- The selected `models` role supplies `model`, `provider`, `api_key_env`, + `base_url`, and `temperature`. +- `instructions.system` becomes the Deep Agents `system_prompt`. +- `runtime.timeout_seconds` sets the NeMo Fabric invocation deadline. - `environment.workspace` roots the Deep Agents filesystem backend (`FilesystemBackend(root_dir=..., virtual_mode=True)`). `virtual_mode` confines the agent to the workspace: absolute paths and `..` cannot escape @@ -59,9 +56,9 @@ NeMo Fabric maps the following into the harness: - Configured MCP servers are loaded as Deep Agents tools via `langchain-mcp-adapters`. A misconfigured server (non-mapping, empty target, unsupported transport) is a normalized configuration failure, not a silent drop. -- `tools.blocked` is enforced by middleware across the full tool surface — Deep - Agents built-ins (including `task`), MCP tools, and **delegated subagents** - alike. Use Deep Agents/native tool names in the blocked list. +- `tools.enabled` and `tools.blocked` are enforced by middleware across the full + tool surface: Deep Agents built-ins (including `task`), MCP tools, and + **delegated subagents** alike. Use Deep Agents-native tool names. - `harness.settings.deepagents` forwards a small set of **documented, JSON-serializable** `create_deep_agent` options (currently `subagents` and `interrupt_on`). It is not a general Python-object escape hatch: the SDK config @@ -69,18 +66,18 @@ NeMo Fabric maps the following into the harness: instances, and Python callables cannot cross the boundary. NeMo Fabric-owned arguments (`model`, `tools`, `backend`, `skills`, `system_prompt`, `middleware`, `checkpointer`) cannot be overridden through this passthrough, and an unknown or - unsupported key is a normalized configuration failure rather than a silently + unsupported key is an adapter configuration failure rather than a silently dropped setting. ### Subagents Deep Agents can delegate to subagents through its built-in `task` tool. Subagents **inherit** the parent run's model, tools, skills, workspace, telemetry, and -permissions. When `tools.blocked` is configured, NeMo Fabric supplies an explicitly -gated `general-purpose` subagent and gates every declarative local subagent, so -delegation cannot broaden capabilities beyond the parent. Remote and precompiled -subagents are rejected in that case because their execution cannot be governed by -the local middleware. Independently configured subagent tools, skills, models, +permissions. When a normalized tools policy is configured, NeMo Fabric supplies +an explicitly gated `general-purpose` subagent and gates every declarative local +subagent, so delegation cannot broaden capabilities beyond the parent. Remote +and precompiled subagents are rejected in that case because their execution +cannot be governed by the local middleware. Independently configured subagent tools, skills, models, MCP servers, middleware, or permissions are **not** exposed through the NeMo Fabric SDK yet; a `subagents` definition here only carries JSON-shaped fields. @@ -98,9 +95,9 @@ NeMo Fabric starts one local adapter host for every runtime. During runtime star the host compiles one Deep Agents graph, opens its async LangGraph checkpointer, and creates one thread ID. Every invocation reuses those native objects; later turns report `resumed` as `true`. The checkpointer lives under -`harness.settings.state_dir` (default the runtime artifacts directory) and is -closed during runtime stop. The live host owns the thread identity, and -LangGraph owns the transcript. +the NeMo Fabric artifact root, scoped by runtime ID, and is closed during +runtime stop. The live host owns the thread identity, and LangGraph owns the +transcript. `Fabric.run(...)` is a convenience over that same lifecycle: it starts the runtime, invokes it once, and stops it. It does not use a separate adapter diff --git a/adapters/deepagents/fabric-adapter.json b/adapters/deepagents/fabric-adapter.json index c32aa03b2..8b1d7baff 100644 --- a/adapters/deepagents/fabric-adapter.json +++ b/adapters/deepagents/fabric-adapter.json @@ -8,7 +8,16 @@ }, "requirements": {}, "config": { - "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "models.temperature", + "instructions.system", + "tools.enabled", + "tools.blocked", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py index 038a48453..a05466442 100644 --- a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py +++ b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py @@ -28,17 +28,8 @@ import nemo_fabric_adapters.common.utils as common_utils HARNESS = "deepagents" -DEFAULT_NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1" # Providers we serve through the OpenAI-compatible ``ChatOpenAI`` client. -OPENAI_COMPATIBLE_PROVIDERS = {"", "nvidia", "openai", "openai-compatible"} -# Providers whose default endpoint is NVIDIA's OpenAI-compatible gateway. -NVIDIA_DEFAULT_PROVIDERS = {"", "nvidia"} -# Conventional credential env var per provider; others must set api_key_env. -PROVIDER_DEFAULT_API_KEY_ENV = { - "": "NVIDIA_API_KEY", - "nvidia": "NVIDIA_API_KEY", - "openai": "OPENAI_API_KEY", -} +OPENAI_COMPATIBLE_PROVIDERS = {"nvidia", "openai", "openai-compatible"} # MCP transports langchain-mcp-adapters accepts (after normalization). VALID_MCP_TRANSPORTS = {"stdio", "sse", "streamable_http", "websocket"} # create_deep_agent arguments Fabric derives from normalized config; the @@ -96,24 +87,26 @@ def wrap_tool_call(self, request: Any, handler: Any) -> Any: return handler(request) -def resolve_api_key_env(settings: dict[str, Any], model_config: dict[str, Any]) -> str: - """Resolve the credential env var, defaulting per provider. +def resolve_api_key_env(model_config: dict[str, Any]) -> str: + """Resolve the credential env var. - An explicit ``api_key_env`` always wins. Otherwise nvidia/unspecified default - to ``NVIDIA_API_KEY`` and openai to ``OPENAI_API_KEY``; any other provider must - set ``api_key_env`` explicitly so a key is never sent to the wrong endpoint. + OpenAI retains its conventional environment variable. Other providers must + name the credential explicitly so a key is never sent to the wrong endpoint. """ - explicit = settings.get("api_key_env") or model_config.get("api_key_env") - if explicit: - return str(explicit) - provider = (settings.get("provider") or model_config.get("provider") or "").lower() - default = PROVIDER_DEFAULT_API_KEY_ENV.get(provider) - if default is None: + explicit = model_config.get("api_key_env") + if isinstance(explicit, str) and explicit: + return explicit + if explicit is not None: raise AdapterConfigError( - f"models.default.api_key_env is required for provider '{provider}'." + "models.default.api_key_env must be a non-empty string." ) - return default + provider = str(model_config.get("provider") or "").lower() + if provider == "openai": + return "OPENAI_API_KEY" + raise AdapterConfigError( + f"models.default.api_key_env is required for provider '{provider}'." + ) def main() -> None: @@ -139,9 +132,8 @@ def preflight_check(payload: dict[str, Any]) -> None: "it with the 'deepagents' extra (pip install nemo-fabric-adapters-deepagents)." ) - settings = common_utils.settings_payload(payload) model_config = selected_model_config(payload) - api_key_env = resolve_api_key_env(settings, model_config) + api_key_env = resolve_api_key_env(model_config) if api_key_env not in os.environ: raise RuntimeError( f"the model-provider credential env var '{api_key_env}' is not set in the " @@ -151,56 +143,42 @@ def preflight_check(payload: dict[str, Any]) -> None: def selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: - settings = common_utils.settings_payload(payload) - models = common_utils.models_payload(payload) - return models.get(settings.get("model", "default"), {}) or {} - - -def resolve_base_url( - settings: dict[str, Any], model_config: dict[str, Any] -) -> str | None: - base_url = ( - settings.get("base_url") - or (model_config.get("settings") or {}).get("base_url") - or model_config.get("base_url") - ) - if base_url: - return base_url - provider = (settings.get("provider") or model_config.get("provider") or "").lower() - # Only NVIDIA (or an unspecified provider) defaults to NVIDIA's endpoint; a - # plain ``openai`` provider must fall through to ChatOpenAI's own default. - if provider in NVIDIA_DEFAULT_PROVIDERS: - return DEFAULT_NVIDIA_BASE_URL - return None + return common_utils.selected_model_config(payload) + + +def resolve_base_url(model_config: dict[str, Any]) -> str | None: + return common_utils.get_base_url(model_config) def build_chat_model(payload: dict[str, Any]) -> tuple[Any, str, str | None]: """Build a LangChain chat model from Fabric model config. - The default path targets NVIDIA-hosted OpenAI-compatible endpoints. A generic - hook falls back to ``langchain.chat_models.init_chat_model`` for any provider - that is not OpenAI-compatible, so other backends can be added without - reworking the adapter. + Known OpenAI-compatible providers use ``ChatOpenAI``. Other providers are + delegated to ``langchain.chat_models.init_chat_model``. """ - settings = common_utils.settings_payload(payload) model_config = selected_model_config(payload) - model_name = settings.get("model_name") or model_config.get("model") + model_name = model_config.get("model") if not model_name: raise RuntimeError( "models.default.model is required for the Deep Agents adapter" ) - api_key_env = resolve_api_key_env(settings, model_config) + api_key_env = resolve_api_key_env(model_config) api_key = os.environ.get(api_key_env) if not api_key: raise RuntimeError(f"{api_key_env} is required for the Deep Agents adapter") - provider = ( - settings.get("provider") or model_config.get("provider") or "nvidia" - ).lower() - base_url = resolve_base_url(settings, model_config) - temperature = settings.get("temperature", model_config.get("temperature")) + provider = str(model_config.get("provider") or "").lower() + if not provider: + raise AdapterConfigError("models.default.provider is required.") + base_url = resolve_base_url(model_config) + temperature = model_config.get("temperature") + + if provider in OPENAI_COMPATIBLE_PROVIDERS - {"openai"} and not base_url: + raise AdapterConfigError( + f"models.default.base_url is required for provider '{provider}'." + ) if provider not in OPENAI_COMPATIBLE_PROVIDERS: # Generic provider hook: honor an explicit non-OpenAI-compatible provider. @@ -231,9 +209,7 @@ def resolve_backend(payload: dict[str, Any]) -> Any: """Root the Deep Agents filesystem backend at the Fabric workspace, if set.""" environment = common_utils.environment_payload(payload) - workspace = environment.get("workspace") or common_utils.settings_payload( - payload - ).get("workspace") + workspace = environment.get("workspace") if not workspace: return None root = Path(str(workspace)) @@ -257,17 +233,22 @@ def _blocked_tool_names(payload: dict[str, Any]) -> set[str]: return set(common_utils.blocked_tools(payload)) +def _enabled_tool_names(payload: dict[str, Any]) -> set[str] | None: + enabled = common_utils.enabled_tools(payload) + return None if enabled is None else set(enabled) + + def _tool_gate_middleware( is_blocked: Callable[[Any], bool], message: Callable[[Any], str] ) -> ToolGateMiddleware: return ToolGateMiddleware(is_blocked, message) -def blocked_tools_middleware(blocked: set[str]) -> Any: - """Middleware that blocks explicitly denied tool calls across the full tool surface.""" +def tool_policy_middleware(enabled: set[str] | None, blocked: set[str]) -> Any: + """Enforce tool selection and blocking across the full tool surface.""" return _tool_gate_middleware( - lambda name: name in blocked, + lambda name: name in blocked or (enabled is not None and name not in enabled), lambda name: f"Tool '{name}' is blocked by the configured tools policy.", ) @@ -322,11 +303,6 @@ def _mcp_connection(name: str, spec: dict[str, Any]) -> dict[str, Any]: def state_dir(payload: dict[str, Any]) -> Path: base_dir = Path(common_utils.base_dir(payload)).resolve() - settings = common_utils.settings_payload(payload) - configured = settings.get("state_dir") - if configured: - path = Path(str(configured)) - return path if path.is_absolute() else base_dir / path artifacts = common_utils.runtime_context(payload).get("artifacts") or {} root = artifacts.get("root") or os.environ.get("FABRIC_ARTIFACTS") if root: @@ -373,7 +349,7 @@ async def build_agent_kwargs( "model": model, "tools": await resolve_tools(payload), # deepagents 0.5.x/0.6.x take the system prompt as ``system_prompt``. - "system_prompt": settings.get("system_prompt"), + "system_prompt": common_utils.system_instruction(payload), "skills": resolve_skills(payload), "backend": resolve_backend(payload), } @@ -382,12 +358,15 @@ async def build_agent_kwargs( extra = settings.get("deepagents") if extra is not None: kwargs.update(_validated_passthrough(extra)) + enabled = _enabled_tool_names(payload) blocked = _blocked_tool_names(payload) - if blocked: + if enabled is not None or blocked: middleware = list(kwargs.get("middleware") or []) - middleware.append(blocked_tools_middleware(blocked)) + middleware.append(tool_policy_middleware(enabled, blocked)) kwargs["middleware"] = middleware - kwargs["subagents"] = _gated_subagents(kwargs.get("subagents"), blocked) + kwargs["subagents"] = _gated_subagents( + kwargs.get("subagents"), enabled, blocked + ) return {key: value for key, value in kwargs.items() if value is not None} @@ -419,46 +398,52 @@ def _validated_passthrough(extra: Any) -> dict[str, Any]: return dict(extra) -def _block_subagent(subagent: dict[str, Any], blocked: set[str]) -> dict[str, Any]: +def _gate_subagent( + subagent: dict[str, Any], enabled: set[str] | None, blocked: set[str] +) -> dict[str, Any]: gated = dict(subagent) gated["middleware"] = [ *(gated.get("middleware") or []), - blocked_tools_middleware(blocked), + tool_policy_middleware(enabled, blocked), ] return gated -def _gated_subagents(subagents: Any, blocked: set[str]) -> list[dict[str, Any]]: +def _gated_subagents( + subagents: Any, enabled: set[str] | None, blocked: set[str] +) -> list[dict[str, Any]]: if subagents is None: configured: list[Any] = [] elif isinstance(subagents, list): configured = subagents else: raise AdapterConfigError( - "harness.settings.deepagents.subagents must be a list when tools.blocked is configured." + "harness.settings.deepagents.subagents must be a list when a tools policy is configured." ) gated: list[dict[str, Any]] = [] for subagent in configured: if not isinstance(subagent, dict): raise AdapterConfigError( - "Deep Agents subagents must be mappings when tools.blocked is configured." + "Deep Agents subagents must be mappings when a tools policy is configured." ) name = str(subagent.get("name") or "") if "graph_id" in subagent: raise AdapterConfigError( - f"tools.blocked cannot be enforced for remote Deep Agents subagent '{name}'." + f"the tools policy cannot be enforced for remote Deep Agents subagent '{name}'." ) if "runnable" in subagent: raise AdapterConfigError( - f"tools.blocked cannot be enforced for precompiled Deep Agents subagent '{name}'." + f"the tools policy cannot be enforced for precompiled Deep Agents subagent '{name}'." ) - gated.append(_block_subagent(subagent, blocked)) + gated.append(_gate_subagent(subagent, enabled, blocked)) if not any(subagent.get("name") == "general-purpose" for subagent in gated): from deepagents.middleware.subagents import GENERAL_PURPOSE_SUBAGENT - gated.insert(0, _block_subagent(dict(GENERAL_PURPOSE_SUBAGENT), blocked)) + gated.insert( + 0, _gate_subagent(dict(GENERAL_PURPOSE_SUBAGENT), enabled, blocked) + ) return gated diff --git a/adapters/hermes/README.md b/adapters/hermes/README.md index 9cc99f68a..1481e947d 100644 --- a/adapters/hermes/README.md +++ b/adapters/hermes/README.md @@ -31,17 +31,24 @@ pip install "nemo-fabric[hermes, hermes-agent]" The adapter receives a normalized payload from NeMo Fabric and materializes a native Hermes Agent configuration for: -- model provider, model name, base URL, temperature, and token settings; -- workspace and terminal settings; +- selected model provider, model name, base URL, and temperature through + `models`; +- `instructions.system` and `runtime.max_turns`; +- workspace and explicit environment variables through `environment`; +- invocation timeout through `runtime.timeout_seconds`; - NeMo Fabric skills as external skill directories for Hermes Agent; - NeMo Fabric MCP servers as Hermes Agent MCP server config; -- `tools.blocked` as disabled toolsets for Hermes Agent, unioned with - `harness.settings.disabled_toolsets`; +- `tools.enabled` and `tools.blocked` as Hermes-native toolset selection and + blocking policy; - optional NeMo Relay telemetry plugin configuration. -`hermes_home` configures a base directory. The adapter creates a child under -`runtimes/` so invocations in one NeMo Fabric runtime share Hermes Agent state -without sharing config or the session database with another runtime. +Tool selectors are Hermes toolset names because that is the native policy +surface Hermes exposes. Keep Hermes-specific controls such as +terminal timeout, reasoning configuration, and plugin configuration in +`harness.settings`. The adapter derives Hermes state from the NeMo Fabric +artifact root and creates a child under `runtimes/`, so invocations +in one NeMo Fabric runtime share state without sharing config or the session +database with another runtime. ## Execution Model diff --git a/adapters/hermes/fabric-adapter.json b/adapters/hermes/fabric-adapter.json index ef60fb920..c77f3836d 100644 --- a/adapters/hermes/fabric-adapter.json +++ b/adapters/hermes/fabric-adapter.json @@ -6,19 +6,18 @@ "runner": { "module": "nemo_fabric_adapters.hermes.adapter" }, - "requirements": { - "env": [ - "NVIDIA_API_KEY" - ] - }, + "requirements": {}, "config": { "accepts": [ "models", - "tools", + "models.base_url", + "models.temperature", + "instructions.system", + "runtime.max_turns", + "tools.enabled", "tools.blocked", "mcp", - "skills", - "telemetry" + "skills" ] }, "telemetry": { diff --git a/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py b/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py index ced7c25e7..fe9f6b2ca 100755 --- a/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py +++ b/adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py @@ -23,12 +23,31 @@ from nemo_fabric_adapters.common import lifecycle import nemo_fabric_adapters.common.utils as common_utils -# Default agent loop budget when harness.settings.max_iterations is unset. +# Default agent loop budget when FabricConfig.runtime.max_turns is unset. # Mirrors Hermes' own AIAgent default (agent/agent_init.py); a lower value such # as 1 silently starves multi-step tasks (they run out of budget before # answering while the trial still reports success). See FABRIC-85. DEFAULT_MAX_ITERATIONS: int = 90 LOGGER = logging.getLogger(__name__) +PROVIDER_DEFAULT_API_KEY_ENV = { + "anthropic": "ANTHROPIC_API_KEY", + "nvidia": "NVIDIA_API_KEY", + "openai": "OPENAI_API_KEY", + "openrouter": "OPENROUTER_API_KEY", +} + + +def _api_key_env(model_config: dict[str, Any]) -> str: + explicit = model_config.get("api_key_env") + if isinstance(explicit, str) and explicit: + return explicit + provider = str(model_config.get("provider") or "").lower() + default = PROVIDER_DEFAULT_API_KEY_ENV.get(provider) + if default is None: + raise ValueError( + f"selected model api_key_env is required for provider {provider!r}" + ) + return default def _fabric_stream_sink_enabled(config: dict[str, Any] | None) -> bool: @@ -58,11 +77,7 @@ def validate_hermes_telemetry_provider(payload: dict[str, Any]) -> None: def disabled_toolsets(payload: dict[str, Any]) -> list[str]: - settings = common_utils.settings_payload(payload) - return common_utils.merge_unique( - common_utils.blocked_tools(payload), - settings.get("disabled_toolsets"), - ) + return common_utils.blocked_tools(payload) def build_hermes_config( @@ -73,10 +88,11 @@ def build_hermes_config( native = common_utils.capability_plan(payload).get("native") or {} environment = common_utils.environment_payload(payload) - model_name = settings.get("model_name") or model_config.get("model", "") - provider = settings.get("provider") or model_config.get("provider") - base_url = common_utils.get_base_url(settings, model_config) + model_name = model_config.get("model", "") + provider = model_config.get("provider") + base_url = common_utils.get_base_url(model_config) blocked_toolsets = disabled_toolsets(payload) + enabled_toolsets = common_utils.enabled_tools(payload) config: dict[str, Any] = { "model": common_utils.without_none( @@ -88,16 +104,14 @@ def build_hermes_config( ), "agent": common_utils.without_none( { - "max_turns": settings.get("max_iterations"), + "max_turns": common_utils.max_turns(payload), "disabled_toolsets": blocked_toolsets or None, } ), "terminal": common_utils.without_none( { - "backend": settings.get("terminal_backend", "local"), - "cwd": str( - environment.get("workspace") or settings.get("workspace") or "." - ), + "backend": "local", + "cwd": str(environment.get("workspace") or "."), "timeout": settings.get("terminal_timeout", 60), } ), @@ -114,12 +128,8 @@ def build_hermes_config( for name, server in sorted(mcp_servers.items()) } - if "enabled_toolsets" in settings: - config["platform_toolsets"] = { - settings.get("toolset_platform", "cli"): common_utils.normalize_list( - settings.get("enabled_toolsets") - ) - } + if enabled_toolsets is not None: + config["platform_toolsets"] = {"cli": enabled_toolsets} plugins = common_utils.normalize_list(settings.get("plugins_enabled")) if relay_enabled and "observability/nemo_relay" not in plugins: @@ -182,15 +192,26 @@ def main() -> None: def resolve_hermes_toolsets( - settings: dict[str, Any], config: dict[str, Any] + payload: dict[str, Any], config: dict[str, Any] ) -> list[str] | None: - if "enabled_toolsets" in settings: - return common_utils.normalize_list(settings.get("enabled_toolsets")) + enabled = common_utils.enabled_tools(payload) + if enabled is not None: + return enabled from hermes_cli.tools_config import _get_platform_tools - platform = settings.get("toolset_platform", "cli") - return sorted(_get_platform_tools(config, platform)) + return sorted(_get_platform_tools(config, "cli")) + + +def _artifact_root(payload: dict[str, Any]) -> Path: + artifacts = common_utils.runtime_context(payload).get("artifacts") or {} + root = artifacts.get("root") if isinstance(artifacts, dict) else None + if root: + artifact_root = Path(str(root)) + if not artifact_root.is_absolute(): + artifact_root = Path(common_utils.base_dir(payload)) / artifact_root + return artifact_root.resolve() + return Path(common_utils.base_dir(payload)).resolve() / "artifacts" class HermesRuntime: @@ -232,11 +253,8 @@ async def start(self, payload: dict[str, Any]) -> None: self._settings = common_utils.settings_payload(payload) self._model_config = common_utils.selected_model_config(payload) self._runtime_id = common_utils.runtime_id(payload) - hermes_home_base = Path(common_utils.base_dir(payload)).joinpath( - self._settings.get("hermes_home", "./artifacts/hermes-home") - ) self._hermes_home = common_utils.runtime_state_directory( - hermes_home_base, payload + _artifact_root(payload) / ".fabric" / "hermes", payload ) self._hermes_home.mkdir(parents=True, exist_ok=True) os.environ["HOME"] = str(self._hermes_home) @@ -244,10 +262,7 @@ async def start(self, payload: dict[str, Any]) -> None: os.environ.setdefault("HERMES_YOLO_MODE", "1") os.environ.setdefault("HERMES_ACCEPT_HOOKS", "1") os.environ["HERMES_SESSION_SOURCE"] = "fabric" - os.environ.setdefault( - "TERMINAL_ENV", - self._settings.get("terminal_backend", "local"), - ) + os.environ["TERMINAL_ENV"] = "local" os.environ.setdefault( "TERMINAL_TIMEOUT", str(self._settings.get("terminal_timeout", 60)), @@ -269,17 +284,11 @@ async def start(self, payload: dict[str, Any]) -> None: self._hermes_home, relay_enabled=relay_enabled, ) - api_key_env = ( - self._settings.get("api_key_env") - or self._model_config.get("api_key_env") - or "NVIDIA_API_KEY" - ) + api_key_env = _api_key_env(self._model_config) api_key = os.environ.get(api_key_env) if not api_key: raise RuntimeError(f"{api_key_env} is required for Hermes mode") - self._base_url = common_utils.get_base_url( - self._settings, self._model_config - ) + self._base_url = common_utils.get_base_url(self._model_config) self._relay_model_name = common_utils.relay_model_name(payload) from hermes_cli.config import load_config @@ -292,22 +301,21 @@ async def start(self, payload: dict[str, Any]) -> None: discover_plugins(force=True) loaded_hermes_config = load_config() self._enabled_toolsets = resolve_hermes_toolsets( - self._settings, loaded_hermes_config + payload, loaded_hermes_config ) self._session_db = SessionDB() self._conversation_history = None - max_iterations = self._settings.get("max_iterations") + max_iterations = common_utils.max_turns(payload) if max_iterations is None: max_iterations = DEFAULT_MAX_ITERATIONS + temperature = self._model_config.get("temperature") self._agent = AIAgent( **filter_supported_kwargs( AIAgent, base_url=self._base_url, api_key=api_key, - provider=self._settings.get("provider") - or self._model_config.get("provider"), - model=self._settings.get("model_name") - or self._model_config.get("model", ""), + provider=self._model_config.get("provider"), + model=self._model_config.get("model", ""), max_iterations=int(max_iterations), enabled_toolsets=self._enabled_toolsets, disabled_toolsets=disabled_toolsets(payload) or None, @@ -318,16 +326,14 @@ async def start(self, payload: dict[str, Any]) -> None: self._settings.get("save_trajectories", False) ), max_tokens=self._settings.get("max_tokens", 512), - temperature=self._settings.get( - "temperature", - self._model_config.get("temperature", 0.0), + request_overrides=( + {"temperature": temperature} + if temperature is not None + else None ), reasoning_config=self._settings.get( "reasoning_config", {"effort": "none"} ), - insert_reasoning=bool( - self._settings.get("insert_reasoning", False) - ), platform="fabric", session_id=self._runtime_id, session_db=self._session_db, @@ -366,7 +372,7 @@ async def invoke(self, invocation: dict[str, Any]) -> dict[str, Any]: def invoke_turn() -> tuple[dict[str, Any], str]: return _invoke_hermes_turn( agent=self._agent, - settings=self._settings, + system_prompt=common_utils.system_instruction(start_payload), user_message=user_message, conversation_history=self._conversation_history, ) @@ -522,7 +528,7 @@ async def stop(self) -> None: def _invoke_hermes_turn( *, agent: Any, - settings: dict[str, Any], + system_prompt: str | None, user_message: str, conversation_history: list[dict[str, Any]] | None, ) -> tuple[dict[str, Any], str]: @@ -530,7 +536,7 @@ def _invoke_hermes_turn( with redirect_stdout(hermes_stdout): conversation_kwargs = filter_supported_call_kwargs( agent.run_conversation, - system_message=settings.get("system_prompt"), + system_message=system_prompt, conversation_history=conversation_history, sync_honcho=False, dont_review=True, diff --git a/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json b/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json index 9f8996f94..52b11a520 100644 --- a/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json +++ b/crates/fabric-cli/assets/adapters/claude/fabric-adapter.json @@ -7,7 +7,16 @@ "module": "nemo_fabric_adapters.claude.adapter" }, "config": { - "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "instructions.system", + "runtime.max_turns", + "tools.enabled", + "tools.blocked", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json b/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json index 251ab043f..a31540298 100644 --- a/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json +++ b/crates/fabric-cli/assets/adapters/codex/fabric-adapter.json @@ -7,7 +7,13 @@ "module": "nemo_fabric_adapters.codex.adapter" }, "config": { - "accepts": ["models", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "instructions.system", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json b/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json index c32aa03b2..8b1d7baff 100644 --- a/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json +++ b/crates/fabric-cli/assets/adapters/deepagents/fabric-adapter.json @@ -8,7 +8,16 @@ }, "requirements": {}, "config": { - "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "models.temperature", + "instructions.system", + "tools.enabled", + "tools.blocked", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json b/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json index ef60fb920..c77f3836d 100644 --- a/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json +++ b/crates/fabric-cli/assets/adapters/hermes/fabric-adapter.json @@ -6,19 +6,18 @@ "runner": { "module": "nemo_fabric_adapters.hermes.adapter" }, - "requirements": { - "env": [ - "NVIDIA_API_KEY" - ] - }, + "requirements": {}, "config": { "accepts": [ "models", - "tools", + "models.base_url", + "models.temperature", + "instructions.system", + "runtime.max_turns", + "tools.enabled", "tools.blocked", "mcp", - "skills", - "telemetry" + "skills" ] }, "telemetry": { diff --git a/crates/fabric-cli/src/app.rs b/crates/fabric-cli/src/app.rs index dfe547ec3..032df62eb 100644 --- a/crates/fabric-cli/src/app.rs +++ b/crates/fabric-cli/src/app.rs @@ -6,7 +6,8 @@ use clap::error::ErrorKind; use clap::{ArgGroup, Args, Parser, Subcommand}; use nemo_fabric_core::{ - ResolveContext, RunRequest, RunStatus, doctor_plan, resolve_run_plan_from_config, run_plan, + ResolveContext, RunRequest, RunStatus, doctor_plan, resolve_diagnostic_plan_from_config, + resolve_run_plan_from_config, run_plan, }; use crate::examples; @@ -47,7 +48,7 @@ enum Command { #[arg(long, default_value = "")] input: String, }, - /// Print the NeMo Fabric core version. + /// Print the NVIDIA NeMo Fabric core version. Version, } @@ -238,7 +239,7 @@ fn run(cli: Cli) -> Result<(), Box> { } Command::Doctor(selector) => { let selected = select_source(&selector)?; - let plan = resolve_run_plan_from_config( + let plan = resolve_diagnostic_plan_from_config( selected.config().clone(), ResolveContext::new(selected.base_dir()), )?; diff --git a/crates/fabric-cli/src/examples.rs b/crates/fabric-cli/src/examples.rs index d0a716f36..e7a2c812a 100644 --- a/crates/fabric-cli/src/examples.rs +++ b/crates/fabric-cli/src/examples.rs @@ -120,7 +120,7 @@ pub fn find(name: &str) -> Option { const EXAMPLES: [Example; 1] = [Example { name: "code-review", - description: "Review a small Python workspace using a maintained skill.", + description: "Review a small Python workspace with a deterministic default or a skill-capable harness.", default_variant: "scripted", variants: CODE_REVIEW_VARIANTS, }]; @@ -137,10 +137,12 @@ fn code_review_config(preset: Preset) -> Result { .as_mut() .expect("CLI presets always define an execution environment"); environment.workspace = Some(PathBuf::from("repo")); - config.skills = Some(SkillConfig { - paths: vec![PathBuf::from("skills/code-review.md")], - extensions: BTreeMap::new(), - }); + if preset.name != "scripted" { + config.skills = Some(SkillConfig { + paths: vec![PathBuf::from("skills/code-review.md")], + extensions: BTreeMap::new(), + }); + } Ok(config) } @@ -166,6 +168,20 @@ mod tests { .expect("select example"); assert!(selected.base_dir().join("repo/calculator.py").is_file()); assert!(selected.base_dir().join("skills/code-review.md").is_file()); + assert!( + selected.config.skills.is_none(), + "the scripted smoke variant does not declare skill support" + ); + assert!( + find("code-review") + .expect("code review example") + .select(Some("hermes")) + .expect("select Hermes example") + .config + .skills + .is_some(), + "maintained harness variants consume the staged skill" + ); let plan = resolve_run_plan_from_config( selected.config.clone(), ResolveContext::new(selected.base_dir()), diff --git a/crates/fabric-cli/src/presets.rs b/crates/fabric-cli/src/presets.rs index 43680ec49..c8bf3cd3d 100644 --- a/crates/fabric-cli/src/presets.rs +++ b/crates/fabric-cli/src/presets.rs @@ -271,10 +271,13 @@ fn config( models: default_model .map(|model| BTreeMap::from_iter([("default".to_string(), model)])) .unwrap_or_default(), + instructions: None, runtime: RuntimeConfig { input_schema: "text".to_string(), output_schema: "message".to_string(), artifacts: None, + timeout_seconds: None, + max_turns: None, extensions: BTreeMap::new(), }, environment: Some(EnvironmentConfig { @@ -283,6 +286,7 @@ fn config( ownership: EnvironmentOwnership::FabricOwned, workspace: None, artifacts: None, + env: BTreeMap::new(), connection: Map::new(), metadata: Map::new(), settings: Map::new(), @@ -308,9 +312,8 @@ fn model( model: name.to_string(), temperature: None, api_key_env: api_key_env.map(str::to_string), - settings: base_url - .map(|value| Map::from_iter([("base_url".to_string(), json!(value))])) - .unwrap_or_default(), + base_url: base_url.map(str::to_string), + settings: Map::new(), extensions: BTreeMap::new(), } } @@ -372,10 +375,7 @@ mod tests { .config() .expect("construct catalog config"); let model = config.models.get("default").expect("default model"); - assert_eq!( - model.settings.get("base_url").and_then(Value::as_str), - Some(NVIDIA_API_CATALOG_BASE_URL) - ); + assert_eq!(model.base_url.as_deref(), Some(NVIDIA_API_CATALOG_BASE_URL)); } for name in ["claude", "codex"] { diff --git a/crates/fabric-cli/src/scaffold.rs b/crates/fabric-cli/src/scaffold.rs index 895946a8f..7973f3d3b 100644 --- a/crates/fabric-cli/src/scaffold.rs +++ b/crates/fabric-cli/src/scaffold.rs @@ -164,6 +164,52 @@ fn render_python(config: &FabricConfig) -> String { "{{HARNESS_SETTINGS}}", &python_value(&Value::Object(config.harness.settings.clone())), ) + .replace( + "{{INSTRUCTIONS}}", + &config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + .map(|instruction| { + format!( + "InstructionsConfig(system=InstructionConfig(content={}, mode=\"replace\"))", + python_string(&instruction.content) + ) + }) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{MAX_TURNS}}", + &config + .runtime + .max_turns + .map(|value| value.to_string()) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{TIMEOUT_SECONDS}}", + &config + .runtime + .timeout_seconds + .map(|value| value.to_string()) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{ENVIRONMENT_ENV}}", + &python_value(&Value::Object( + config + .environment + .as_ref() + .map(|environment| { + environment + .env + .iter() + .map(|(name, value)| (name.clone(), Value::String(value.clone()))) + .collect() + }) + .unwrap_or_default(), + )), + ) .replace("{{MODELS}}", &python_models(config.models.get("default"))) } @@ -175,8 +221,13 @@ fn python_models(model: Option<&ModelConfig>) -> String { .temperature .map(|value| value.to_string()) .unwrap_or_else(|| "None".to_string()); + let base_url = model + .base_url + .as_deref() + .map(python_string) + .unwrap_or_else(|| "None".to_string()); format!( - "{{\"default\": ModelConfig(provider={}, model={}, temperature={temperature}, api_key_env={}, settings={})}}", + "{{\"default\": ModelConfig(provider={}, model={}, temperature={temperature}, api_key_env={}, base_url={base_url}, settings={})}}", python_string(&model.provider), python_string(&model.model), model @@ -229,9 +280,66 @@ fn render_rust(config: &FabricConfig) -> String { "{{HARNESS_SETTINGS}}", &rust_settings(&config.harness.settings), ) + .replace( + "{{INSTRUCTIONS}}", + &config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + .map(|instruction| { + format!( + "Some(nemo_fabric_core::InstructionsConfig {{ system: Some(nemo_fabric_core::InstructionConfig {{ content: {}.to_string(), mode: nemo_fabric_core::InstructionMode::Replace, extensions: BTreeMap::new() }}), extensions: BTreeMap::new() }})", + rust_string(&instruction.content) + ) + }) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{MAX_TURNS}}", + &config + .runtime + .max_turns + .map(|value| format!("Some({value})")) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{TIMEOUT_SECONDS}}", + &config + .runtime + .timeout_seconds + .map(|value| format!("Some({value})")) + .unwrap_or_else(|| "None".to_string()), + ) + .replace( + "{{ENVIRONMENT_ENV}}", + &rust_string_map( + config + .environment + .as_ref() + .map(|environment| &environment.env), + ), + ) .replace("{{MODELS}}", &rust_models(config.models.get("default"))) } +fn rust_string_map(values: Option<&std::collections::BTreeMap>) -> String { + let Some(values) = values.filter(|values| !values.is_empty()) else { + return "BTreeMap::new()".to_string(); + }; + format!( + "BTreeMap::from_iter([{}])", + values + .iter() + .map(|(key, value)| format!( + "({}.to_string(), {}.to_string())", + rust_string(key), + rust_string(value) + )) + .collect::>() + .join(", ") + ) +} + fn rust_settings(settings: &serde_json::Map) -> String { if settings.is_empty() { return "Map::new()".to_string(); @@ -265,8 +373,13 @@ fn rust_models(model: Option<&ModelConfig>) -> String { .temperature .map(|value| format!("Some({value})")) .unwrap_or_else(|| "None".to_string()); + let base_url = model + .base_url + .as_deref() + .map(|value| format!("Some({}.to_string())", rust_string(value))) + .unwrap_or_else(|| "None".to_string()); format!( - "BTreeMap::from_iter([(\"default\".to_string(), nemo_fabric_core::ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: {temperature}, api_key_env: {api_key}, settings: {}, extensions: BTreeMap::new() }})])", + "BTreeMap::from_iter([(\"default\".to_string(), nemo_fabric_core::ModelConfig {{ provider: {}.to_string(), model: {}.to_string(), temperature: {temperature}, api_key_env: {api_key}, base_url: {base_url}, settings: {}, extensions: BTreeMap::new() }})])", rust_string(&model.provider), rust_string(&model.model), rust_settings(&model.settings), @@ -405,15 +518,13 @@ mod tests { let python = render_python(&config); assert!(python.contains("temperature=0.2")); - assert!( - python.contains("settings={\"base_url\": \"https://integrate.api.nvidia.com/v1\"}") - ); + assert!(python.contains("base_url=\"https://integrate.api.nvidia.com/v1\"")); let rust = render_rust(&config); assert!(rust.contains("temperature: Some(0.2)")); - assert!(rust.contains( - "(\"base_url\".to_string(), serde_json::json!(\"https://integrate.api.nvidia.com/v1\"))" - )); + assert!( + rust.contains("base_url: Some(\"https://integrate.api.nvidia.com/v1\".to_string())") + ); } #[test] diff --git a/crates/fabric-cli/templates/python/main.py.tmpl b/crates/fabric-cli/templates/python/main.py.tmpl index 76c039d1b..67ce7df90 100644 --- a/crates/fabric-cli/templates/python/main.py.tmpl +++ b/crates/fabric-cli/templates/python/main.py.tmpl @@ -12,6 +12,8 @@ from nemo_fabric import EnvironmentConfig from nemo_fabric import Fabric from nemo_fabric import FabricConfig from nemo_fabric import HarnessConfig +from nemo_fabric import InstructionConfig +from nemo_fabric import InstructionsConfig from nemo_fabric import MetadataConfig from nemo_fabric import ModelConfig from nemo_fabric import RuntimeConfig @@ -30,8 +32,18 @@ def build_config() -> FabricConfig: settings={{HARNESS_SETTINGS}}, ), models={{MODELS}}, - runtime=RuntimeConfig(input_schema="text", output_schema="message"), - environment=EnvironmentConfig(provider="local", workspace="repo"), + instructions={{INSTRUCTIONS}}, + runtime=RuntimeConfig( + input_schema="text", + output_schema="message", + timeout_seconds={{TIMEOUT_SECONDS}}, + max_turns={{MAX_TURNS}}, + ), + environment=EnvironmentConfig( + provider="local", + workspace="repo", + env={{ENVIRONMENT_ENV}}, + ), ) config.add_skill_path("skills/code-review.md") return config diff --git a/crates/fabric-cli/templates/rust/main.rs.tmpl b/crates/fabric-cli/templates/rust/main.rs.tmpl index edbffadb4..930c5003f 100644 --- a/crates/fabric-cli/templates/rust/main.rs.tmpl +++ b/crates/fabric-cli/templates/rust/main.rs.tmpl @@ -26,10 +26,13 @@ fn build_config() -> FabricConfig { extensions: BTreeMap::new(), }, models: {{MODELS}}, + instructions: {{INSTRUCTIONS}}, runtime: RuntimeConfig { input_schema: "text".to_string(), output_schema: "message".to_string(), artifacts: None, + timeout_seconds: {{TIMEOUT_SECONDS}}, + max_turns: {{MAX_TURNS}}, extensions: BTreeMap::new(), }, environment: Some(EnvironmentConfig { @@ -38,6 +41,7 @@ fn build_config() -> FabricConfig { ownership: EnvironmentOwnership::FabricOwned, workspace: Some(PathBuf::from("repo")), artifacts: None, + env: {{ENVIRONMENT_ENV}}, connection: Map::new(), metadata: Map::new(), settings: Map::new(), diff --git a/crates/fabric-cli/tests/frontier_presets.rs b/crates/fabric-cli/tests/frontier_presets.rs index 6790d725f..5c08e282e 100644 --- a/crates/fabric-cli/tests/frontier_presets.rs +++ b/crates/fabric-cli/tests/frontier_presets.rs @@ -37,7 +37,7 @@ fn frontier_presets_require_an_explicit_endpoint() { let plan: serde_json::Value = serde_json::from_slice(&configured.stdout).expect("parse CLI plan"); assert_eq!( - plan["config"]["models"]["default"]["settings"]["base_url"].as_str(), + plan["config"]["models"]["default"]["base_url"].as_str(), Some("https://frontier.example/v1"), "{preset} did not preserve the Frontier endpoint" ); diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 55ce741f1..7a9a6ca1d 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -7,7 +7,7 @@ use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; -use schemars::JsonSchema; +use schemars::{JsonSchema, Schema, SchemaGenerator}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -16,7 +16,11 @@ use crate::error::{FabricError, Result}; /// Adapter descriptor contract version supported by this core. pub const ADAPTER_CONTRACT_VERSION: &str = "fabric.adapter/v1alpha1"; -/// Versioned NeMo Fabric agent config. +/// Versioned NVIDIA NeMo Fabric agent config. +/// +/// NeMo Fabric-owned fields apply uniformly, while adapter-translated fields are +/// validated against the selected adapter descriptor. See the +/// [configuration compatibility matrix](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/sdk/python.mdx#normalized-configuration-compatibility). #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct FabricConfig { /// Config schema version. @@ -25,10 +29,13 @@ pub struct FabricConfig { pub metadata: MetadataConfig, /// Harness selection and harness-specific settings. pub harness: HarnessConfig, - /// Model aliases. + /// Named model roles. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] pub models: BTreeMap, - /// Runtime input/output contract. + /// Portable agent instructions for the selected harness. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub instructions: Option, + /// Invocation runtime contract. pub runtime: RuntimeConfig, /// Environment where the harness or its tools execute. #[serde(default, skip_serializing_if = "Option::is_none")] @@ -53,10 +60,47 @@ pub struct FabricConfig { pub extensions: BTreeMap, } +/// How an instruction value is applied to the selected harness. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum InstructionMode { + /// Replace the harness default instruction value. + #[default] + Replace, +} + +/// One portable instruction value. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct InstructionConfig { + /// Instruction text. + #[schemars(length(min = 1), regex(pattern = r"\S"))] + pub content: String, + /// How the instruction is applied. + #[serde(default)] + pub mode: InstructionMode, + /// Additive instruction fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// Harness-neutral agent instruction configuration. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct InstructionsConfig { + /// System instructions for the selected harness. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub system: Option, + /// Additive instruction categories. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + /// Harness-neutral tool capability configuration. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct ToolsConfig { - /// Adapter-native tool names or toolset names to block. + /// Adapter-native tool names to expose. `None` preserves the harness default. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option>, + /// Adapter-native tool names to block. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub blocked: Vec, /// Additive tool configuration fields. @@ -328,9 +372,9 @@ pub struct AdapterRequirements { /// Adapter config support. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterConfigSupport { - /// NeMo Fabric config areas or policy paths accepted by this adapter. + /// Normalized NVIDIA NeMo Fabric config areas or policy paths accepted by this adapter. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub accepts: Vec, + pub accepts: Vec, /// Harness-native files generated by this adapter. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub generates: Vec, @@ -339,6 +383,40 @@ pub struct AdapterConfigSupport { pub extensions: BTreeMap, } +/// Adapter-translated normalized NVIDIA NeMo Fabric configuration fields. +#[derive( + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema, +)] +pub enum AdapterConfigField { + /// Normalized model selection and credentials. + #[serde(rename = "models")] + Models, + /// Custom model endpoint. + #[serde(rename = "models.base_url")] + ModelBaseUrl, + /// Model temperature. + #[serde(rename = "models.temperature")] + ModelTemperature, + /// Portable system instructions. + #[serde(rename = "instructions.system")] + SystemInstructions, + /// Per-invocation harness turn limit. + #[serde(rename = "runtime.max_turns")] + MaxTurns, + /// Adapter-native tool names to expose. + #[serde(rename = "tools.enabled")] + EnabledTools, + /// Adapter-native tool names to block. + #[serde(rename = "tools.blocked")] + BlockedTools, + /// Harness-native MCP servers. + #[serde(rename = "mcp")] + Mcp, + /// Harness-native skills. + #[serde(rename = "skills")] + Skills, +} + /// Adapter telemetry support. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterTelemetrySupport { @@ -407,6 +485,9 @@ pub struct ModelConfig { /// Optional environment variable containing an API key. #[serde(default, skip_serializing_if = "Option::is_none")] pub api_key_env: Option, + /// Optional provider endpoint URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub base_url: Option, /// Provider-specific settings. #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")] pub settings: serde_json::Map, @@ -415,7 +496,7 @@ pub struct ModelConfig { pub extensions: BTreeMap, } -/// Runtime input/output contract. +/// Invocation runtime contract. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct RuntimeConfig { /// Input schema label. @@ -427,6 +508,14 @@ pub struct RuntimeConfig { /// Artifact directory. #[serde(default, skip_serializing_if = "Option::is_none")] pub artifacts: Option, + /// Maximum duration of one invocation in seconds. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[schemars(extend("exclusiveMinimum" = 0.0))] + pub timeout_seconds: Option, + /// Maximum number of harness turns within one invocation. + #[serde(default, skip_serializing_if = "Option::is_none")] + #[schemars(range(min = 1, max = u32::MAX))] + pub max_turns: Option, /// Additive normalized runtime fields. #[serde(default, flatten)] pub extensions: BTreeMap, @@ -457,6 +546,14 @@ pub struct EnvironmentConfig { /// Artifact path inside or outside the provider. #[serde(default, skip_serializing_if = "Option::is_none")] pub artifacts: Option, + /// Environment variables visible to the harness and its tools. + /// + /// Values are serialized into the run plan and can appear wherever configs + /// or plans are logged or persisted. Prefer `api_key_env`-style + /// environment-variable-name indirection for credentials. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + #[schemars(schema_with = "environment_variables_schema")] + pub env: BTreeMap, /// Provider connection metadata, such as server URL, credential reference, or namespace. #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")] pub connection: serde_json::Map, @@ -471,6 +568,15 @@ pub struct EnvironmentConfig { pub extensions: BTreeMap, } +fn environment_variables_schema(generator: &mut SchemaGenerator) -> Schema { + let mut schema = BTreeMap::::json_schema(generator); + schema.insert( + "propertyNames".into(), + serde_json::json!({"pattern": r"\S"}), + ); + schema +} + fn default_control_location() -> ControlLocation { ControlLocation::InEnvControl } @@ -1002,10 +1108,96 @@ fn validate_config(config: &FabricConfig) -> Result<()> { available: Vec::new(), }); } + if config.runtime.max_turns == Some(0) { + return invalid_config("runtime.max_turns", "must be greater than zero"); + } + if let Some(timeout) = config.runtime.timeout_seconds + && (!timeout.is_finite() + || timeout <= 0.0 + || std::time::Duration::try_from_secs_f64(timeout).is_err()) + { + return invalid_config( + "runtime.timeout_seconds", + "must be a finite number greater than zero", + ); + } + if let Some(system) = config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + && system.content.trim().is_empty() + { + return invalid_config("instructions.system.content", "must be a non-empty string"); + } + for (role, model) in &config.models { + if model.provider.trim().is_empty() + || model.provider.trim() != model.provider + || model.provider.to_ascii_lowercase() != model.provider + { + return invalid_config( + format!("models.{role}.provider"), + "must be a non-empty lowercase identifier", + ); + } + if model.model.trim().is_empty() { + return invalid_config(format!("models.{role}.model"), "must be a non-empty string"); + } + if model + .api_key_env + .as_ref() + .is_some_and(|name| name.trim().is_empty()) + { + return invalid_config( + format!("models.{role}.api_key_env"), + "must be a non-empty string", + ); + } + if let Some(base_url) = &model.base_url + && base_url.trim().is_empty() + { + return invalid_config( + format!("models.{role}.base_url"), + "must be a non-empty string", + ); + } + } + if let Some(environment) = &config.environment { + for name in environment.env.keys() { + if name.trim().is_empty() { + return invalid_config("environment.env", "variable names must not be empty"); + } + } + } + if let Some(tools) = &config.tools { + if let Some(enabled) = &tools.enabled { + validate_names("tools.enabled", enabled)?; + if let Some(name) = enabled.iter().find(|name| tools.blocked.contains(name)) { + return invalid_config( + "tools", + format!("`{name}` cannot be both enabled and blocked"), + ); + } + } + validate_names("tools.blocked", &tools.blocked)?; + } + Ok(()) +} + +fn validate_names(field: &str, names: &[String]) -> Result<()> { + if names.iter().any(|name| name.trim().is_empty()) { + return invalid_config(field, "entries must be non-empty strings"); + } Ok(()) } -/// Resolve a typed NeMo Fabric config into a runnable plan. +fn invalid_config(field: impl Into, reason: impl Into) -> Result { + Err(FabricError::InvalidConfig { + field: field.into(), + reason: reason.into(), + }) +} + +/// Resolve a typed NVIDIA NeMo Fabric config into a runnable plan. /// /// Callers provide an already-composed typed config and the explicit base /// directory used for resolving relative paths. @@ -1026,6 +1218,44 @@ pub fn resolve_run_plan_from_config_with_adapter_directories( config: FabricConfig, context: ResolveContext, adapter_directories: &[PathBuf], +) -> Result { + resolve_run_plan_from_config_with_adapter_directories_mode( + config, + context, + adapter_directories, + true, + ) +} + +/// Resolve a typed Fabric config while retaining adapter incompatibilities for diagnostics. +#[doc(hidden)] +pub fn resolve_diagnostic_plan_from_config( + config: FabricConfig, + context: ResolveContext, +) -> Result { + resolve_diagnostic_plan_from_config_with_adapter_directories(config, context, &[]) +} + +/// Resolve a diagnostic plan with additional adapter descriptor directories. +#[doc(hidden)] +pub fn resolve_diagnostic_plan_from_config_with_adapter_directories( + config: FabricConfig, + context: ResolveContext, + adapter_directories: &[PathBuf], +) -> Result { + resolve_run_plan_from_config_with_adapter_directories_mode( + config, + context, + adapter_directories, + false, + ) +} + +fn resolve_run_plan_from_config_with_adapter_directories_mode( + config: FabricConfig, + context: ResolveContext, + adapter_directories: &[PathBuf], + enforce_compatibility: bool, ) -> Result { validate_config(&config)?; let supplied_base_dir = context.base_dir; @@ -1035,7 +1265,7 @@ pub fn resolve_run_plan_from_config_with_adapter_directories( path: supplied_base_dir, source, })?; - resolve_run_plan(config, base_dir, adapter_directories) + resolve_run_plan(config, base_dir, adapter_directories, enforce_compatibility) } fn read_json(path: &Path) -> Result @@ -1056,15 +1286,22 @@ fn resolve_run_plan( config: FabricConfig, base_dir: PathBuf, adapter_directories: &[PathBuf], + enforce_compatibility: bool, ) -> Result { let adapter_descriptor = resolve_adapter_descriptor(&config, &base_dir, adapter_directories)?; let descriptor = adapter_descriptor .as_ref() .map(|adapter| &adapter.descriptor); + if enforce_compatibility { + validate_adapter_config_compatibility(&config, descriptor)?; + } let resolution = resolve_resolution(&config, descriptor)?; let environment_plan = resolve_environment_plan(&config, &base_dir); validate_control_location(descriptor, environment_plan.as_ref())?; let capability_plan = resolve_capability_plan(&config, &base_dir, adapter_descriptor.as_ref()); + if enforce_compatibility { + validate_capability_plan_compatibility(&capability_plan, descriptor)?; + } let capabilities = resolve_runtime_capabilities(&config, descriptor); let telemetry_plan = resolve_telemetry_plan(&config, descriptor)?; Ok(RunPlan { @@ -1080,6 +1317,120 @@ fn resolve_run_plan( }) } +fn validate_capability_plan_compatibility( + capability_plan: &CapabilityPlan, + descriptor: Option<&AdapterDescriptor>, +) -> Result<()> { + let Some(route) = capability_plan + .routes + .iter() + .find(|route| route.target == CapabilityTarget::Unsupported) + else { + return Ok(()); + }; + Err(FabricError::AdapterCompatibility { + adapter_id: descriptor + .map(|descriptor| descriptor.adapter_id.clone()) + .unwrap_or_else(|| "unknown".to_string()), + field: route.config_field(), + reason: route.reason.clone(), + }) +} + +pub(crate) fn validate_adapter_config_compatibility( + config: &FabricConfig, + descriptor: Option<&AdapterDescriptor>, +) -> Result<()> { + let Some(issue) = adapter_config_compatibility_issues(config, descriptor) + .into_iter() + .next() + else { + return Ok(()); + }; + Err(FabricError::AdapterCompatibility { + adapter_id: issue.adapter_id, + field: issue.field, + reason: issue.reason, + }) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) struct AdapterCompatibilityIssue { + pub(crate) adapter_id: String, + pub(crate) field: String, + pub(crate) reason: String, +} + +pub(crate) fn adapter_config_compatibility_issues( + config: &FabricConfig, + descriptor: Option<&AdapterDescriptor>, +) -> Vec { + let Some(descriptor) = descriptor else { + return Vec::new(); + }; + let accepts = |field: AdapterConfigField| descriptor.config.accepts.contains(&field); + let incompatible = |field: String, reason: String| AdapterCompatibilityIssue { + adapter_id: descriptor.adapter_id.clone(), + reason, + field, + }; + let mut issues = Vec::new(); + + if config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + .is_some() + && !accepts(AdapterConfigField::SystemInstructions) + { + issues.push(incompatible( + "instructions.system".to_string(), + "the adapter does not declare an equivalent native mapping".to_string(), + )); + } + if config.runtime.max_turns.is_some() && !accepts(AdapterConfigField::MaxTurns) { + issues.push(incompatible( + "runtime.max_turns".to_string(), + "the adapter does not declare an equivalent native mapping".to_string(), + )); + } + if !config.models.is_empty() && !accepts(AdapterConfigField::Models) { + issues.push(incompatible( + "models".to_string(), + "the adapter does not consume normalized model configuration".to_string(), + )); + return issues; + }; + + let selected_model = match (config.models.get_key_value("default"), config.models.len()) { + (Some(model), _) => Some(model), + (None, 0) => None, + (None, 1) => config.models.first_key_value(), + (None, _) => { + issues.push(incompatible( + "models".to_string(), + "multiple model roles are configured and no default role selects one".to_string(), + )); + None + } + }; + if let Some((role, model)) = selected_model { + if model.base_url.is_some() && !accepts(AdapterConfigField::ModelBaseUrl) { + issues.push(incompatible( + format!("models.{role}.base_url"), + "the adapter does not declare custom endpoint support".to_string(), + )); + } + if model.temperature.is_some() && !accepts(AdapterConfigField::ModelTemperature) { + issues.push(incompatible( + format!("models.{role}.temperature"), + "the adapter does not declare an equivalent native mapping".to_string(), + )); + } + } + issues +} + fn resolve_adapter_descriptor( config: &FabricConfig, base_dir: &Path, @@ -1197,6 +1548,7 @@ fn resolve_environment_plan(config: &FabricConfig, base_dir: &Path) -> Option, ) -> CapabilityPlan { - let accepts = |area: &str| { + let accepts = |field: AdapterConfigField| { adapter_descriptor - .map(|adapter| { - adapter - .descriptor - .config - .accepts - .iter() - .any(|accepted| accepted == area) - }) + .map(|adapter| adapter.descriptor.config.accepts.contains(&field)) .unwrap_or(false) }; let skill_paths: Vec = config @@ -1231,7 +1576,7 @@ fn resolve_capability_plan( .collect() }) .unwrap_or_default(); - let skills_are_native = !skill_paths.is_empty() && accepts("skills"); + let skills_are_native = !skill_paths.is_empty() && accepts(AdapterConfigField::Skills); let mcp_servers: BTreeMap = config .mcp .as_ref() @@ -1251,35 +1596,59 @@ fn resolve_capability_plan( .collect() }) .unwrap_or_default(); + let enabled_tools = config + .tools + .as_ref() + .and_then(|tools| tools.enabled.clone()); let blocked_tools = config .tools .as_ref() .map(|tools| tools.blocked.clone()) .unwrap_or_default(); - let tools_configured = !blocked_tools.is_empty(); - let tools_are_native = tools_configured && accepts("tools.blocked"); + let enabled_tools_configured = enabled_tools.is_some(); + let blocked_tools_configured = !blocked_tools.is_empty(); + let tools_configured = enabled_tools_configured || blocked_tools_configured; let mut native = CapabilityTargetPlan::default(); let managed = CapabilityTargetPlan::default(); let mut unsupported = CapabilityTargetPlan::default(); let mut routes = Vec::new(); - if tools_configured { - if tools_are_native { + for (configured, support, field, description) in [ + ( + enabled_tools_configured, + AdapterConfigField::EnabledTools, + "tools.enabled", + "enabled-tools selection", + ), + ( + blocked_tools_configured, + AdapterConfigField::BlockedTools, + "tools.blocked", + "blocked-tools policy", + ), + ] { + if !configured { + continue; + } + if accepts(support) { native.tools_configured = true; routes.push(CapabilityRoute { kind: CapabilityKind::Tools, - name: "tools.blocked".to_string(), + name: field.to_string(), target: CapabilityTarget::HarnessNative, - reason: "selected adapter explicitly supports the NeMo Fabric blocked-tools policy" - .to_string(), + reason: format!( + "selected adapter explicitly supports the NeMo Fabric {description}" + ), }); } else { unsupported.tools_configured = true; routes.push(CapabilityRoute { kind: CapabilityKind::Tools, - name: "tools.blocked".to_string(), + name: field.to_string(), target: CapabilityTarget::Unsupported, - reason: "selected adapter does not explicitly declare blocked-tools policy support and NeMo Fabric-managed enforcement is not implemented".to_string(), + reason: format!( + "selected adapter does not explicitly declare {description} support and NeMo Fabric-managed enforcement is not implemented" + ), }); } } @@ -1305,8 +1674,8 @@ fn resolve_capability_plan( } for (name, server) in &mcp_servers { - let can_map_native = - accepts("mcp") && matches!(server.exposure, McpExposure::HarnessNative); + let can_map_native = accepts(AdapterConfigField::Mcp) + && matches!(server.exposure, McpExposure::HarnessNative); if can_map_native { native.mcp_servers.insert(name.clone(), server.clone()); routes.push(CapabilityRoute { @@ -1336,6 +1705,7 @@ fn resolve_capability_plan( CapabilityPlan { tools: ToolsPlan { + enabled: enabled_tools, blocked: blocked_tools, }, tools_configured, @@ -1525,6 +1895,9 @@ pub struct EnvironmentPlan { /// Resolved artifact path. #[serde(default, skip_serializing_if = "Option::is_none")] pub artifacts: Option, + /// Environment variables visible to the harness and its tools. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub env: BTreeMap, /// Provider connection metadata. #[serde(default, skip_serializing_if = "serde_json::Map::is_empty")] pub connection: serde_json::Map, @@ -1568,7 +1941,10 @@ pub struct CapabilityPlan { /// Normalized tool policy for a run. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct ToolsPlan { - /// Adapter-native tool names or toolset names to block. + /// Adapter-native tool names to expose. `None` preserves the harness default. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub enabled: Option>, + /// Adapter-native tool names to block. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub blocked: Vec, } @@ -1587,19 +1963,32 @@ pub struct CapabilityTargetPlan { pub mcp_servers: BTreeMap, } -/// One capability routing decision. +/// One capability execution assignment. +/// +/// Routes apply to executable tool, skill, and MCP capabilities. Adapter-translated +/// scalar configuration is validated separately against [`AdapterConfigSupport`]. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct CapabilityRoute { /// Capability kind. pub kind: CapabilityKind, /// Capability name. pub name: String, - /// Routing target. + /// Component responsible for executing the capability. pub target: CapabilityTarget, /// Human-readable reason for the selected route. pub reason: String, } +impl CapabilityRoute { + pub(crate) fn config_field(&self) -> String { + match self.kind { + CapabilityKind::Mcp => format!("mcp.servers.{}", self.name), + CapabilityKind::Skills => "skills".to_string(), + CapabilityKind::Tools => self.name.clone(), + } + } +} + /// Capability kind. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] @@ -1612,15 +2001,17 @@ pub enum CapabilityKind { Mcp, } -/// Capability routing target. +/// Component responsible for executing a configured capability. +/// +/// This target describes execution ownership, not network routing. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum CapabilityTarget { - /// Adapter maps the capability into harness-native config. + /// The selected adapter maps and executes the capability through its harness. HarnessNative, - /// NeMo Fabric exposes or manages the capability around the harness. + /// NeMo Fabric executes the capability outside the harness-native surface. FabricManaged, - /// Capability is configured but no executable surface exists. + /// Neither the adapter nor NeMo Fabric can execute the configured capability. Unsupported, } @@ -1775,6 +2166,322 @@ mod tests { assert!(value.get("effective_config").is_none()); } + #[test] + fn normalized_fields_survive_planning() { + let mut config = typed_config("nvidia.fabric.hermes"); + config.instructions = Some(InstructionsConfig { + system: Some(InstructionConfig { + content: "Be concise.".to_string(), + mode: InstructionMode::Replace, + extensions: BTreeMap::new(), + }), + extensions: BTreeMap::new(), + }); + config.runtime.max_turns = Some(7); + config.runtime.timeout_seconds = Some(12.5); + config.environment.as_mut().expect("environment").env = + BTreeMap::from([("VISIBLE".to_string(), "yes".to_string())]); + config.models.insert( + "default".to_string(), + ModelConfig { + provider: "nvidia".to_string(), + model: "nvidia/test".to_string(), + temperature: Some(0.2), + api_key_env: Some("NVIDIA_API_KEY".to_string()), + base_url: Some("https://models.example/v1".to_string()), + settings: serde_json::Map::new(), + extensions: BTreeMap::new(), + }, + ); + config.tools = Some(ToolsConfig { + enabled: Some(vec!["terminal".to_string()]), + blocked: vec!["browser".to_string()], + extensions: BTreeMap::new(), + }); + + let plan = + resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-normalized")) + .expect("normalized plan"); + + assert_eq!( + plan.config + .instructions + .as_ref() + .and_then(|instructions| instructions.system.as_ref()) + .map(|instruction| instruction.content.as_str()), + Some("Be concise.") + ); + assert_eq!(plan.config.runtime.max_turns, Some(7)); + assert_eq!(plan.config.runtime.timeout_seconds, Some(12.5)); + assert_eq!( + plan.environment_plan + .as_ref() + .and_then(|environment| environment.env.get("VISIBLE")), + Some(&"yes".to_string()) + ); + assert_eq!( + plan.capability_plan.tools.enabled.as_ref(), + Some(&vec!["terminal".to_string()]) + ); + assert!(plan.capability_plan.routes.iter().any(|route| { + route.name == "tools.enabled" && route.target == CapabilityTarget::HarnessNative + })); + } + + #[test] + fn empty_system_instruction_is_rejected() { + let mut config = typed_config("nvidia.fabric.hermes"); + config.instructions = Some(InstructionsConfig { + system: Some(InstructionConfig { + content: " ".to_string(), + mode: InstructionMode::Replace, + extensions: BTreeMap::new(), + }), + extensions: BTreeMap::new(), + }); + + let error = resolve_run_plan_from_config( + config, + ResolveContext::new("/tmp/fabric-empty-instruction"), + ) + .expect_err("blank instruction must be rejected"); + + assert!(matches!( + error, + FabricError::InvalidConfig { field, .. } + if field == "instructions.system.content" + )); + } + + #[test] + fn unsupported_normalized_scalar_reports_adapter_config_incompatibility() { + for adapter_id in ["nvidia.fabric.codex", "nvidia.fabric.langchain.deepagents"] { + let mut config = typed_config(adapter_id); + config.runtime.max_turns = Some(3); + + let error = resolve_run_plan_from_config( + config, + ResolveContext::new("/tmp/fabric-incompatible"), + ) + .expect_err("adapter does not advertise max_turns"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { + adapter_id: actual, + field, + .. + } if actual == adapter_id && field == "runtime.max_turns" + )); + } + } + + #[test] + fn unsupported_model_temperature_reports_canonical_field() { + for (adapter_id, provider) in [ + ("nvidia.fabric.claude", "anthropic"), + ("nvidia.fabric.codex", "openai"), + ] { + let mut config = typed_config(adapter_id); + config.models.insert( + "review".to_string(), + ModelConfig { + provider: provider.to_string(), + model: "test-model".to_string(), + temperature: Some(0.2), + api_key_env: None, + base_url: None, + settings: serde_json::Map::new(), + extensions: BTreeMap::new(), + }, + ); + + let error = resolve_run_plan_from_config( + config, + ResolveContext::new("/tmp/fabric-temperature"), + ) + .expect_err("adapter does not advertise model temperature"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { + adapter_id: actual, + field, + .. + } if actual == adapter_id && field == "models.review.temperature" + )); + } + } + + #[test] + fn unsupported_enabled_tools_report_canonical_field() { + let adapter_id = "nvidia.fabric.codex"; + let mut config = typed_config(adapter_id); + config.tools = Some(ToolsConfig { + enabled: Some(vec!["terminal".to_string()]), + blocked: Vec::new(), + extensions: BTreeMap::new(), + }); + + let error = + resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-enabled-tools")) + .expect_err("adapter does not advertise enabled tools"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { + adapter_id: actual, + field, + .. + } if actual == adapter_id && field == "tools.enabled" + )); + } + + #[test] + fn sole_named_model_is_selected_without_forcing_default_role() { + let mut config = typed_config("nvidia.fabric.claude"); + config.models.insert( + "review".to_string(), + ModelConfig { + provider: "anthropic".to_string(), + model: "claude-test".to_string(), + temperature: None, + api_key_env: None, + base_url: None, + settings: serde_json::Map::new(), + extensions: BTreeMap::new(), + }, + ); + + resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-model-role")) + .expect("sole named model"); + } + + #[test] + fn multiple_models_require_an_explicit_default_role() { + let mut config = typed_config("nvidia.fabric.claude"); + for role in ["fast", "slow"] { + config.models.insert( + role.to_string(), + ModelConfig { + provider: "anthropic".to_string(), + model: format!("claude-{role}"), + temperature: None, + api_key_env: None, + base_url: None, + settings: serde_json::Map::new(), + extensions: BTreeMap::new(), + }, + ); + } + + let error = + resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-model-role")) + .expect_err("ambiguous model roles"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { field, reason, .. } + if field == "models" && reason.contains("no default role") + )); + } + + #[test] + fn enabled_and_blocked_tool_policies_are_routed_independently() { + let mut config = typed_config("nvidia.fabric.hermes"); + config.tools = Some(ToolsConfig { + enabled: Some(Vec::new()), + blocked: vec!["browser".to_string()], + extensions: BTreeMap::new(), + }); + + let plan = resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-tools")) + .expect("tool capability plan"); + + assert!(plan.capability_plan.routes.iter().any(|route| { + route.name == "tools.enabled" && route.target == CapabilityTarget::HarnessNative + })); + assert!(plan.capability_plan.routes.iter().any(|route| { + route.name == "tools.blocked" && route.target == CapabilityTarget::HarnessNative + })); + } + + #[test] + fn unsupported_tool_policy_fails_during_planning() { + let mut config = typed_config("nvidia.fabric.codex"); + config.tools = Some(ToolsConfig { + enabled: None, + blocked: vec!["Bash".to_string()], + extensions: BTreeMap::new(), + }); + + let error = resolve_run_plan_from_config( + config, + ResolveContext::new("/tmp/fabric-unsupported-tools"), + ) + .expect_err("Codex does not support per-tool blocking"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { + adapter_id, + field, + .. + } if adapter_id == "nvidia.fabric.codex" && field == "tools.blocked" + )); + } + + #[test] + fn unsupported_mcp_reports_canonical_config_path() { + let mut config = typed_config("nvidia.fabric.claude"); + config.mcp = Some(McpConfig { + servers: BTreeMap::from([( + "docs".to_string(), + McpServerConfig { + transport: "streamable-http".to_string(), + url: "https://mcp.example".to_string(), + exposure: McpExposure::FabricManaged, + extensions: BTreeMap::new(), + }, + )]), + extensions: BTreeMap::new(), + }); + + let error = resolve_run_plan_from_config( + config, + ResolveContext::new("/tmp/fabric-unsupported-mcp"), + ) + .expect_err("Fabric-managed MCP is not implemented"); + + assert!(matches!( + error, + FabricError::AdapterCompatibility { + adapter_id, + field, + .. + } if adapter_id == "nvidia.fabric.claude" && field == "mcp.servers.docs" + )); + } + + #[test] + fn overlapping_tool_policy_is_invalid() { + let mut config = typed_config("nvidia.fabric.hermes"); + config.tools = Some(ToolsConfig { + enabled: Some(vec!["browser".to_string()]), + blocked: vec!["browser".to_string()], + extensions: BTreeMap::new(), + }); + + let error = + resolve_run_plan_from_config(config, ResolveContext::new("/tmp/fabric-invalid-tools")) + .expect_err("overlapping tool policy"); + + assert!(matches!( + error, + FabricError::InvalidConfig { field, .. } if field == "tools" + )); + } + #[test] fn loads_and_validates_json_adapter_descriptor() { let descriptor = diff --git a/crates/fabric-core/src/doctor.rs b/crates/fabric-core/src/doctor.rs index 8327311cb..a97ceeb2b 100644 --- a/crates/fabric-core/src/doctor.rs +++ b/crates/fabric-core/src/doctor.rs @@ -12,7 +12,7 @@ use serde_json::Value; use crate::config::{ AdapterKind, CapabilityKind, CapabilityTarget, ControlLocation, EnvironmentOwnership, - ResolutionStrategy, RunPlan, + ResolutionStrategy, RunPlan, adapter_config_compatibility_issues, }; /// Diagnostic status. @@ -56,6 +56,7 @@ pub struct DoctorReport { pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { let mut checks = Vec::new(); checks.push(check_adapter_descriptor(plan)); + checks.extend(check_adapter_config_compatibility(plan)); checks.push(check_resolution(plan)); checks.extend(check_runtime_execution_surface(plan)); checks.push(check_environment_context(plan)); @@ -71,6 +72,31 @@ pub fn doctor_plan(plan: &RunPlan) -> DoctorReport { } } +fn check_adapter_config_compatibility(plan: &RunPlan) -> Vec { + adapter_config_compatibility_issues( + &plan.config, + plan.adapter_descriptor + .as_ref() + .map(|adapter| &adapter.descriptor), + ) + .into_iter() + .map(|issue| { + let mut metadata = BTreeMap::new(); + metadata.insert("adapter_id".to_string(), Value::String(issue.adapter_id)); + metadata.insert("field".to_string(), Value::String(issue.field.clone())); + check_with_metadata( + "config.unsupported", + DoctorStatus::Fail, + format!( + "configuration at `{}` cannot be implemented by the selected adapter: {}", + issue.field, issue.reason + ), + metadata, + ) + }) + .collect() +} + fn check_adapter_descriptor(plan: &RunPlan) -> DoctorCheck { if let Some(adapter) = &plan.adapter_descriptor { let mut metadata = BTreeMap::new(); @@ -467,7 +493,11 @@ fn worst(left: DoctorStatus, right: DoctorStatus) -> DoctorStatus { #[cfg(test)] mod tests { use super::*; - use crate::config::{FabricConfig, ResolveContext, resolve_run_plan_from_config}; + use crate::config::{ + FabricConfig, ResolveContext, resolve_diagnostic_plan_from_config, + resolve_run_plan_from_config, + }; + use crate::error::FabricError; #[test] fn diagnoses_a_typed_plan_without_file_source_fields() { @@ -504,4 +534,45 @@ mod tests { .any(|check| check.message.contains(&expected_command)) ); } + + #[test] + fn diagnoses_unsupported_config_without_weakening_strict_planning() { + let config: FabricConfig = serde_json::from_value(serde_json::json!({ + "schema_version": "fabric.agent/v1alpha1", + "metadata": {"name": "incompatible-agent"}, + "harness": { + "adapter_id": "nvidia.fabric.codex", + "resolution": "preinstalled" + }, + "runtime": {"max_turns": 3}, + "tools": {"enabled": []} + })) + .expect("typed config"); + let base_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../.."); + + let strict_error = + resolve_run_plan_from_config(config.clone(), ResolveContext::new(&base_dir)) + .expect_err("strict planning must reject unsupported config"); + assert!(matches!( + strict_error, + FabricError::AdapterCompatibility { .. } + )); + + let plan = resolve_diagnostic_plan_from_config(config, ResolveContext::new(base_dir)) + .expect("diagnostic plan"); + let report = doctor_plan(&plan); + + assert_eq!(report.status, DoctorStatus::Fail); + assert!(report.checks.iter().any(|check| { + check.name == "config.unsupported" + && check.status == DoctorStatus::Fail + && check.metadata.get("field") + == Some(&Value::String("runtime.max_turns".to_string())) + })); + assert!(report.checks.iter().any(|check| { + check.name == "capability.unsupported" + && check.status == DoctorStatus::Fail + && check.message.contains("tools.enabled") + })); + } } diff --git a/crates/fabric-core/src/error.rs b/crates/fabric-core/src/error.rs index 77cc7c746..2a5e19af1 100644 --- a/crates/fabric-core/src/error.rs +++ b/crates/fabric-core/src/error.rs @@ -64,6 +64,24 @@ pub enum FabricError { /// Validation message. message: String, }, + /// A normalized Fabric config field is invalid. + #[error("invalid Fabric configuration at `{field}`: {reason}")] + InvalidConfig { + /// Canonical configuration path. + field: String, + /// Validation failure. + reason: String, + }, + /// A valid normalized field cannot be implemented by the selected adapter. + #[error("adapter `{adapter_id}` cannot implement configuration at `{field}`: {reason}")] + AdapterCompatibility { + /// Selected adapter id. + adapter_id: String, + /// Canonical configuration path. + field: String, + /// Compatibility failure. + reason: String, + }, /// A requested schema is not known. #[error("unknown schema `{schema}`; available schemas: {available:?}")] UnknownSchema { @@ -103,14 +121,6 @@ pub enum FabricError { /// Bounded adapter-host diagnostics. diagnostics: String, }, - /// The selected harness cannot enforce the configured blocked-tools policy. - #[error("harness `{harness}` cannot enforce configured blocked tools: {reason}")] - UnsupportedToolsPolicy { - /// Harness type. - harness: String, - /// Capability-routing explanation. - reason: String, - }, /// A runtime handle was used with a different run plan than the one that created it. #[error( "runtime handle does not match run plan for `{field}`: expected `{expected}` but found `{actual}` (runtime `{runtime_id}`)" diff --git a/crates/fabric-core/src/lib.rs b/crates/fabric-core/src/lib.rs index c2fc71dcd..dd6c0bc7f 100644 --- a/crates/fabric-core/src/lib.rs +++ b/crates/fabric-core/src/lib.rs @@ -10,14 +10,17 @@ pub mod runtime; pub mod schema; pub use config::{ - ADAPTER_CONTRACT_VERSION, AdapterConfigSupport, AdapterDescriptor, AdapterDescriptorSource, - AdapterKind, AdapterRequirements, AdapterTelemetryProviderSupport, AdapterTelemetrySupport, - CapabilityPlan, ControlLocation, EnvironmentConfig, EnvironmentOwnership, EnvironmentPlan, - FabricConfig, HarnessConfig, McpConfig, McpExposure, McpServerPlan, MetadataConfig, + ADAPTER_CONTRACT_VERSION, AdapterConfigField, AdapterConfigSupport, AdapterDescriptor, + AdapterDescriptorSource, AdapterKind, AdapterRequirements, AdapterTelemetryProviderSupport, + AdapterTelemetrySupport, CapabilityPlan, ControlLocation, EnvironmentConfig, + EnvironmentOwnership, EnvironmentPlan, FabricConfig, HarnessConfig, InstructionConfig, + InstructionMode, InstructionsConfig, McpConfig, McpExposure, McpServerPlan, MetadataConfig, ModelConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, - TelemetryProvider, TelemetryProviderConfig, load_adapter_descriptor, - resolve_run_plan_from_config, resolve_run_plan_from_config_with_adapter_directories, + TelemetryProvider, TelemetryProviderConfig, ToolsConfig, load_adapter_descriptor, + resolve_diagnostic_plan_from_config, + resolve_diagnostic_plan_from_config_with_adapter_directories, resolve_run_plan_from_config, + resolve_run_plan_from_config_with_adapter_directories, }; pub use doctor::{DoctorCheck, DoctorReport, DoctorStatus, doctor_plan}; pub use error::{FabricError, Result}; diff --git a/crates/fabric-core/src/runtime.rs b/crates/fabric-core/src/runtime.rs index 6f54c3292..e89cd5535 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -20,8 +20,8 @@ use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; use crate::config::{ - AdapterKind, CapabilityKind, CapabilityPlan, CapabilityTarget, ControlLocation, - EnvironmentOwnership, FabricConfig, RunPlan, TelemetryPlan, + AdapterKind, CapabilityPlan, CapabilityTarget, ControlLocation, EnvironmentOwnership, + FabricConfig, RunPlan, TelemetryPlan, validate_adapter_config_compatibility, }; use crate::error::{FabricError, Result}; @@ -242,6 +242,9 @@ pub struct EnvironmentHandle { /// Artifact root visible to the harness runtime. #[serde(default, skip_serializing_if = "Option::is_none")] pub artifacts: Option, + /// Environment variables visible to the harness and its tools. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub env: BTreeMap, /// Whether NeMo Fabric owns the environment resource. pub ownership: EnvironmentOwnership, /// Provider connection metadata. @@ -475,6 +478,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { ownership, workspace, artifacts, + environment_env, connection_settings, environment_metadata, settings, @@ -485,6 +489,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { environment.ownership, environment.workspace.clone(), environment.artifacts.clone(), + environment.env.clone(), environment.connection.clone(), environment.metadata.clone(), environment.settings.clone(), @@ -500,6 +505,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { .artifacts .as_ref() .map(|artifacts| resolve_path(&plan.base_dir, artifacts)), + BTreeMap::new(), serde_json::Map::new(), serde_json::Map::new(), serde_json::Map::new(), @@ -524,6 +530,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { control_location, workspace, artifacts, + env: environment_env, ownership, connection, metadata, @@ -532,7 +539,7 @@ pub fn prepare_environment(plan: &RunPlan) -> Result { /// Start or connect to a harness runtime. pub fn start_runtime(plan: &RunPlan) -> Result { - validate_blocked_tools_support(plan)?; + validate_adapter_compatibility(plan)?; let environment = prepare_environment(plan)?; if uses_local_host(plan) { return LocalHostAdapter.start(plan, environment); @@ -549,7 +556,7 @@ pub fn invoke_runtime( runtime: &RuntimeHandle, request: RunRequest, ) -> Result { - validate_blocked_tools_support(plan)?; + validate_adapter_compatibility(plan)?; validate_runtime_handle(plan, runtime)?; if uses_local_host(plan) { return LocalHostAdapter.invoke(plan, runtime, request); @@ -560,12 +567,22 @@ pub fn invoke_runtime( }) } -fn validate_blocked_tools_support(plan: &RunPlan) -> Result<()> { - if let Some(route) = plan.capability_plan.routes.iter().find(|route| { - route.kind == CapabilityKind::Tools && route.target == CapabilityTarget::Unsupported - }) { - return Err(FabricError::UnsupportedToolsPolicy { - harness: harness(plan), +fn validate_adapter_compatibility(plan: &RunPlan) -> Result<()> { + validate_adapter_config_compatibility( + &plan.config, + plan.adapter_descriptor + .as_ref() + .map(|adapter| &adapter.descriptor), + )?; + if let Some(route) = plan + .capability_plan + .routes + .iter() + .find(|route| route.target == CapabilityTarget::Unsupported) + { + return Err(FabricError::AdapterCompatibility { + adapter_id: adapter_id(plan).unwrap_or_else(|| harness(plan)), + field: route.config_field(), reason: route.reason.clone(), }); } @@ -647,6 +664,7 @@ struct RuntimeEnvironmentBinding<'a> { control_location: ControlLocation, workspace: &'a Option, artifacts: &'a Option, + env: &'a BTreeMap, ownership: EnvironmentOwnership, connection: &'a BTreeMap, metadata: &'a BTreeMap, @@ -658,6 +676,7 @@ fn runtime_environment_binding(environment: &EnvironmentHandle) -> RuntimeEnviro control_location: environment.control_location, workspace: &environment.workspace, artifacts: &environment.artifacts, + env: &environment.env, ownership: environment.ownership, connection: &environment.connection, metadata: &environment.metadata, @@ -894,7 +913,22 @@ fn run_local_host_adapter( runtime: &RuntimeHandle, request: RunRequest, ) -> Result { - run_local_host_adapter_with_timeout(plan, runtime, request, LOCAL_HOST_INVOKE_TIMEOUT) + let timeout = match plan.config.runtime.timeout_seconds { + Some(seconds) if seconds <= 0.0 => { + return Err(FabricError::InvalidConfig { + field: "runtime.timeout_seconds".to_string(), + reason: "must be a finite number greater than zero".to_string(), + }); + } + Some(seconds) => { + Duration::try_from_secs_f64(seconds).map_err(|_| FabricError::InvalidConfig { + field: "runtime.timeout_seconds".to_string(), + reason: "must be a finite number greater than zero".to_string(), + })? + } + None => LOCAL_HOST_INVOKE_TIMEOUT, + }; + run_local_host_adapter_with_timeout(plan, runtime, request, timeout) } fn run_local_host_adapter_with_timeout( @@ -937,7 +971,16 @@ fn run_local_host_adapter_with_timeout( &artifacts, relay_config.as_ref(), )?; - let adapter_payload = serde_json::to_string_pretty(&adapter_invocation) + let mut persisted_invocation = adapter_invocation.clone(); + for value in persisted_invocation + .runtime_context + .environment + .env + .values_mut() + { + *value = "[REDACTED]".to_string(); + } + let adapter_payload = serde_json::to_string_pretty(&persisted_invocation) .map_err(FabricError::SerializeJson)?; let fabric_invocation = write_fabric_invocation(&fabric_home, &adapter_payload)?; let lifecycle_request = AdapterLifecycleRequest::new(AdapterLifecycleRequestKind::Invoke( @@ -1336,7 +1379,8 @@ fn process_local_host_command( command .args(&command_args) .current_dir(cwd) - .envs(&settings.env); + .envs(&settings.env) + .envs(&runtime.environment.env); let display = std::iter::once(command_path.to_string_lossy().into_owned()) .chain(command_args) .collect::>() @@ -1359,7 +1403,8 @@ fn python_local_host_command(plan: &RunPlan, runtime: &RuntimeHandle) -> Result< .arg(&settings.module) .args(&settings.args) .current_dir(cwd) - .envs(&settings.env); + .envs(&settings.env) + .envs(&runtime.environment.env); Ok(( command, format!("{} -m {}", python.to_string_lossy(), settings.module), @@ -2460,6 +2505,7 @@ for line in sys.stdin: "runtime_id": invocation["runtime_context"]["runtime_id"], "invocation_id": invocation["runtime_context"]["invocation_id"], "request_id": invocation["runtime_context"]["request_id"], + "normalized_env": os.environ.get("FABRIC_NORMALIZED_ENV"), } if MODE == "adapter_reported_failure": output.update({ @@ -2494,17 +2540,15 @@ for line in sys.stdin: "env": {"FABRIC_FAKE_HOST_MODE": mode}, }, }, - "models": { - "default": { - "provider": "test", - "model": "test-model", - }, - }, "runtime": { "input_schema": "text", "output_schema": "text", "artifacts": "./artifacts", }, + "environment": { + "provider": "local", + "env": {"FABRIC_NORMALIZED_ENV": "visible"}, + }, }); if relay { config_value["telemetry"] = serde_json::json!({ @@ -2538,8 +2582,22 @@ for line in sys.stdin: assert_eq!(first.output["invocation_count"], serde_json::json!(1)); assert_eq!(second.output["invocation_count"], serde_json::json!(2)); assert_eq!(first.output["input"], serde_json::json!("first")); + assert_eq!(first.output["normalized_env"], serde_json::json!("visible")); assert_eq!(second.output["input"], serde_json::json!("second")); assert_eq!(first.metadata["host_pid"], second.metadata["host_pid"]); + let persisted_invocation: Value = serde_json::from_str( + &fs::read_to_string( + first.metadata["fabric_invocation"] + .as_str() + .expect("invocation path"), + ) + .expect("read persisted invocation"), + ) + .expect("parse persisted invocation"); + assert_eq!( + persisted_invocation["runtime_context"]["environment"]["env"]["FABRIC_NORMALIZED_ENV"], + serde_json::json!("[REDACTED]") + ); assert_eq!( first.metadata["adapter_runner"], serde_json::json!("persistent_local_host") @@ -2699,6 +2757,52 @@ for line in sys.stdin: let _ = fs::remove_dir_all(root); } + #[test] + fn local_host_rejects_zero_timeout_from_deserialized_plan() { + let (root, plan) = local_host_plan("success"); + let runtime = start_runtime(&plan).expect("start local host"); + let mut invalid_plan = plan.clone(); + invalid_plan.config.runtime.timeout_seconds = Some(0.0); + + let error = run_local_host_adapter(&invalid_plan, &runtime, RunRequest::text("first")) + .expect_err("zero timeout must be rejected"); + + assert!(matches!( + error, + FabricError::InvalidConfig { field, .. } + if field == "runtime.timeout_seconds" + )); + stop_runtime(&plan, &runtime).expect("stop local host"); + let _ = fs::remove_dir_all(root); + } + + #[test] + fn local_host_revalidates_scalar_compatibility_at_runtime_boundaries() { + let (root, plan) = local_host_plan("success"); + let mut incompatible_plan = plan.clone(); + incompatible_plan.config.runtime.max_turns = Some(1); + + let start_error = + start_runtime(&incompatible_plan).expect_err("start must reject incompatibility"); + assert!(matches!( + start_error, + FabricError::AdapterCompatibility { field, .. } + if field == "runtime.max_turns" + )); + + let runtime = start_runtime(&plan).expect("start local host"); + let invoke_error = invoke_runtime(&incompatible_plan, &runtime, RunRequest::text("first")) + .expect_err("invoke must reject incompatibility"); + assert!(matches!( + invoke_error, + FabricError::AdapterCompatibility { field, .. } + if field == "runtime.max_turns" + )); + + stop_runtime(&plan, &runtime).expect("stop local host"); + let _ = fs::remove_dir_all(root); + } + #[test] fn local_host_timeout_prevents_waiting_invocation_from_reusing_host() { let (root, plan) = local_host_plan("invoke_timeout"); diff --git a/crates/fabric-core/src/schema.rs b/crates/fabric-core/src/schema.rs index 46824a6a7..3bb2ac261 100644 --- a/crates/fabric-core/src/schema.rs +++ b/crates/fabric-core/src/schema.rs @@ -212,6 +212,36 @@ mod tests { } } + #[test] + fn agent_schema_enforces_positive_invocation_limits() { + let schema = generate_schema(SchemaName::Agent).expect("schema generation"); + + assert_eq!( + schema["$defs"]["InstructionConfig"]["properties"]["content"]["minLength"], + 1 + ); + assert_eq!( + schema["$defs"]["InstructionConfig"]["properties"]["content"]["pattern"], + r"\S" + ); + assert_eq!( + schema["$defs"]["EnvironmentConfig"]["properties"]["env"]["propertyNames"]["pattern"], + r"\S" + ); + assert_eq!( + schema["$defs"]["RuntimeConfig"]["properties"]["max_turns"]["minimum"], + 1 + ); + assert_eq!( + schema["$defs"]["RuntimeConfig"]["properties"]["max_turns"]["maximum"], + u32::MAX + ); + assert_eq!( + schema["$defs"]["RuntimeConfig"]["properties"]["timeout_seconds"]["exclusiveMinimum"], + 0.0 + ); + } + #[test] fn adapter_descriptor_schema_rejects_empty_identifiers() { let schema = generate_schema(SchemaName::AdapterDescriptor).expect("schema generation"); diff --git a/crates/fabric-python/src/lib.rs b/crates/fabric-python/src/lib.rs index 0cdc81fcf..975dfff37 100644 --- a/crates/fabric-python/src/lib.rs +++ b/crates/fabric-python/src/lib.rs @@ -11,6 +11,7 @@ use std::time::{Duration, Instant}; use nemo_fabric_core::{ FabricConfig, ResolveContext, RunPlan, RunRequest, RuntimeHandle, doctor_plan, + resolve_diagnostic_plan_from_config_with_adapter_directories, resolve_run_plan_from_config_with_adapter_directories, run_plan, }; use pyo3::exceptions::PyRuntimeError; @@ -66,7 +67,7 @@ fn doctor_config( let (context, adapter_directories) = resolve_context(py, base_dir, &config)?; let plan = py .detach(|| { - resolve_run_plan_from_config_with_adapter_directories( + resolve_diagnostic_plan_from_config_with_adapter_directories( config, context, &adapter_directories, diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index ae5bbf230..3b69350cf 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -34,16 +34,22 @@ from nemo_fabric import ( HarnessConfig, MetadataConfig, ModelConfig, + RuntimeConfig, ) config = FabricConfig( metadata=MetadataConfig(name="quickstart-agent"), - harness=HarnessConfig(adapter_id="nvidia.fabric.hermes"), + harness=HarnessConfig( + adapter_id="nvidia.fabric.hermes", + resolution="preinstalled", + ), + runtime=RuntimeConfig(max_turns=1), models={ "default": ModelConfig( provider="nvidia", model="nvidia/nemotron-3-nano-30b-a3b", api_key_env="NVIDIA_API_KEY", + base_url="https://integrate.api.nvidia.com/v1", ) }, ) diff --git a/docs/integrations/harness/claude.mdx b/docs/integrations/harness/claude.mdx index 58f4a411b..9a0ea1981 100644 --- a/docs/integrations/harness/claude.mdx +++ b/docs/integrations/harness/claude.mdx @@ -8,11 +8,10 @@ SPDX-License-Identifier: Apache-2.0 */} The `nvidia.fabric.claude` adapter uses the Claude Agent SDK and its bundled Claude Code runtime. NeMo Fabric preserves Claude's native credential precedence and forwards only supported operating-system, configuration, and authentication -variables plus values explicitly configured in `harness.settings.env`. +variables plus values explicitly configured in `environment.env`. -The adapter pins `claude-agent-sdk==0.2.120`. The SDK owns its compatible Claude -Code runtime unless `harness.settings.cli_path` explicitly selects another -executable. +The adapter pins `claude-agent-sdk==0.2.120`. The SDK owns and selects its +compatible Claude Code runtime. ## Install the Adapter @@ -33,9 +32,9 @@ harness = HarnessConfig(adapter_id="nvidia.fabric.claude") ``` Use normalized `FabricConfig` fields to configure the model, workspace, skills, -MCP servers, blocked tools, and telemetry. Use `harness.settings` for supported -Claude-specific options such as `system_prompt`, `allowed_tools`, -`permission_mode`, `max_turns`, and `max_budget_usd`. +MCP servers, instructions, turn limit, tool policy, and telemetry. Use +`harness.settings` for supported Claude-specific options such as +`allowed_tools`, `permission_mode`, `max_budget_usd`, and `setting_sources`. ## Choose an Authentication Mode @@ -75,6 +74,14 @@ export ANTHROPIC_API_KEY=sk-ant-api03-example The adapter also forwards the selected model's `api_key_env` when the model configuration names a different environment variable. +## Use a Compatible Custom Provider + +Use `provider="anthropic"` for Claude's native authentication and endpoint +discovery. For another provider name, set both `api_key_env` and `base_url`. +The configured endpoint must implement the Anthropic Messages protocol. NeMo +Fabric maps the named credential and endpoint into Claude Code's environment; +it does not maintain a provider-name allowlist or infer provider endpoints. + ## Use Workload Identity Federation For a named WIF profile, set the profile and optional nondefault configuration @@ -110,7 +117,8 @@ variables take precedence over federation even when their value is empty. NeMo Relay does not authenticate Claude. A Relay-enabled NeMo Fabric runtime starts one gateway as a supervised sidecar, sets `ANTHROPIC_BASE_URL` for the -Claude runtime, and reuses the gateway across ordered invocations. +Claude runtime, passes an explicit selected model endpoint to the gateway as its +Anthropic upstream, and reuses the gateway across ordered invocations. `Fabric.run(...)` starts the same runtime, invokes it once, and stops it, so the gateway is scoped to that single invocation. Claude still resolves its credential through the selected mode, and NeMo Fabric does not write authentication values @@ -118,5 +126,5 @@ to Relay configuration or artifacts. NeMo Fabric supports the external NeMo Relay CLI from `0.6.0` up to, but not including, `0.7.0`. The Python package named `nemo-relay` does not install this -CLI. NeMo Fabric owns sidecar supervision and Claude configuration; Relay owns the -gateway transport and semantic observability pipeline. +CLI. NeMo Fabric owns sidecar supervision, Claude configuration, and upstream +selection. Relay owns the gateway transport and semantic observability pipeline. diff --git a/docs/integrations/harness/codex.mdx b/docs/integrations/harness/codex.mdx index e73e360f8..0ab031770 100644 --- a/docs/integrations/harness/codex.mdx +++ b/docs/integrations/harness/codex.mdx @@ -30,9 +30,9 @@ harness = HarnessConfig(adapter_id="nvidia.fabric.codex") Use normalized `FabricConfig` fields to configure the model, workspace, skills, MCP servers, and telemetry. The Codex adapter does not support normalized -`tools.blocked` policy. Use `harness.settings` for supported Codex-specific -options such as `sandbox`, `approval_mode`, `base_instructions`, -`developer_instructions`, and `output_schema`. +`tools.enabled` or `tools.blocked` policy. Use `harness.settings` for supported Codex-specific +options such as `sandbox`, `approval_mode`, `developer_instructions`, and +`output_schema`. Configure base instructions through `instructions.system`. ## Configure MCP and Skills @@ -70,32 +70,18 @@ and cannot vary between `Runtime.invoke(...)` calls. Start a new runtime to change them. `Fabric.run(...)` starts the same runtime, invokes it once, and stops it, so the same settings are scoped to that single invocation. -The Codex adapter does not declare `tools.blocked` support. Codex can filter +The Codex adapter does not declare `tools.enabled` or `tools.blocked` support. Codex can filter individual MCP server tools, but the pinned runtime does not provide one deny mechanism that covers built-in, local, MCP, and hosted tools. NeMo Fabric reports normalized blocked-tool policy as unsupported instead of enforcing only part of the requested policy. -## Select a Codex Runtime +## Codex Runtime Ownership The Codex SDK installs and selects its matching app-server runtime. NeMo Fabric does not declare or select the runtime package separately. A `codex` command on `PATH` does not replace the SDK-owned runtime. -To override the SDK-selected app-server intentionally, set an absolute path or -a path relative to the NeMo Fabric config root in `harness.settings.codex_bin`: - -```python -from examples.code_review_agent import codex_config - -config = codex_config() -config.harness.settings["codex_bin"] = "/path/to/codex" -``` - -NeMo Fabric passes this path to `CodexConfig.codex_bin`; it does not invoke the -command as a CLI adapter. Pin the override in reproducible environments because -the app-server protocol can change before a matching Python SDK is published. - ## Choose an Authentication Mode Codex supports the following OpenAI authentication modes for local work: @@ -138,20 +124,25 @@ the current real-agent acceptance path validates cached Codex authentication. Treat `CODEX_HOME/auth.json` as a secret when Codex uses file-based credential storage. Do not commit or copy it into NeMo Fabric configuration or artifacts. +## Use a Compatible Custom Provider + +Use `provider="openai"` for Codex's native authentication and endpoint +discovery. For another provider name, set both `api_key_env` and `base_url`. +The configured endpoint must implement the OpenAI Responses protocol. NeMo +Fabric defines a runtime-scoped Codex model provider with the configured name; +it does not maintain a provider-name allowlist or reuse the native Codex login +for that provider. + ## Use Authentication with Relay -NeMo Relay does not replace OpenAI authentication. A Relay-enabled NeMo Fabric -runtime starts one gateway as a supervised sidecar and directs the Codex SDK's -built-in OpenAI provider through that gateway. The runtime reuses the gateway -and SDK client across ordered invocations. `Fabric.run(...)` starts the same -runtime, invokes it once, and stops it, so the gateway is scoped to that single -invocation. The SDK still obtains credentials from the selected Codex -authentication mode. - -Relay-enabled Codex runs require `models.default.provider` to be `openai`. -The custom `nvidia` provider uses its configured NVIDIA Responses endpoint and -does not support the built-in provider redirect that Relay requires. Use the -`nvidia` provider without Relay. +NeMo Relay does not replace model-provider authentication. A Relay-enabled NeMo +Fabric runtime starts one gateway as a supervised sidecar and directs the +selected Responses-compatible provider through it. For a configured custom +provider, NeMo Fabric passes its explicit `base_url` to the gateway as the +OpenAI-compatible upstream. The runtime reuses the gateway and SDK client across +ordered invocations. `Fabric.run(...)` starts the same runtime, invokes it once, +and stops it, so the gateway is scoped to that single invocation. The SDK still +obtains credentials from the selected provider configuration. NeMo Fabric supplies runtime-scoped Relay configuration to the SDK. It does not copy the Codex credential store into Relay configuration or persist credentials diff --git a/docs/integrations/harness/hermes.mdx b/docs/integrations/harness/hermes.mdx index 2f079f4b8..0a4564e21 100644 --- a/docs/integrations/harness/hermes.mdx +++ b/docs/integrations/harness/hermes.mdx @@ -32,9 +32,9 @@ harness = HarnessConfig(adapter_id="nvidia.fabric.hermes") ``` Use normalized `FabricConfig` fields to configure the model, workspace, skills, -MCP servers, blocked toolsets, and telemetry. Use `harness.settings` only for -Hermes Agent-specific options such as `hermes_home` or -`disabled_toolsets`. +MCP servers, instructions, turn limit, native tool selectors, and telemetry. +Use `harness.settings` only for Hermes Agent-specific options such as terminal +timeout or reasoning configuration. ## Understand the Runtime Lifecycle diff --git a/docs/reference/api/python-library-reference/index.md b/docs/reference/api/python-library-reference/index.md index 9e92bd916..f7a7cf7b2 100644 --- a/docs/reference/api/python-library-reference/index.md +++ b/docs/reference/api/python-library-reference/index.md @@ -10,10 +10,10 @@ SPDX-License-Identifier: Apache-2.0 --> ## Modules -- [`nemo_fabric.client`](./nemo_fabric.client.md#module-nemo_fabricclient): Native Python client for resolving and running NeMo Fabric agents. +- [`nemo_fabric.client`](./nemo_fabric.client.md#module-nemo_fabricclient): Native Python client for resolving and running NVIDIA NeMo Fabric agents. - [`nemo_fabric.runtime`](./nemo_fabric.runtime.md#module-nemo_fabricruntime): Runtime lifecycle support for the NVIDIA NeMo Fabric Python SDK. - [`nemo_fabric.streaming`](./nemo_fabric.streaming.md#module-nemo_fabricstreaming): NVIDIA NeMo Relay streaming support for the NVIDIA NeMo Fabric Python SDK. -- [`nemo_fabric.models`](./nemo_fabric.models.md#module-nemo_fabricmodels): Pydantic SDK models for NeMo Fabric configuration and requests. +- [`nemo_fabric.models`](./nemo_fabric.models.md#module-nemo_fabricmodels): Pydantic SDK models for NVIDIA NeMo Fabric configuration and requests. - [`nemo_fabric.types`](./nemo_fabric.types.md#module-nemo_fabrictypes): Public data contracts for the NeMo Fabric Python SDK. - [`nemo_fabric.errors`](./nemo_fabric.errors.md#module-nemo_fabricerrors): Public exception hierarchy for the NeMo Fabric Python SDK. @@ -27,10 +27,12 @@ SPDX-License-Identifier: Apache-2.0 --> - [`models.FabricBaseModel`](./nemo_fabric.models.md#class-fabricbasemodel): Base class for SDK-facing Pydantic models. - [`models.FabricConfig`](./nemo_fabric.models.md#class-fabricconfig): SDK-facing typed NeMo Fabric agent configuration. - [`models.HarnessConfig`](./nemo_fabric.models.md#class-harnessconfig): Harness adapter selection plus adapter-owned settings. +- [`models.InstructionConfig`](./nemo_fabric.models.md#class-instructionconfig): One portable instruction value. +- [`models.InstructionsConfig`](./nemo_fabric.models.md#class-instructionsconfig): Harness-neutral agent instructions. - [`models.McpConfig`](./nemo_fabric.models.md#class-mcpconfig): MCP capability configuration. - [`models.McpServerConfig`](./nemo_fabric.models.md#class-mcpserverconfig): MCP server configuration. - [`models.MetadataConfig`](./nemo_fabric.models.md#class-metadataconfig): Human-readable agent identity. -- [`models.ModelConfig`](./nemo_fabric.models.md#class-modelconfig): Model alias configuration. +- [`models.ModelConfig`](./nemo_fabric.models.md#class-modelconfig): Configuration for one model role. - [`models.RelayAtifConfig`](./nemo_fabric.models.md#class-relayatifconfig): NeMo Relay ATIF export configuration. - [`models.RelayAtofConfig`](./nemo_fabric.models.md#class-relayatofconfig): NeMo Relay ATOF export configuration. - [`models.RelayAtofFileSinkConfig`](./nemo_fabric.models.md#class-relayatoffilesinkconfig): NeMo Relay ATOF file sink configuration. @@ -43,7 +45,7 @@ SPDX-License-Identifier: Apache-2.0 --> - [`models.RelayOtlpConfig`](./nemo_fabric.models.md#class-relayotlpconfig): NeMo Relay OTLP export configuration for OpenTelemetry/OpenInference. - [`models.RelayS3StorageConfig`](./nemo_fabric.models.md#class-relays3storageconfig): NeMo Relay ATIF S3 storage configuration. - [`models.RunRequest`](./nemo_fabric.models.md#class-runrequest): One validated NeMo Fabric invocation request. -- [`models.RuntimeConfig`](./nemo_fabric.models.md#class-runtimeconfig): Runtime input/output contract. +- [`models.RuntimeConfig`](./nemo_fabric.models.md#class-runtimeconfig): Invocation runtime contract. - [`models.SkillConfig`](./nemo_fabric.models.md#class-skillconfig): Skill capability configuration. - [`models.TelemetryConfig`](./nemo_fabric.models.md#class-telemetryconfig): Telemetry configuration. - [`models.TelemetryProviderConfig`](./nemo_fabric.models.md#class-telemetryproviderconfig): Provider-specific telemetry configuration. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.client.md b/docs/reference/api/python-library-reference/nemo_fabric.client.md index 725aa426b..1cd9dd8b4 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.client.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.client.md @@ -8,7 +8,7 @@ SPDX-License-Identifier: Apache-2.0 --> # module `nemo_fabric.client` -Native Python client for resolving and running NeMo Fabric agents. +Native Python client for resolving and running NVIDIA NeMo Fabric agents. diff --git a/docs/reference/api/python-library-reference/nemo_fabric.models.md b/docs/reference/api/python-library-reference/nemo_fabric.models.md index d6ea0991c..d0408bd34 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.models.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.models.md @@ -8,7 +8,7 @@ SPDX-License-Identifier: Apache-2.0 --> # module `nemo_fabric.models` -Pydantic SDK models for NeMo Fabric configuration and requests. +Pydantic SDK models for NVIDIA NeMo Fabric configuration and requests. The Rust core remains the source of truth for persisted schema snapshots. These models provide the Python SDK's typed authoring surface and intentionally keep extension fields so consumers can carry adapter- or application-owned data without waiting for a schema release. @@ -190,6 +190,147 @@ Returns the set of fields that have been explicitly set on this model instance. +**Returns:** + A set of strings representing the fields that have been set, i.e. that were not filled from defaults. + + + +--- + + +### classmethod `from_mapping` + +```python +def from_mapping(value: Mapping[str, Any]) -> Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +def to_mapping() -> dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `InstructionConfig` + +One portable instruction value. + + + +### Fields + +The model defines the following fields: + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `content` | `str` | Yes | — | `MinLen(min_length=1), _PydanticGeneralMetadata(pattern='\\S')` | — | +| `mode` | `Literal['replace']` | No | `'replace'` | — | — | + +--- + +### property extra_fields + +Return fields preserved by the extension point for this model. + +--- + +### property model_extra + +Get extra fields set during validation. + + + +**Returns:** + A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. + +--- + +### property model_fields_set + +Returns the set of fields that have been explicitly set on this model instance. + + + +**Returns:** + A set of strings representing the fields that have been set, i.e. that were not filled from defaults. + + + +--- + + +### classmethod `from_mapping` + +```python +def from_mapping(value: Mapping[str, Any]) -> Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +def to_mapping() -> dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `InstructionsConfig` + +Harness-neutral agent instructions. + + + +### Fields + +The model defines the following fields: + +| Field | Type | Required | Default | Constraints | Description | +| --- | --- | --- | --- | --- | --- | +| `system` | `InstructionConfig \| None` | No | `None` | — | — | + +--- + +### property extra_fields + +Return fields preserved by the extension point for this model. + +--- + +### property model_extra + +Get extra fields set during validation. + + + +**Returns:** + A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`. + +--- + +### property model_fields_set + +Returns the set of fields that have been explicitly set on this model instance. + + + **Returns:** A set of strings representing the fields that have been set, i.e. that were not filled from defaults. @@ -223,7 +364,7 @@ Return a detached JSON-compatible mapping for Rust/core calls. ## class `RuntimeConfig` -Runtime input/output contract. +Invocation runtime contract. @@ -236,6 +377,8 @@ The model defines the following fields: | `input_schema` | `str \| None` | No | `None` | — | — | | `output_schema` | `str \| None` | No | `None` | — | — | | `artifacts` | `str \| Path \| None` | No | `None` | — | — | +| `timeout_seconds` | `float \| None` | No | `None` | `Gt(gt=0), _PydanticGeneralMetadata(allow_inf_nan=False)` | — | +| `max_turns` | `int \| None` | No | `None` | `Gt(gt=0), Le(le=4294967295)` | — | --- @@ -310,6 +453,7 @@ The model defines the following fields: | `provider` | `str` | No | `'local'` | `MinLen(min_length=1)` | Environment provider, such as local, docker, opensandbox, or k8s. | | `workspace` | `str \| Path \| None` | No | `None` | — | Workspace path visible to the harness. | | `artifacts` | `str \| Path \| None` | No | `None` | — | Environment-specific artifact path. | +| `env` | `dict[str, str]` | No | `dict()` | — | Environment variables visible to the harness and its tools. Values are serialized into configuration and run plans; prefer api_key_env-style environment-variable-name indirection for credentials. | | `settings` | `dict[str, Any]` | No | `dict()` | — | Provider-specific configuration interpreted by the environment provider. | | `metadata` | `dict[str, Any]` | No | `dict()` | — | Consumer-owned environment metadata passed through without NeMo Fabric semantics. | | `connection` | `dict[str, Any]` | No | `dict()` | — | Connection data for an existing environment, such as URL, namespace, or credential reference. | @@ -374,7 +518,7 @@ Return a detached JSON-compatible mapping for Rust/core calls. ## class `ModelConfig` -Model alias configuration. +Configuration for one model role. @@ -388,6 +532,7 @@ The model defines the following fields: | `model` | `str` | Yes | — | `MinLen(min_length=1)` | — | | `api_key_env` | `str \| None` | No | `None` | — | — | | `temperature` | `float \| None` | No | `None` | — | — | +| `base_url` | `str \| None` | No | `None` | `MinLen(min_length=1)` | — | | `settings` | `dict[str, Any]` | No | `dict()` | — | — | --- @@ -1717,7 +1862,8 @@ The model defines the following fields: | Field | Type | Required | Default | Constraints | Description | | --- | --- | --- | --- | --- | --- | -| `blocked` | `list[str]` | No | `list()` | — | — | +| `enabled` | `list[str] \| None` | No | `None` | — | Adapter-native tools to expose. None preserves the harness default; an empty list exposes no tools. | +| `blocked` | `list[str]` | No | `list()` | — | Adapter-native tool names to deny. | --- @@ -1779,6 +1925,8 @@ Return a detached JSON-compatible mapping for Rust/core calls. SDK-facing typed NeMo Fabric agent configuration. +NeMo Fabric-owned fields apply uniformly. Adapter-translated fields are checked against the selected descriptor; refer to the [normalized configuration compatibility table](../../../sdk/python.mdx#normalized-configuration-compatibility). + ### Fields @@ -1792,12 +1940,13 @@ The model defines the following fields: | `harness` | `HarnessConfig` | Yes | — | — | — | | `runtime` | `RuntimeConfig` | No | `RuntimeConfig()` | — | — | | `environment` | `EnvironmentConfig \| None` | No | `None` | — | — | -| `models` | `dict[str, ModelConfig \| dict[str, Any]]` | No | `dict()` | — | — | +| `models` | `dict[str, ModelConfig]` | No | `dict()` | — | — | +| `instructions` | `InstructionsConfig \| None` | No | `None` | — | — | | `mcp` | `McpConfig \| None` | No | `None` | — | — | | `skills` | `SkillConfig \| None` | No | `None` | — | — | | `telemetry` | `TelemetryConfig \| None` | No | `None` | — | — | | `relay` | `RelayConfig \| dict[str, Any] \| None` | No | `None` | — | — | -| `tools` | `ToolsConfig \| dict[str, Any] \| None` | No | `None` | — | — | +| `tools` | `ToolsConfig \| None` | No | `None` | — | — | --- @@ -1867,7 +2016,7 @@ Add a skill path and return this config. def block_tools(*tools: str) -> Self ``` -Block adapter-native tool names or toolsets and return this config. +Block adapter-native tool names and return this config. --- diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/constant-adapter-contract-version.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/constant-adapter-contract-version.mdx index a960829f4..8b6722551 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/constant-adapter-contract-version.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/constant-adapter-contract-version.mdx @@ -9,6 +9,6 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
str = \"fabric.adapter/v1alpha1\";"}} />
+
str = \"fabric.adapter/v1alpha1\";"}} />
Adapter descriptor contract version supported by this core. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterconfigfield.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterconfigfield.mdx new file mode 100644 index 000000000..76013daf7 --- /dev/null +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterconfigfield.mdx @@ -0,0 +1,220 @@ +--- +title: "Enum Adapter Config Field" +sidebar-title: "AdapterConfigField" +description: "Adapter-translated normalized NVIDIA NeMo Fabric configuration fields." +position: 2 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p nemo-fabric-core`. + +```rust +pub enum AdapterConfigField { + Models, + ModelBaseUrl, + ModelTemperature, + SystemInstructions, + MaxTurns, + EnabledTools, + BlockedTools, + Mcp, + Skills, +} +``` + +Adapter-translated normalized NVIDIA NeMo Fabric configuration fields. + +## Variants + +### `Models` + +
+ +Normalized model selection and credentials. + +### `ModelBaseUrl` + +
+ +Custom model endpoint. + +### `ModelTemperature` + +
+ +Model temperature. + +### `SystemInstructions` + +
+ +Portable system instructions. + +### `MaxTurns` + +
+ +Per-invocation harness turn limit. + +### `EnabledTools` + +
+ +Adapter-native tool names to expose. + +### `BlockedTools` + +
+ +Adapter-native tool names to block. + +### `Mcp` + +
+ +Harness-native MCP servers. + +### `Skills` + +
+ +Harness-native skills. + +## Trait Implementations + +### `impl Clone for AdapterConfigField` + +
Clone for AdapterConfigField"}} />
+ +#### `clone` + +
clone(&self) -> AdapterConfigField"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for AdapterConfigField` + +
Debug for AdapterConfigField"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl<'de> Deserialize<'de> for AdapterConfigField` + +
Deserialize<'de> for AdapterConfigField"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl Hash for AdapterConfigField` + +
Hash for AdapterConfigField"}} />
+ +#### `hash` + +
hash<__H: Hasher>(&self, state: &mut __H)"}} />
+ +#### `hash_slice` + +
hash_slice<H>(data: &[Self], state: &mut H)where\n    H: Hasher,\n    Self: Sized,"}} />
+ +### `impl JsonSchema for AdapterConfigField` + +
AdapterConfigField"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl Ord for AdapterConfigField` + +
Ord for AdapterConfigField"}} />
+ +#### `cmp` + +
cmp(&self, other: &AdapterConfigField) -> Ordering"}} />
+ +#### `max` + +
max(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
+ +#### `min` + +
min(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
+ +#### `clamp` + +
clamp(self, min: Self, max: Self) -> Selfwhere\n    Self: Sized,"}} />
+ +### `impl PartialEq for AdapterConfigField` + +
PartialEq for AdapterConfigField"}} />
+ +#### `eq` + +
eq(&self, other: &AdapterConfigField) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl PartialOrd for AdapterConfigField` + +
PartialOrd for AdapterConfigField"}} />
+ +#### `partial_cmp` + +
partial_cmp(&self, other: &AdapterConfigField) -> Option<Ordering>"}} />
+ +#### `lt` + +
lt(&self, other: &Rhs) -> bool"}} />
+ +#### `le` + +
le(&self, other: &Rhs) -> bool"}} />
+ +#### `gt` + +
gt(&self, other: &Rhs) -> bool"}} />
+ +#### `ge` + +
ge(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for AdapterConfigField` + +
Serialize for AdapterConfigField"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for AdapterConfigField` + +
Copy for AdapterConfigField"}} />
+ +### `impl Eq for AdapterConfigField` + +
Eq for AdapterConfigField"}} />
+ +### `impl StructuralPartialEq for AdapterConfigField` + +
StructuralPartialEq for AdapterConfigField"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterdescriptorsource.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterdescriptorsource.mdx index e371f0a16..d779d6f24 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterdescriptorsource.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterdescriptorsource.mdx @@ -2,7 +2,7 @@ title: "Enum Adapter Descriptor Source" sidebar-title: "AdapterDescriptorSource" description: "Where NeMo Fabric resolved an adapter descriptor from." -position: 4 +position: 5 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,23 +36,23 @@ Descriptor registered by the agent package or local development config. ### `impl Clone for AdapterDescriptorSource` -
Clone for AdapterDescriptorSource"}} />
+
Clone for AdapterDescriptorSource"}} />
#### `clone` -
clone(&self) -> AdapterDescriptorSource"}} />
+
clone(&self) -> AdapterDescriptorSource"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterDescriptorSource` -
Debug for AdapterDescriptorSource"}} />
+
Debug for AdapterDescriptorSource"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for AdapterDescriptorSource` @@ -60,7 +60,7 @@ Descriptor registered by the agent package or local development config. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterDescriptorSource` @@ -68,11 +68,11 @@ Descriptor registered by the agent package or local development config. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -80,19 +80,19 @@ Descriptor registered by the agent package or local development config. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterDescriptorSource` -
PartialEq for AdapterDescriptorSource"}} />
+
PartialEq for AdapterDescriptorSource"}} />
#### `eq` -
eq(&self, other: &AdapterDescriptorSource) -> bool"}} />
+
eq(&self, other: &AdapterDescriptorSource) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterDescriptorSource` @@ -100,16 +100,16 @@ Descriptor registered by the agent package or local development config. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for AdapterDescriptorSource` -
Copy for AdapterDescriptorSource"}} />
+
Copy for AdapterDescriptorSource"}} />
### `impl Eq for AdapterDescriptorSource` -
Eq for AdapterDescriptorSource"}} />
+
Eq for AdapterDescriptorSource"}} />
### `impl StructuralPartialEq for AdapterDescriptorSource` -
StructuralPartialEq for AdapterDescriptorSource"}} />
+
StructuralPartialEq for AdapterDescriptorSource"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterkind.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterkind.mdx index ee6ab0b8a..53b29aa98 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterkind.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-adapterkind.mdx @@ -2,7 +2,7 @@ title: "Enum Adapter Kind" sidebar-title: "AdapterKind" description: "Adapter implementation kind." -position: 5 +position: 6 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -50,23 +50,23 @@ Delegate to a harness-native plugin package. ### `impl Clone for AdapterKind` -
Clone for AdapterKind"}} />
+
Clone for AdapterKind"}} />
#### `clone` -
clone(&self) -> AdapterKind"}} />
+
clone(&self) -> AdapterKind"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterKind` -
Debug for AdapterKind"}} />
+
Debug for AdapterKind"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for AdapterKind` @@ -74,7 +74,7 @@ Delegate to a harness-native plugin package. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterKind` @@ -82,11 +82,11 @@ Delegate to a harness-native plugin package. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -94,19 +94,19 @@ Delegate to a harness-native plugin package. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterKind` -
PartialEq for AdapterKind"}} />
+
PartialEq for AdapterKind"}} />
#### `eq` -
eq(&self, other: &AdapterKind) -> bool"}} />
+
eq(&self, other: &AdapterKind) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterKind` @@ -114,16 +114,16 @@ Delegate to a harness-native plugin package. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for AdapterKind` -
Copy for AdapterKind"}} />
+
Copy for AdapterKind"}} />
### `impl Eq for AdapterKind` -
Eq for AdapterKind"}} />
+
Eq for AdapterKind"}} />
### `impl StructuralPartialEq for AdapterKind` -
StructuralPartialEq for AdapterKind"}} />
+
StructuralPartialEq for AdapterKind"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx index 1c7ff8a99..d9c7fcc7d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitykind.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Kind" sidebar-title: "CapabilityKind" description: "Capability kind." -position: 38 +position: 41 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -43,23 +43,23 @@ MCP server. ### `impl Clone for CapabilityKind` -
Clone for CapabilityKind"}} />
+
Clone for CapabilityKind"}} />
#### `clone` -
clone(&self) -> CapabilityKind"}} />
+
clone(&self) -> CapabilityKind"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for CapabilityKind` -
Debug for CapabilityKind"}} />
+
Debug for CapabilityKind"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for CapabilityKind` @@ -67,7 +67,7 @@ MCP server. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for CapabilityKind` @@ -75,11 +75,11 @@ MCP server. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +87,19 @@ MCP server. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for CapabilityKind` -
PartialEq for CapabilityKind"}} />
+
PartialEq for CapabilityKind"}} />
#### `eq` -
eq(&self, other: &CapabilityKind) -> bool"}} />
+
eq(&self, other: &CapabilityKind) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for CapabilityKind` @@ -107,16 +107,16 @@ MCP server. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for CapabilityKind` -
Copy for CapabilityKind"}} />
+
Copy for CapabilityKind"}} />
### `impl Eq for CapabilityKind` -
Eq for CapabilityKind"}} />
+
Eq for CapabilityKind"}} />
### `impl StructuralPartialEq for CapabilityKind` -
StructuralPartialEq for CapabilityKind"}} />
+
StructuralPartialEq for CapabilityKind"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx index 8b2a233c1..234b7e510 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-capabilitytarget.mdx @@ -1,8 +1,8 @@ --- title: "Enum Capability Target" sidebar-title: "CapabilityTarget" -description: "Capability routing target." -position: 39 +description: "Component responsible for executing a configured capability." +position: 42 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -17,7 +17,9 @@ pub enum CapabilityTarget { } ``` -Capability routing target. +Component responsible for executing a configured capability. + +This target describes execution ownership, not network routing. ## Variants @@ -25,41 +27,41 @@ Capability routing target.
-Adapter maps the capability into harness-native config. +The selected adapter maps and executes the capability through its harness. ### `FabricManaged`
-NeMo Fabric exposes or manages the capability around the harness. +NeMo Fabric executes the capability outside the harness-native surface. ### `Unsupported`
-Capability is configured but no executable surface exists. +Neither the adapter nor NeMo Fabric can execute the configured capability. ## Trait Implementations ### `impl Clone for CapabilityTarget` -
Clone for CapabilityTarget"}} />
+
Clone for CapabilityTarget"}} />
#### `clone` -
clone(&self) -> CapabilityTarget"}} />
+
clone(&self) -> CapabilityTarget"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for CapabilityTarget` -
Debug for CapabilityTarget"}} />
+
Debug for CapabilityTarget"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for CapabilityTarget` @@ -67,7 +69,7 @@ Capability is configured but no executable surface exists. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for CapabilityTarget` @@ -75,11 +77,11 @@ Capability is configured but no executable surface exists. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +89,19 @@ Capability is configured but no executable surface exists. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for CapabilityTarget` -
PartialEq for CapabilityTarget"}} />
+
PartialEq for CapabilityTarget"}} />
#### `eq` -
eq(&self, other: &CapabilityTarget) -> bool"}} />
+
eq(&self, other: &CapabilityTarget) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for CapabilityTarget` @@ -107,16 +109,16 @@ Capability is configured but no executable surface exists. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for CapabilityTarget` -
Copy for CapabilityTarget"}} />
+
Copy for CapabilityTarget"}} />
### `impl Eq for CapabilityTarget` -
Eq for CapabilityTarget"}} />
+
Eq for CapabilityTarget"}} />
### `impl StructuralPartialEq for CapabilityTarget` -
StructuralPartialEq for CapabilityTarget"}} />
+
StructuralPartialEq for CapabilityTarget"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-controllocation.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-controllocation.mdx index 325d1a899..f18fab757 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-controllocation.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-controllocation.mdx @@ -2,7 +2,7 @@ title: "Enum Control Location" sidebar-title: "ControlLocation" description: "Where NeMo Fabric control code runs relative to the environment." -position: 10 +position: 11 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,23 +36,23 @@ NeMo Fabric runs inside the prepared environment with the harness. ### `impl Clone for ControlLocation` -
Clone for ControlLocation"}} />
+
Clone for ControlLocation"}} />
#### `clone` -
clone(&self) -> ControlLocation"}} />
+
clone(&self) -> ControlLocation"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ControlLocation` -
Debug for ControlLocation"}} />
+
Debug for ControlLocation"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ControlLocation` @@ -60,7 +60,7 @@ NeMo Fabric runs inside the prepared environment with the harness. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ControlLocation` @@ -68,11 +68,11 @@ NeMo Fabric runs inside the prepared environment with the harness. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -80,19 +80,19 @@ NeMo Fabric runs inside the prepared environment with the harness. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ControlLocation` -
PartialEq for ControlLocation"}} />
+
PartialEq for ControlLocation"}} />
#### `eq` -
eq(&self, other: &ControlLocation) -> bool"}} />
+
eq(&self, other: &ControlLocation) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ControlLocation` @@ -100,16 +100,16 @@ NeMo Fabric runs inside the prepared environment with the harness. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for ControlLocation` -
Copy for ControlLocation"}} />
+
Copy for ControlLocation"}} />
### `impl Eq for ControlLocation` -
Eq for ControlLocation"}} />
+
Eq for ControlLocation"}} />
### `impl StructuralPartialEq for ControlLocation` -
StructuralPartialEq for ControlLocation"}} />
+
StructuralPartialEq for ControlLocation"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx index f00de5b8e..a69a56dbf 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-environmentownership.mdx @@ -2,7 +2,7 @@ title: "Enum Environment Ownership" sidebar-title: "EnvironmentOwnership" description: "Whether NeMo Fabric owns the underlying environment resource." -position: 12 +position: 13 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,23 +36,23 @@ NeMo Fabric created or leased the environment resource and may release it. ### `impl Clone for EnvironmentOwnership` -
Clone for EnvironmentOwnership"}} />
+
Clone for EnvironmentOwnership"}} />
#### `clone` -
clone(&self) -> EnvironmentOwnership"}} />
+
clone(&self) -> EnvironmentOwnership"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for EnvironmentOwnership` -
Debug for EnvironmentOwnership"}} />
+
Debug for EnvironmentOwnership"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for EnvironmentOwnership` @@ -60,7 +60,7 @@ NeMo Fabric created or leased the environment resource and may release it. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for EnvironmentOwnership` @@ -68,11 +68,11 @@ NeMo Fabric created or leased the environment resource and may release it. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -80,19 +80,19 @@ NeMo Fabric created or leased the environment resource and may release it. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for EnvironmentOwnership` -
PartialEq for EnvironmentOwnership"}} />
+
PartialEq for EnvironmentOwnership"}} />
#### `eq` -
eq(&self, other: &EnvironmentOwnership) -> bool"}} />
+
eq(&self, other: &EnvironmentOwnership) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for EnvironmentOwnership` @@ -100,16 +100,16 @@ NeMo Fabric created or leased the environment resource and may release it. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for EnvironmentOwnership` -
Copy for EnvironmentOwnership"}} />
+
Copy for EnvironmentOwnership"}} />
### `impl Eq for EnvironmentOwnership` -
Eq for EnvironmentOwnership"}} />
+
Eq for EnvironmentOwnership"}} />
### `impl StructuralPartialEq for EnvironmentOwnership` -
StructuralPartialEq for EnvironmentOwnership"}} />
+
StructuralPartialEq for EnvironmentOwnership"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-instructionmode.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-instructionmode.mdx new file mode 100644 index 000000000..f5e94243f --- /dev/null +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-instructionmode.mdx @@ -0,0 +1,116 @@ +--- +title: "Enum Instruction Mode" +sidebar-title: "InstructionMode" +description: "How an instruction value is applied to the selected harness." +position: 18 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p nemo-fabric-core`. + +```rust +pub enum InstructionMode { + Replace, +} +``` + +How an instruction value is applied to the selected harness. + +## Variants + +### `Replace` + +
+ +Replace the harness default instruction value. + +## Trait Implementations + +### `impl Clone for InstructionMode` + +
Clone for InstructionMode"}} />
+ +#### `clone` + +
clone(&self) -> InstructionMode"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for InstructionMode` + +
Debug for InstructionMode"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for InstructionMode` + +
Default for InstructionMode"}} />
+ +#### `default` + +
default() -> InstructionMode"}} />
+ +### `impl<'de> Deserialize<'de> for InstructionMode` + +
Deserialize<'de> for InstructionMode"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for InstructionMode` + +
InstructionMode"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for InstructionMode` + +
PartialEq for InstructionMode"}} />
+ +#### `eq` + +
eq(&self, other: &InstructionMode) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for InstructionMode` + +
Serialize for InstructionMode"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for InstructionMode` + +
Copy for InstructionMode"}} />
+ +### `impl Eq for InstructionMode` + +
Eq for InstructionMode"}} />
+ +### `impl StructuralPartialEq for InstructionMode` + +
StructuralPartialEq for InstructionMode"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx index 7cc8b5feb..7c79c7a94 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-mcpexposure.mdx @@ -2,7 +2,7 @@ title: "Enum McpExposure" sidebar-title: "McpExposure" description: "MCP exposure strategy." -position: 17 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,23 +36,23 @@ NeMo Fabric manages MCP and exposes basic tools/actions. ### `impl Clone for McpExposure` -
Clone for McpExposure"}} />
+
Clone for McpExposure"}} />
#### `clone` -
clone(&self) -> McpExposure"}} />
+
clone(&self) -> McpExposure"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for McpExposure` -
Debug for McpExposure"}} />
+
Debug for McpExposure"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for McpExposure` @@ -60,7 +60,7 @@ NeMo Fabric manages MCP and exposes basic tools/actions. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for McpExposure` @@ -68,11 +68,11 @@ NeMo Fabric manages MCP and exposes basic tools/actions. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -80,19 +80,19 @@ NeMo Fabric manages MCP and exposes basic tools/actions. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for McpExposure` -
PartialEq for McpExposure"}} />
+
PartialEq for McpExposure"}} />
#### `eq` -
eq(&self, other: &McpExposure) -> bool"}} />
+
eq(&self, other: &McpExposure) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for McpExposure` @@ -100,16 +100,16 @@ NeMo Fabric manages MCP and exposes basic tools/actions. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for McpExposure` -
Copy for McpExposure"}} />
+
Copy for McpExposure"}} />
### `impl Eq for McpExposure` -
Eq for McpExposure"}} />
+
Eq for McpExposure"}} />
### `impl StructuralPartialEq for McpExposure` -
StructuralPartialEq for McpExposure"}} />
+
StructuralPartialEq for McpExposure"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx index 086061cb2..0be6dfb2a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatifstorageconfig.mdx @@ -2,14 +2,14 @@ title: "Enum Relay Atif Storage Config" sidebar-title: "RelayAtifStorageConfig" description: "Relay ATIF remote storage configuration." -position: 43 +position: 47 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n        headers: BTreeMap<String, String>,\n        header_env: BTreeMap<String, String>,\n        timeout_millis: u64,\n        extensions: BTreeMap<String, Value>,\n    },\n    S3 {\n        bucket: String,\n        key_prefix: Option<String>,\n        access_key_id: Option<String>,\n        secret_access_key_var: Option<String>,\n        session_token_var: Option<String>,\n        region: Option<String>,\n        endpoint_url: Option<String>,\n        allow_http: Option<bool>,\n        extensions: BTreeMap<String, Value>,\n    },\n}"}} />
+
String,\n        headers: BTreeMap<String, String>,\n        header_env: BTreeMap<String, String>,\n        timeout_millis: u64,\n        extensions: BTreeMap<String, Value>,\n    },\n    S3 {\n        bucket: String,\n        key_prefix: Option<String>,\n        access_key_id: Option<String>,\n        secret_access_key_var: Option<String>,\n        session_token_var: Option<String>,\n        region: Option<String>,\n        endpoint_url: Option<String>,\n        allow_http: Option<bool>,\n        extensions: BTreeMap<String, Value>,\n    },\n}"}} />
Relay ATIF remote storage configuration. @@ -91,23 +91,23 @@ Additive S3 storage fields. ### `impl Clone for RelayAtifStorageConfig` -
Clone for RelayAtifStorageConfig"}} />
+
Clone for RelayAtifStorageConfig"}} />
#### `clone` -
clone(&self) -> RelayAtifStorageConfig"}} />
+
clone(&self) -> RelayAtifStorageConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtifStorageConfig` -
Debug for RelayAtifStorageConfig"}} />
+
Debug for RelayAtifStorageConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RelayAtifStorageConfig` @@ -115,7 +115,7 @@ Additive S3 storage fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtifStorageConfig` @@ -123,11 +123,11 @@ Additive S3 storage fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -135,19 +135,19 @@ Additive S3 storage fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtifStorageConfig` -
PartialEq for RelayAtifStorageConfig"}} />
+
PartialEq for RelayAtifStorageConfig"}} />
#### `eq` -
eq(&self, other: &RelayAtifStorageConfig) -> bool"}} />
+
eq(&self, other: &RelayAtifStorageConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtifStorageConfig` @@ -155,8 +155,8 @@ Additive S3 storage fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayAtifStorageConfig` -
StructuralPartialEq for RelayAtifStorageConfig"}} />
+
StructuralPartialEq for RelayAtifStorageConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx index 6e0360147..9a040d0f4 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofmode.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Mode" sidebar-title: "RelayAtofMode" description: "Relay ATOF file mode." -position: 44 +position: 48 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,31 +36,31 @@ Overwrite an existing ATOF file. ### `impl Clone for RelayAtofMode` -
Clone for RelayAtofMode"}} />
+
Clone for RelayAtofMode"}} />
#### `clone` -
clone(&self) -> RelayAtofMode"}} />
+
clone(&self) -> RelayAtofMode"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtofMode` -
Debug for RelayAtofMode"}} />
+
Debug for RelayAtofMode"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayAtofMode` -
Default for RelayAtofMode"}} />
+
Default for RelayAtofMode"}} />
#### `default` -
default() -> RelayAtofMode"}} />
+
default() -> RelayAtofMode"}} />
### `impl<'de> Deserialize<'de> for RelayAtofMode` @@ -68,7 +68,7 @@ Overwrite an existing ATOF file. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtofMode` @@ -76,11 +76,11 @@ Overwrite an existing ATOF file. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -88,19 +88,19 @@ Overwrite an existing ATOF file. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtofMode` -
PartialEq for RelayAtofMode"}} />
+
PartialEq for RelayAtofMode"}} />
#### `eq` -
eq(&self, other: &RelayAtofMode) -> bool"}} />
+
eq(&self, other: &RelayAtofMode) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtofMode` @@ -108,16 +108,16 @@ Overwrite an existing ATOF file. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RelayAtofMode` -
Copy for RelayAtofMode"}} />
+
Copy for RelayAtofMode"}} />
### `impl Eq for RelayAtofMode` -
Eq for RelayAtofMode"}} />
+
Eq for RelayAtofMode"}} />
### `impl StructuralPartialEq for RelayAtofMode` -
StructuralPartialEq for RelayAtofMode"}} />
+
StructuralPartialEq for RelayAtofMode"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofsinkconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofsinkconfig.mdx index 888163684..58c34f63c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofsinkconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofsinkconfig.mdx @@ -2,14 +2,14 @@ title: "Enum Relay Atof Sink Config" sidebar-title: "RelayAtofSinkConfig" description: "Relay ATOF sink configuration." -position: 45 +position: 49 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Option<PathBuf>,\n        filename: Option<String>,\n        mode: RelayAtofMode,\n        extensions: BTreeMap<String, Value>,\n    },\n    Stream {\n        url: String,\n        transport: RelayAtofStreamTransport,\n        headers: BTreeMap<String, String>,\n        header_env: BTreeMap<String, String>,\n        timeout_millis: u64,\n        field_name_policy: RelayAtofStreamFieldNamePolicy,\n        name: Option<String>,\n        extensions: BTreeMap<String, Value>,\n    },\n}"}} />
+
Option<PathBuf>,\n        filename: Option<String>,\n        mode: RelayAtofMode,\n        extensions: BTreeMap<String, Value>,\n    },\n    Stream {\n        url: String,\n        transport: RelayAtofStreamTransport,\n        headers: BTreeMap<String, String>,\n        header_env: BTreeMap<String, String>,\n        timeout_millis: u64,\n        field_name_policy: RelayAtofStreamFieldNamePolicy,\n        name: Option<String>,\n        extensions: BTreeMap<String, Value>,\n    },\n}"}} />
Relay ATOF sink configuration. @@ -83,23 +83,23 @@ Additive stream sink fields. ### `impl Clone for RelayAtofSinkConfig` -
Clone for RelayAtofSinkConfig"}} />
+
Clone for RelayAtofSinkConfig"}} />
#### `clone` -
clone(&self) -> RelayAtofSinkConfig"}} />
+
clone(&self) -> RelayAtofSinkConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtofSinkConfig` -
Debug for RelayAtofSinkConfig"}} />
+
Debug for RelayAtofSinkConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RelayAtofSinkConfig` @@ -107,7 +107,7 @@ Additive stream sink fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtofSinkConfig` @@ -115,11 +115,11 @@ Additive stream sink fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -127,19 +127,19 @@ Additive stream sink fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtofSinkConfig` -
PartialEq for RelayAtofSinkConfig"}} />
+
PartialEq for RelayAtofSinkConfig"}} />
#### `eq` -
eq(&self, other: &RelayAtofSinkConfig) -> bool"}} />
+
eq(&self, other: &RelayAtofSinkConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtofSinkConfig` @@ -147,8 +147,8 @@ Additive stream sink fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayAtofSinkConfig` -
StructuralPartialEq for RelayAtofSinkConfig"}} />
+
StructuralPartialEq for RelayAtofSinkConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamfieldnamepolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamfieldnamepolicy.mdx index 2c5af3301..d384b634b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamfieldnamepolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamfieldnamepolicy.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Stream Field Name Policy" sidebar-title: "RelayAtofStreamFieldNamePolicy" description: "Relay ATOF stream field-name policy." -position: 46 +position: 50 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,31 +36,31 @@ Replace dots in field names. ### `impl Clone for RelayAtofStreamFieldNamePolicy` -
Clone for RelayAtofStreamFieldNamePolicy"}} />
+
Clone for RelayAtofStreamFieldNamePolicy"}} />
#### `clone` -
clone(&self) -> RelayAtofStreamFieldNamePolicy"}} />
+
clone(&self) -> RelayAtofStreamFieldNamePolicy"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtofStreamFieldNamePolicy` -
Debug for RelayAtofStreamFieldNamePolicy"}} />
+
Debug for RelayAtofStreamFieldNamePolicy"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayAtofStreamFieldNamePolicy` -
Default for RelayAtofStreamFieldNamePolicy"}} />
+
Default for RelayAtofStreamFieldNamePolicy"}} />
#### `default` -
default() -> RelayAtofStreamFieldNamePolicy"}} />
+
default() -> RelayAtofStreamFieldNamePolicy"}} />
### `impl<'de> Deserialize<'de> for RelayAtofStreamFieldNamePolicy` @@ -68,7 +68,7 @@ Replace dots in field names. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtofStreamFieldNamePolicy` @@ -76,11 +76,11 @@ Replace dots in field names. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -88,19 +88,19 @@ Replace dots in field names. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtofStreamFieldNamePolicy` -
PartialEq for RelayAtofStreamFieldNamePolicy"}} />
+
PartialEq for RelayAtofStreamFieldNamePolicy"}} />
#### `eq` -
eq(&self, other: &RelayAtofStreamFieldNamePolicy) -> bool"}} />
+
eq(&self, other: &RelayAtofStreamFieldNamePolicy) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtofStreamFieldNamePolicy` @@ -108,16 +108,16 @@ Replace dots in field names. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RelayAtofStreamFieldNamePolicy` -
Copy for RelayAtofStreamFieldNamePolicy"}} />
+
Copy for RelayAtofStreamFieldNamePolicy"}} />
### `impl Eq for RelayAtofStreamFieldNamePolicy` -
Eq for RelayAtofStreamFieldNamePolicy"}} />
+
Eq for RelayAtofStreamFieldNamePolicy"}} />
### `impl StructuralPartialEq for RelayAtofStreamFieldNamePolicy` -
StructuralPartialEq for RelayAtofStreamFieldNamePolicy"}} />
+
StructuralPartialEq for RelayAtofStreamFieldNamePolicy"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamtransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamtransport.mdx index 6da59a005..e81ca0d55 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamtransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayatofstreamtransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Stream Transport" sidebar-title: "RelayAtofStreamTransport" description: "Relay ATOF stream transport." -position: 47 +position: 51 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -43,31 +43,31 @@ NDJSON transport. ### `impl Clone for RelayAtofStreamTransport` -
Clone for RelayAtofStreamTransport"}} />
+
Clone for RelayAtofStreamTransport"}} />
#### `clone` -
clone(&self) -> RelayAtofStreamTransport"}} />
+
clone(&self) -> RelayAtofStreamTransport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtofStreamTransport` -
Debug for RelayAtofStreamTransport"}} />
+
Debug for RelayAtofStreamTransport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayAtofStreamTransport` -
Default for RelayAtofStreamTransport"}} />
+
Default for RelayAtofStreamTransport"}} />
#### `default` -
default() -> RelayAtofStreamTransport"}} />
+
default() -> RelayAtofStreamTransport"}} />
### `impl<'de> Deserialize<'de> for RelayAtofStreamTransport` @@ -75,7 +75,7 @@ NDJSON transport. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtofStreamTransport` @@ -83,11 +83,11 @@ NDJSON transport. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +95,19 @@ NDJSON transport. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtofStreamTransport` -
PartialEq for RelayAtofStreamTransport"}} />
+
PartialEq for RelayAtofStreamTransport"}} />
#### `eq` -
eq(&self, other: &RelayAtofStreamTransport) -> bool"}} />
+
eq(&self, other: &RelayAtofStreamTransport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtofStreamTransport` @@ -115,16 +115,16 @@ NDJSON transport. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RelayAtofStreamTransport` -
Copy for RelayAtofStreamTransport"}} />
+
Copy for RelayAtofStreamTransport"}} />
### `impl Eq for RelayAtofStreamTransport` -
Eq for RelayAtofStreamTransport"}} />
+
Eq for RelayAtofStreamTransport"}} />
### `impl StructuralPartialEq for RelayAtofStreamTransport` -
StructuralPartialEq for RelayAtofStreamTransport"}} />
+
StructuralPartialEq for RelayAtofStreamTransport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx index 4dac33fbf..4e3be35c2 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayotlptransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Otlp Transport" sidebar-title: "RelayOtlpTransport" description: "Relay OTLP transport." -position: 48 +position: 52 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -36,31 +36,31 @@ OTLP gRPC transport. ### `impl Clone for RelayOtlpTransport` -
Clone for RelayOtlpTransport"}} />
+
Clone for RelayOtlpTransport"}} />
#### `clone` -
clone(&self) -> RelayOtlpTransport"}} />
+
clone(&self) -> RelayOtlpTransport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayOtlpTransport` -
Debug for RelayOtlpTransport"}} />
+
Debug for RelayOtlpTransport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayOtlpTransport` -
Default for RelayOtlpTransport"}} />
+
Default for RelayOtlpTransport"}} />
#### `default` -
default() -> RelayOtlpTransport"}} />
+
default() -> RelayOtlpTransport"}} />
### `impl<'de> Deserialize<'de> for RelayOtlpTransport` @@ -68,7 +68,7 @@ OTLP gRPC transport. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayOtlpTransport` @@ -76,11 +76,11 @@ OTLP gRPC transport. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -88,19 +88,19 @@ OTLP gRPC transport. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayOtlpTransport` -
PartialEq for RelayOtlpTransport"}} />
+
PartialEq for RelayOtlpTransport"}} />
#### `eq` -
eq(&self, other: &RelayOtlpTransport) -> bool"}} />
+
eq(&self, other: &RelayOtlpTransport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayOtlpTransport` @@ -108,16 +108,16 @@ OTLP gRPC transport. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RelayOtlpTransport` -
Copy for RelayOtlpTransport"}} />
+
Copy for RelayOtlpTransport"}} />
### `impl Eq for RelayOtlpTransport` -
Eq for RelayOtlpTransport"}} />
+
Eq for RelayOtlpTransport"}} />
### `impl StructuralPartialEq for RelayOtlpTransport` -
StructuralPartialEq for RelayOtlpTransport"}} />
+
StructuralPartialEq for RelayOtlpTransport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx index 433a4ebb8..5b9b5b9a7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-relayunsupportedbehavior.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Unsupported Behavior" sidebar-title: "RelayUnsupportedBehavior" description: "Relay unsupported/unknown config handling." -position: 49 +position: 53 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -43,31 +43,31 @@ Error on the unsupported or unknown value. ### `impl Clone for RelayUnsupportedBehavior` -
Clone for RelayUnsupportedBehavior"}} />
+
Clone for RelayUnsupportedBehavior"}} />
#### `clone` -
clone(&self) -> RelayUnsupportedBehavior"}} />
+
clone(&self) -> RelayUnsupportedBehavior"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayUnsupportedBehavior` -
Debug for RelayUnsupportedBehavior"}} />
+
Debug for RelayUnsupportedBehavior"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayUnsupportedBehavior` -
Default for RelayUnsupportedBehavior"}} />
+
Default for RelayUnsupportedBehavior"}} />
#### `default` -
default() -> RelayUnsupportedBehavior"}} />
+
default() -> RelayUnsupportedBehavior"}} />
### `impl<'de> Deserialize<'de> for RelayUnsupportedBehavior` @@ -75,7 +75,7 @@ Error on the unsupported or unknown value. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayUnsupportedBehavior` @@ -83,11 +83,11 @@ Error on the unsupported or unknown value. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +95,19 @@ Error on the unsupported or unknown value. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayUnsupportedBehavior` -
PartialEq for RelayUnsupportedBehavior"}} />
+
PartialEq for RelayUnsupportedBehavior"}} />
#### `eq` -
eq(&self, other: &RelayUnsupportedBehavior) -> bool"}} />
+
eq(&self, other: &RelayUnsupportedBehavior) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayUnsupportedBehavior` @@ -115,16 +115,16 @@ Error on the unsupported or unknown value. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RelayUnsupportedBehavior` -
Copy for RelayUnsupportedBehavior"}} />
+
Copy for RelayUnsupportedBehavior"}} />
### `impl Eq for RelayUnsupportedBehavior` -
Eq for RelayUnsupportedBehavior"}} />
+
Eq for RelayUnsupportedBehavior"}} />
### `impl StructuralPartialEq for RelayUnsupportedBehavior` -
StructuralPartialEq for RelayUnsupportedBehavior"}} />
+
StructuralPartialEq for RelayUnsupportedBehavior"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx index 0de8929b3..fb85a702d 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-resolutionstrategy.mdx @@ -2,7 +2,7 @@ title: "Enum Resolution Strategy" sidebar-title: "ResolutionStrategy" description: "Adapter install or availability strategy." -position: 21 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -71,23 +71,23 @@ Adapter is installed through a harness-native plugin manager. ### `impl Clone for ResolutionStrategy` -
Clone for ResolutionStrategy"}} />
+
Clone for ResolutionStrategy"}} />
#### `clone` -
clone(&self) -> ResolutionStrategy"}} />
+
clone(&self) -> ResolutionStrategy"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ResolutionStrategy` -
Debug for ResolutionStrategy"}} />
+
Debug for ResolutionStrategy"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ResolutionStrategy` @@ -95,7 +95,7 @@ Adapter is installed through a harness-native plugin manager. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ResolutionStrategy` @@ -103,11 +103,11 @@ Adapter is installed through a harness-native plugin manager. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -115,19 +115,19 @@ Adapter is installed through a harness-native plugin manager. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ResolutionStrategy` -
PartialEq for ResolutionStrategy"}} />
+
PartialEq for ResolutionStrategy"}} />
#### `eq` -
eq(&self, other: &ResolutionStrategy) -> bool"}} />
+
eq(&self, other: &ResolutionStrategy) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ResolutionStrategy` @@ -135,16 +135,16 @@ Adapter is installed through a harness-native plugin manager. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for ResolutionStrategy` -
Copy for ResolutionStrategy"}} />
+
Copy for ResolutionStrategy"}} />
### `impl Eq for ResolutionStrategy` -
Eq for ResolutionStrategy"}} />
+
Eq for ResolutionStrategy"}} />
### `impl StructuralPartialEq for ResolutionStrategy` -
StructuralPartialEq for ResolutionStrategy"}} />
+
StructuralPartialEq for ResolutionStrategy"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx index c8669d6fc..27d869fcd 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/enum-telemetryprovider.mdx @@ -2,7 +2,7 @@ title: "Enum Telemetry Provider" sidebar-title: "TelemetryProvider" description: "Telemetry runtime provider." -position: 30 +position: 34 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -40,7 +40,7 @@ Let the selected adapter handle telemetry natively. #### `as_str` -
str"}} />
+
str"}} />
Return the stable configuration value for this provider. @@ -48,31 +48,31 @@ Return the stable configuration value for this provider. ### `impl Clone for TelemetryProvider` -
Clone for TelemetryProvider"}} />
+
Clone for TelemetryProvider"}} />
#### `clone` -
clone(&self) -> TelemetryProvider"}} />
+
clone(&self) -> TelemetryProvider"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for TelemetryProvider` -
Debug for TelemetryProvider"}} />
+
Debug for TelemetryProvider"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for TelemetryProvider` -
Default for TelemetryProvider"}} />
+
Default for TelemetryProvider"}} />
#### `default` -
default() -> TelemetryProvider"}} />
+
default() -> TelemetryProvider"}} />
### `impl<'de> Deserialize<'de> for TelemetryProvider` @@ -80,7 +80,7 @@ Return the stable configuration value for this provider. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for TelemetryProvider` @@ -88,11 +88,11 @@ Return the stable configuration value for this provider. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -100,63 +100,63 @@ Return the stable configuration value for this provider. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl Ord for TelemetryProvider` -
Ord for TelemetryProvider"}} />
+
Ord for TelemetryProvider"}} />
#### `cmp` -
cmp(&self, other: &TelemetryProvider) -> Ordering"}} />
+
cmp(&self, other: &TelemetryProvider) -> Ordering"}} />
#### `max` -
max(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
+
max(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
#### `min` -
min(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
+
min(self, other: Self) -> Selfwhere\n    Self: Sized,"}} />
#### `clamp` -
clamp(self, min: Self, max: Self) -> Selfwhere\n    Self: Sized,"}} />
+
clamp(self, min: Self, max: Self) -> Selfwhere\n    Self: Sized,"}} />
### `impl PartialEq for TelemetryProvider` -
PartialEq for TelemetryProvider"}} />
+
PartialEq for TelemetryProvider"}} />
#### `eq` -
eq(&self, other: &TelemetryProvider) -> bool"}} />
+
eq(&self, other: &TelemetryProvider) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl PartialOrd for TelemetryProvider` -
PartialOrd for TelemetryProvider"}} />
+
PartialOrd for TelemetryProvider"}} />
#### `partial_cmp` -
partial_cmp(&self, other: &TelemetryProvider) -> Option<Ordering>"}} />
+
partial_cmp(&self, other: &TelemetryProvider) -> Option<Ordering>"}} />
#### `lt` -
lt(&self, other: &Rhs) -> bool"}} />
+
lt(&self, other: &Rhs) -> bool"}} />
#### `le` -
le(&self, other: &Rhs) -> bool"}} />
+
le(&self, other: &Rhs) -> bool"}} />
#### `gt` -
gt(&self, other: &Rhs) -> bool"}} />
+
gt(&self, other: &Rhs) -> bool"}} />
#### `ge` -
ge(&self, other: &Rhs) -> bool"}} />
+
ge(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for TelemetryProvider` @@ -164,16 +164,16 @@ Return the stable configuration value for this provider. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for TelemetryProvider` -
Copy for TelemetryProvider"}} />
+
Copy for TelemetryProvider"}} />
### `impl Eq for TelemetryProvider` -
Eq for TelemetryProvider"}} />
+
Eq for TelemetryProvider"}} />
### `impl StructuralPartialEq for TelemetryProvider` -
StructuralPartialEq for TelemetryProvider"}} />
+
StructuralPartialEq for TelemetryProvider"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx index 854193ef4..ea329c478 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-load-adapter-descriptor.mdx @@ -2,13 +2,13 @@ title: "Function load_adapter_descriptor" sidebar-title: "load_adapter_descriptor" description: "Load an adapter descriptor from JSON package metadata." -position: 32 +position: 37 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
AsRef<Path>,\n) -> Result<AdapterDescriptor>"}} />
+
AsRef<Path>,\n) -> Result<AdapterDescriptor>"}} />
Load an adapter descriptor from JSON package metadata. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx index f66cf0ae7..5f4098022 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/fn-resolve-run-plan-from-config.mdx @@ -1,8 +1,8 @@ --- title: "Function resolve_run_plan_from_config" sidebar-title: "resolve_run_plan_from_config" -description: "Resolve a typed NeMo Fabric config into a runnable plan." -position: 33 +description: "Resolve a typed NVIDIA NeMo Fabric config into a runnable plan." +position: 38 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -11,6 +11,6 @@ Generated from `cargo doc --no-deps -p nemo-fabric-core`.
FabricConfig,\n    context: ResolveContext,\n) -> Result<RunPlan>"}} />
-Resolve a typed NeMo Fabric config into a runnable plan. +Resolve a typed NVIDIA NeMo Fabric config into a runnable plan. Callers provide an already-composed typed config and the explicit base directory used for resolving relative paths. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx index 080370f07..f8c9d41f6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/index.mdx @@ -2,7 +2,7 @@ title: "Module config" sidebar-title: "config" description: "NeMo Fabric config models and loading helpers." -position: 65 +position: 70 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -19,12 +19,14 @@ NeMo Fabric config models and loading helpers. - [AdapterTelemetryProviderSupport](struct-adaptertelemetryprovidersupport.mdx): Telemetry capabilities for one adapter-supported provider. - [AdapterTelemetrySupport](struct-adaptertelemetrysupport.mdx): Adapter telemetry support. - [CapabilityPlan](struct-capabilityplan.mdx): Resolved capability configuration. -- [CapabilityRoute](struct-capabilityroute.mdx): One capability routing decision. +- [CapabilityRoute](struct-capabilityroute.mdx): One capability execution assignment. - [CapabilityTargetPlan](struct-capabilitytargetplan.mdx): Capabilities routed to one target. - [EnvironmentConfig](struct-environmentconfig.mdx): Execution environment configuration. - [EnvironmentPlan](struct-environmentplan.mdx): Resolved environment plan. -- [FabricConfig](struct-fabricconfig.mdx): Versioned NeMo Fabric agent config. +- [FabricConfig](struct-fabricconfig.mdx): Versioned NVIDIA NeMo Fabric agent config. - [HarnessConfig](struct-harnessconfig.mdx): Harness selection. +- [InstructionConfig](struct-instructionconfig.mdx): One portable instruction value. +- [InstructionsConfig](struct-instructionsconfig.mdx): Harness-neutral agent instruction configuration. - [McpConfig](struct-mcpconfig.mdx): MCP capability configuration. - [McpServerConfig](struct-mcpserverconfig.mdx): MCP server configuration. - [McpServerPlan](struct-mcpserverplan.mdx): Resolved MCP server exposure. @@ -41,7 +43,7 @@ NeMo Fabric config models and loading helpers. - [ResolvedAdapterDescriptor](struct-resolvedadapterdescriptor.mdx): Adapter descriptor selected for a run plan. - [RunPlan](struct-runplan.mdx): Resolved NeMo Fabric run plan. - [RuntimeCapabilities](struct-runtimecapabilities.mdx): Lifecycle behavior implemented by a resolved runtime path. -- [RuntimeConfig](struct-runtimeconfig.mdx): Runtime input/output contract. +- [RuntimeConfig](struct-runtimeconfig.mdx): Invocation runtime contract. - [SkillConfig](struct-skillconfig.mdx): Skill capability configuration. - [TelemetryConfig](struct-telemetryconfig.mdx): Telemetry configuration. - [TelemetryPlan](struct-telemetryplan.mdx): Resolved telemetry plan. @@ -51,12 +53,14 @@ NeMo Fabric config models and loading helpers. ## Enums +- [AdapterConfigField](enum-adapterconfigfield.mdx): Adapter-translated normalized NVIDIA NeMo Fabric configuration fields. - [AdapterDescriptorSource](enum-adapterdescriptorsource.mdx): Where NeMo Fabric resolved an adapter descriptor from. - [AdapterKind](enum-adapterkind.mdx): Adapter implementation kind. - [CapabilityKind](enum-capabilitykind.mdx): Capability kind. -- [CapabilityTarget](enum-capabilitytarget.mdx): Capability routing target. +- [CapabilityTarget](enum-capabilitytarget.mdx): Component responsible for executing a configured capability. - [ControlLocation](enum-controllocation.mdx): Where NeMo Fabric control code runs relative to the environment. - [EnvironmentOwnership](enum-environmentownership.mdx): Whether NeMo Fabric owns the underlying environment resource. +- [InstructionMode](enum-instructionmode.mdx): How an instruction value is applied to the selected harness. - [McpExposure](enum-mcpexposure.mdx): MCP exposure strategy. - [RelayAtifStorageConfig](enum-relayatifstorageconfig.mdx): Relay ATIF remote storage configuration. - [RelayAtofMode](enum-relayatofmode.mdx): Relay ATOF file mode. @@ -75,4 +79,4 @@ NeMo Fabric config models and loading helpers. ## Functions - [load_adapter_descriptor](fn-load-adapter-descriptor.mdx): Load an adapter descriptor from JSON package metadata. -- [resolve_run_plan_from_config](fn-resolve-run-plan-from-config.mdx): Resolve a typed NeMo Fabric config into a runnable plan. +- [resolve_run_plan_from_config](fn-resolve-run-plan-from-config.mdx): Resolve a typed NVIDIA NeMo Fabric config into a runnable plan. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterconfigsupport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterconfigsupport.mdx index b728f9784..6b6018832 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterconfigsupport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterconfigsupport.mdx @@ -2,22 +2,22 @@ title: "Struct Adapter Config Support" sidebar-title: "AdapterConfigSupport" description: "Adapter config support." -position: 2 +position: 3 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<String>,\n    pub generates: Vec<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Vec<AdapterConfigField>,\n    pub generates: Vec<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Adapter config support. ## Fields -### `accepts: Vec` +### `accepts: Vec` -NeMo Fabric config areas or policy paths accepted by this adapter. +Normalized NVIDIA NeMo Fabric config areas or policy paths accepted by this adapter. ### `generates: Vec` @@ -31,31 +31,31 @@ Additive adapter config-support fields. ### `impl Clone for AdapterConfigSupport` -
Clone for AdapterConfigSupport"}} />
+
Clone for AdapterConfigSupport"}} />
#### `clone` -
clone(&self) -> AdapterConfigSupport"}} />
+
clone(&self) -> AdapterConfigSupport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterConfigSupport` -
Debug for AdapterConfigSupport"}} />
+
Debug for AdapterConfigSupport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for AdapterConfigSupport` -
Default for AdapterConfigSupport"}} />
+
Default for AdapterConfigSupport"}} />
#### `default` -
default() -> AdapterConfigSupport"}} />
+
default() -> AdapterConfigSupport"}} />
### `impl<'de> Deserialize<'de> for AdapterConfigSupport` @@ -63,7 +63,7 @@ Additive adapter config-support fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterConfigSupport` @@ -71,11 +71,11 @@ Additive adapter config-support fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Additive adapter config-support fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterConfigSupport` -
PartialEq for AdapterConfigSupport"}} />
+
PartialEq for AdapterConfigSupport"}} />
#### `eq` -
eq(&self, other: &AdapterConfigSupport) -> bool"}} />
+
eq(&self, other: &AdapterConfigSupport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterConfigSupport` @@ -103,8 +103,8 @@ Additive adapter config-support fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterConfigSupport` -
StructuralPartialEq for AdapterConfigSupport"}} />
+
StructuralPartialEq for AdapterConfigSupport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterdescriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterdescriptor.mdx index 2c970ab72..a5beaad34 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterdescriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterdescriptor.mdx @@ -2,14 +2,14 @@ title: "Struct Adapter Descriptor" sidebar-title: "AdapterDescriptor" description: "Language-neutral adapter descriptor for a harness integration." -position: 3 +position: 4 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub adapter_id: String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub runner: Map<String, Value>,\n    pub requirements: AdapterRequirements,\n    pub config: AdapterConfigSupport,\n    pub telemetry: AdapterTelemetrySupport,\n    pub capabilities: RuntimeCapabilities,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub adapter_id: String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub runner: Map<String, Value>,\n    pub requirements: AdapterRequirements,\n    pub config: AdapterConfigSupport,\n    pub telemetry: AdapterTelemetrySupport,\n    pub capabilities: RuntimeCapabilities,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Language-neutral adapter descriptor for a harness integration. @@ -59,23 +59,23 @@ Additive adapter descriptor fields. ### `impl Clone for AdapterDescriptor` -
Clone for AdapterDescriptor"}} />
+
Clone for AdapterDescriptor"}} />
#### `clone` -
clone(&self) -> AdapterDescriptor"}} />
+
clone(&self) -> AdapterDescriptor"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterDescriptor` -
Debug for AdapterDescriptor"}} />
+
Debug for AdapterDescriptor"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for AdapterDescriptor` @@ -83,7 +83,7 @@ Additive adapter descriptor fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterDescriptor` @@ -91,11 +91,11 @@ Additive adapter descriptor fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -103,19 +103,19 @@ Additive adapter descriptor fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterDescriptor` -
PartialEq for AdapterDescriptor"}} />
+
PartialEq for AdapterDescriptor"}} />
#### `eq` -
eq(&self, other: &AdapterDescriptor) -> bool"}} />
+
eq(&self, other: &AdapterDescriptor) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterDescriptor` @@ -123,8 +123,8 @@ Additive adapter descriptor fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterDescriptor` -
StructuralPartialEq for AdapterDescriptor"}} />
+
StructuralPartialEq for AdapterDescriptor"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterrequirements.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterrequirements.mdx index 9f94d5838..5127b32bc 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterrequirements.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adapterrequirements.mdx @@ -2,14 +2,14 @@ title: "Struct Adapter Requirements" sidebar-title: "AdapterRequirements" description: "Adapter runtime requirements." -position: 6 +position: 7 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<String>,\n    pub env: Vec<String>,\n    pub files: Vec<PathBuf>,\n    pub services: Vec<String>,\n    pub plugin_hooks: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Vec<String>,\n    pub env: Vec<String>,\n    pub files: Vec<PathBuf>,\n    pub services: Vec<String>,\n    pub plugin_hooks: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Adapter runtime requirements. @@ -43,31 +43,31 @@ Additive requirement fields. ### `impl Clone for AdapterRequirements` -
Clone for AdapterRequirements"}} />
+
Clone for AdapterRequirements"}} />
#### `clone` -
clone(&self) -> AdapterRequirements"}} />
+
clone(&self) -> AdapterRequirements"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterRequirements` -
Debug for AdapterRequirements"}} />
+
Debug for AdapterRequirements"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for AdapterRequirements` -
Default for AdapterRequirements"}} />
+
Default for AdapterRequirements"}} />
#### `default` -
default() -> AdapterRequirements"}} />
+
default() -> AdapterRequirements"}} />
### `impl<'de> Deserialize<'de> for AdapterRequirements` @@ -75,7 +75,7 @@ Additive requirement fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterRequirements` @@ -83,11 +83,11 @@ Additive requirement fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +95,19 @@ Additive requirement fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterRequirements` -
PartialEq for AdapterRequirements"}} />
+
PartialEq for AdapterRequirements"}} />
#### `eq` -
eq(&self, other: &AdapterRequirements) -> bool"}} />
+
eq(&self, other: &AdapterRequirements) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterRequirements` @@ -115,8 +115,8 @@ Additive requirement fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterRequirements` -
StructuralPartialEq for AdapterRequirements"}} />
+
StructuralPartialEq for AdapterRequirements"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetryprovidersupport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetryprovidersupport.mdx index 31d7665a6..03ef0b6d3 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetryprovidersupport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetryprovidersupport.mdx @@ -2,14 +2,14 @@ title: "Struct Adapter Telemetry Provider Support" sidebar-title: "AdapterTelemetryProviderSupport" description: "Telemetry capabilities for one adapter-supported provider." -position: 7 +position: 8 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<String>,\n    pub integration_modes: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Vec<String>,\n    pub integration_modes: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Telemetry capabilities for one adapter-supported provider. @@ -31,31 +31,31 @@ Additive provider capability fields. ### `impl Clone for AdapterTelemetryProviderSupport` -
Clone for AdapterTelemetryProviderSupport"}} />
+
Clone for AdapterTelemetryProviderSupport"}} />
#### `clone` -
clone(&self) -> AdapterTelemetryProviderSupport"}} />
+
clone(&self) -> AdapterTelemetryProviderSupport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterTelemetryProviderSupport` -
Debug for AdapterTelemetryProviderSupport"}} />
+
Debug for AdapterTelemetryProviderSupport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for AdapterTelemetryProviderSupport` -
Default for AdapterTelemetryProviderSupport"}} />
+
Default for AdapterTelemetryProviderSupport"}} />
#### `default` -
default() -> AdapterTelemetryProviderSupport"}} />
+
default() -> AdapterTelemetryProviderSupport"}} />
### `impl<'de> Deserialize<'de> for AdapterTelemetryProviderSupport` @@ -63,7 +63,7 @@ Additive provider capability fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterTelemetryProviderSupport` @@ -71,11 +71,11 @@ Additive provider capability fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Additive provider capability fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterTelemetryProviderSupport` -
PartialEq for AdapterTelemetryProviderSupport"}} />
+
PartialEq for AdapterTelemetryProviderSupport"}} />
#### `eq` -
eq(&self, other: &AdapterTelemetryProviderSupport) -> bool"}} />
+
eq(&self, other: &AdapterTelemetryProviderSupport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterTelemetryProviderSupport` @@ -103,8 +103,8 @@ Additive provider capability fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterTelemetryProviderSupport` -
StructuralPartialEq for AdapterTelemetryProviderSupport"}} />
+
StructuralPartialEq for AdapterTelemetryProviderSupport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetrysupport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetrysupport.mdx index 9f994d264..ef86b594f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetrysupport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-adaptertelemetrysupport.mdx @@ -2,14 +2,14 @@ title: "Struct Adapter Telemetry Support" sidebar-title: "AdapterTelemetrySupport" description: "Adapter telemetry support." -position: 8 +position: 9 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
BTreeMap<TelemetryProvider, AdapterTelemetryProviderSupport>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
BTreeMap<TelemetryProvider, AdapterTelemetryProviderSupport>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Adapter telemetry support. @@ -27,31 +27,31 @@ Additive adapter telemetry fields. ### `impl Clone for AdapterTelemetrySupport` -
Clone for AdapterTelemetrySupport"}} />
+
Clone for AdapterTelemetrySupport"}} />
#### `clone` -
clone(&self) -> AdapterTelemetrySupport"}} />
+
clone(&self) -> AdapterTelemetrySupport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterTelemetrySupport` -
Debug for AdapterTelemetrySupport"}} />
+
Debug for AdapterTelemetrySupport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for AdapterTelemetrySupport` -
Default for AdapterTelemetrySupport"}} />
+
Default for AdapterTelemetrySupport"}} />
#### `default` -
default() -> AdapterTelemetrySupport"}} />
+
default() -> AdapterTelemetrySupport"}} />
### `impl<'de> Deserialize<'de> for AdapterTelemetrySupport` @@ -59,7 +59,7 @@ Additive adapter telemetry fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterTelemetrySupport` @@ -67,11 +67,11 @@ Additive adapter telemetry fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive adapter telemetry fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterTelemetrySupport` -
PartialEq for AdapterTelemetrySupport"}} />
+
PartialEq for AdapterTelemetrySupport"}} />
#### `eq` -
eq(&self, other: &AdapterTelemetrySupport) -> bool"}} />
+
eq(&self, other: &AdapterTelemetrySupport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterTelemetrySupport` @@ -99,8 +99,8 @@ Additive adapter telemetry fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterTelemetrySupport` -
StructuralPartialEq for AdapterTelemetrySupport"}} />
+
StructuralPartialEq for AdapterTelemetrySupport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx index 0488ee8c1..245316be1 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityplan.mdx @@ -2,14 +2,14 @@ title: "Struct Capability Plan" sidebar-title: "CapabilityPlan" description: "Resolved capability configuration." -position: 9 +position: 10 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
ToolsPlan,\n    pub tools_configured: bool,\n    pub skill_paths: Vec<PathBuf>,\n    pub mcp_servers: BTreeMap<String, McpServerPlan>,\n    pub native: CapabilityTargetPlan,\n    pub managed: CapabilityTargetPlan,\n    pub unsupported: CapabilityTargetPlan,\n    pub routes: Vec<CapabilityRoute>,\n}"}} />
+
ToolsPlan,\n    pub tools_configured: bool,\n    pub skill_paths: Vec<PathBuf>,\n    pub mcp_servers: BTreeMap<String, McpServerPlan>,\n    pub native: CapabilityTargetPlan,\n    pub managed: CapabilityTargetPlan,\n    pub unsupported: CapabilityTargetPlan,\n    pub routes: Vec<CapabilityRoute>,\n}"}} />
Resolved capability configuration. @@ -51,31 +51,31 @@ Routing decisions made while planning the configured capabilities. ### `impl Clone for CapabilityPlan` -
Clone for CapabilityPlan"}} />
+
Clone for CapabilityPlan"}} />
#### `clone` -
clone(&self) -> CapabilityPlan"}} />
+
clone(&self) -> CapabilityPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for CapabilityPlan` -
Debug for CapabilityPlan"}} />
+
Debug for CapabilityPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for CapabilityPlan` -
Default for CapabilityPlan"}} />
+
Default for CapabilityPlan"}} />
#### `default` -
default() -> CapabilityPlan"}} />
+
default() -> CapabilityPlan"}} />
### `impl<'de> Deserialize<'de> for CapabilityPlan` @@ -83,7 +83,7 @@ Routing decisions made while planning the configured capabilities. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for CapabilityPlan` @@ -91,11 +91,11 @@ Routing decisions made while planning the configured capabilities. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -103,19 +103,19 @@ Routing decisions made while planning the configured capabilities. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for CapabilityPlan` -
PartialEq for CapabilityPlan"}} />
+
PartialEq for CapabilityPlan"}} />
#### `eq` -
eq(&self, other: &CapabilityPlan) -> bool"}} />
+
eq(&self, other: &CapabilityPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for CapabilityPlan` @@ -123,8 +123,8 @@ Routing decisions made while planning the configured capabilities. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for CapabilityPlan` -
StructuralPartialEq for CapabilityPlan"}} />
+
StructuralPartialEq for CapabilityPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute.mdx index c7a7477e6..4ba76e7ad 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilityroute.mdx @@ -1,7 +1,7 @@ --- title: "Struct Capability Route" sidebar-title: "CapabilityRoute" -description: "One capability routing decision." +description: "One capability execution assignment." position: 7 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. @@ -9,9 +9,11 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
CapabilityKind,\n    pub name: String,\n    pub target: CapabilityTarget,\n    pub reason: String,\n}"}} />
+
CapabilityKind,\n    pub name: String,\n    pub target: CapabilityTarget,\n    pub reason: String,\n}"}} />
-One capability routing decision. +One capability execution assignment. + +Routes apply to executable tool, skill, and MCP capabilities. Adapter-translated scalar configuration is validated separately against [`AdapterConfigSupport`](struct-adapterconfigsupport.mdx). ## Fields @@ -25,7 +27,7 @@ Capability name. ### `target: CapabilityTarget` -Routing target. +Component responsible for executing the capability. ### `reason: String` @@ -35,23 +37,23 @@ Human-readable reason for the selected route. ### `impl Clone for CapabilityRoute` -
Clone for CapabilityRoute"}} />
+
Clone for CapabilityRoute"}} />
#### `clone` -
clone(&self) -> CapabilityRoute"}} />
+
clone(&self) -> CapabilityRoute"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for CapabilityRoute` -
Debug for CapabilityRoute"}} />
+
Debug for CapabilityRoute"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for CapabilityRoute` @@ -59,7 +61,7 @@ Human-readable reason for the selected route. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for CapabilityRoute` @@ -67,11 +69,11 @@ Human-readable reason for the selected route. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +81,19 @@ Human-readable reason for the selected route. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for CapabilityRoute` -
PartialEq for CapabilityRoute"}} />
+
PartialEq for CapabilityRoute"}} />
#### `eq` -
eq(&self, other: &CapabilityRoute) -> bool"}} />
+
eq(&self, other: &CapabilityRoute) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for CapabilityRoute` @@ -99,8 +101,8 @@ Human-readable reason for the selected route. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for CapabilityRoute` -
StructuralPartialEq for CapabilityRoute"}} />
+
StructuralPartialEq for CapabilityRoute"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan.mdx index f68b7d416..439e1bf7c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-capabilitytargetplan.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub skill_paths: Vec<PathBuf>,\n    pub mcp_servers: BTreeMap<String, McpServerPlan>,\n}"}} />
+
bool,\n    pub skill_paths: Vec<PathBuf>,\n    pub mcp_servers: BTreeMap<String, McpServerPlan>,\n}"}} />
Capabilities routed to one target. @@ -31,31 +31,31 @@ MCP servers for this target. ### `impl Clone for CapabilityTargetPlan` -
Clone for CapabilityTargetPlan"}} />
+
Clone for CapabilityTargetPlan"}} />
#### `clone` -
clone(&self) -> CapabilityTargetPlan"}} />
+
clone(&self) -> CapabilityTargetPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for CapabilityTargetPlan` -
Debug for CapabilityTargetPlan"}} />
+
Debug for CapabilityTargetPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for CapabilityTargetPlan` -
Default for CapabilityTargetPlan"}} />
+
Default for CapabilityTargetPlan"}} />
#### `default` -
default() -> CapabilityTargetPlan"}} />
+
default() -> CapabilityTargetPlan"}} />
### `impl<'de> Deserialize<'de> for CapabilityTargetPlan` @@ -63,7 +63,7 @@ MCP servers for this target. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for CapabilityTargetPlan` @@ -71,11 +71,11 @@ MCP servers for this target. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ MCP servers for this target. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for CapabilityTargetPlan` -
PartialEq for CapabilityTargetPlan"}} />
+
PartialEq for CapabilityTargetPlan"}} />
#### `eq` -
eq(&self, other: &CapabilityTargetPlan) -> bool"}} />
+
eq(&self, other: &CapabilityTargetPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for CapabilityTargetPlan` @@ -103,8 +103,8 @@ MCP servers for this target. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for CapabilityTargetPlan` -
StructuralPartialEq for CapabilityTargetPlan"}} />
+
StructuralPartialEq for CapabilityTargetPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx index 81b294b1d..97b1987d7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Environment Config" sidebar-title: "EnvironmentConfig" description: "Execution environment configuration." -position: 11 +position: 12 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub control_location: ControlLocation,\n    pub ownership: EnvironmentOwnership,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub connection: Map<String, Value>,\n    pub metadata: Map<String, Value>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub control_location: ControlLocation,\n    pub ownership: EnvironmentOwnership,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub env: BTreeMap<String, String>,\n    pub connection: Map<String, Value>,\n    pub metadata: Map<String, Value>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Execution environment configuration. @@ -35,6 +35,12 @@ Workspace path inside or outside the provider. Artifact path inside or outside the provider. +### `env: BTreeMap` + +Environment variables visible to the harness and its tools. + +Values are serialized into the run plan and can appear wherever configs or plans are logged or persisted. Prefer `api_key_env`-style environment-variable-name indirection for credentials. + ### `connection: Map` Provider connection metadata, such as server URL, credential reference, or namespace. @@ -55,23 +61,23 @@ Additive normalized environment fields. ### `impl Clone for EnvironmentConfig` -
Clone for EnvironmentConfig"}} />
+
Clone for EnvironmentConfig"}} />
#### `clone` -
clone(&self) -> EnvironmentConfig"}} />
+
clone(&self) -> EnvironmentConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for EnvironmentConfig` -
Debug for EnvironmentConfig"}} />
+
Debug for EnvironmentConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for EnvironmentConfig` @@ -79,7 +85,7 @@ Additive normalized environment fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for EnvironmentConfig` @@ -87,11 +93,11 @@ Additive normalized environment fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -99,19 +105,19 @@ Additive normalized environment fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for EnvironmentConfig` -
PartialEq for EnvironmentConfig"}} />
+
PartialEq for EnvironmentConfig"}} />
#### `eq` -
eq(&self, other: &EnvironmentConfig) -> bool"}} />
+
eq(&self, other: &EnvironmentConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for EnvironmentConfig` @@ -119,8 +125,8 @@ Additive normalized environment fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for EnvironmentConfig` -
StructuralPartialEq for EnvironmentConfig"}} />
+
StructuralPartialEq for EnvironmentConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx index d849fc103..6c6a0754a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-environmentplan.mdx @@ -2,14 +2,14 @@ title: "Struct Environment Plan" sidebar-title: "EnvironmentPlan" description: "Resolved environment plan." -position: 13 +position: 14 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub control_location: ControlLocation,\n    pub ownership: EnvironmentOwnership,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub connection: Map<String, Value>,\n    pub metadata: Map<String, Value>,\n    pub settings: Map<String, Value>,\n}"}} />
+
String,\n    pub control_location: ControlLocation,\n    pub ownership: EnvironmentOwnership,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub env: BTreeMap<String, String>,\n    pub connection: Map<String, Value>,\n    pub metadata: Map<String, Value>,\n    pub settings: Map<String, Value>,\n}"}} />
Resolved environment plan. @@ -35,6 +35,10 @@ Resolved workspace path. Resolved artifact path. +### `env: BTreeMap` + +Environment variables visible to the harness and its tools. + ### `connection: Map` Provider connection metadata. @@ -51,23 +55,23 @@ Provider-specific settings. ### `impl Clone for EnvironmentPlan` -
Clone for EnvironmentPlan"}} />
+
Clone for EnvironmentPlan"}} />
#### `clone` -
clone(&self) -> EnvironmentPlan"}} />
+
clone(&self) -> EnvironmentPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for EnvironmentPlan` -
Debug for EnvironmentPlan"}} />
+
Debug for EnvironmentPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for EnvironmentPlan` @@ -75,7 +79,7 @@ Provider-specific settings. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for EnvironmentPlan` @@ -83,11 +87,11 @@ Provider-specific settings. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +99,19 @@ Provider-specific settings. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for EnvironmentPlan` -
PartialEq for EnvironmentPlan"}} />
+
PartialEq for EnvironmentPlan"}} />
#### `eq` -
eq(&self, other: &EnvironmentPlan) -> bool"}} />
+
eq(&self, other: &EnvironmentPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for EnvironmentPlan` @@ -115,8 +119,8 @@ Provider-specific settings. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for EnvironmentPlan` -
StructuralPartialEq for EnvironmentPlan"}} />
+
StructuralPartialEq for EnvironmentPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx index 00089ddfb..fac25b79c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-fabricconfig.mdx @@ -1,17 +1,19 @@ --- title: "Struct Fabric Config" sidebar-title: "FabricConfig" -description: "Versioned NeMo Fabric agent config." -position: 14 +description: "Versioned NVIDIA NeMo Fabric agent config." +position: 15 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub metadata: MetadataConfig,\n    pub harness: HarnessConfig,\n    pub models: BTreeMap<String, ModelConfig>,\n    pub runtime: RuntimeConfig,\n    pub environment: Option<EnvironmentConfig>,\n    pub tools: Option<ToolsConfig>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub relay: Option<RelayConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub metadata: MetadataConfig,\n    pub harness: HarnessConfig,\n    pub models: BTreeMap<String, ModelConfig>,\n    pub instructions: Option<InstructionsConfig>,\n    pub runtime: RuntimeConfig,\n    pub environment: Option<EnvironmentConfig>,\n    pub tools: Option<ToolsConfig>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub relay: Option<RelayConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
-Versioned NeMo Fabric agent config. +Versioned NVIDIA NeMo Fabric agent config. + +NeMo Fabric-owned fields apply uniformly, while adapter-translated fields are validated against the selected adapter descriptor. See the [configuration compatibility matrix](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/sdk/python.mdx#normalized-configuration-compatibility). ## Fields @@ -29,11 +31,15 @@ Harness selection and harness-specific settings. ### `models: BTreeMap` -Model aliases. +Named model roles. + +### `instructions: Option` + +Portable agent instructions for the selected harness. ### `runtime: RuntimeConfig` -Runtime input/output contract. +Invocation runtime contract. ### `environment: Option` @@ -67,23 +73,23 @@ Additive fields not yet recognized by this core version. ### `impl Clone for FabricConfig` -
Clone for FabricConfig"}} />
+
Clone for FabricConfig"}} />
#### `clone` -
clone(&self) -> FabricConfig"}} />
+
clone(&self) -> FabricConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for FabricConfig` -
Debug for FabricConfig"}} />
+
Debug for FabricConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for FabricConfig` @@ -91,7 +97,7 @@ Additive fields not yet recognized by this core version. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for FabricConfig` @@ -99,11 +105,11 @@ Additive fields not yet recognized by this core version. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -111,19 +117,19 @@ Additive fields not yet recognized by this core version. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for FabricConfig` -
PartialEq for FabricConfig"}} />
+
PartialEq for FabricConfig"}} />
#### `eq` -
eq(&self, other: &FabricConfig) -> bool"}} />
+
eq(&self, other: &FabricConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for FabricConfig` @@ -131,8 +137,8 @@ Additive fields not yet recognized by this core version. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for FabricConfig` -
StructuralPartialEq for FabricConfig"}} />
+
StructuralPartialEq for FabricConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx index bc3e2cc3a..a8f1b6470 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-harnessconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Harness Config" sidebar-title: "HarnessConfig" description: "Harness selection." -position: 15 +position: 16 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub resolution: Option<ResolutionStrategy>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub resolution: Option<ResolutionStrategy>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Harness selection. @@ -35,23 +35,23 @@ Additive normalized harness fields. ### `impl Clone for HarnessConfig` -
Clone for HarnessConfig"}} />
+
Clone for HarnessConfig"}} />
#### `clone` -
clone(&self) -> HarnessConfig"}} />
+
clone(&self) -> HarnessConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for HarnessConfig` -
Debug for HarnessConfig"}} />
+
Debug for HarnessConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for HarnessConfig` @@ -59,7 +59,7 @@ Additive normalized harness fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for HarnessConfig` @@ -67,11 +67,11 @@ Additive normalized harness fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive normalized harness fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for HarnessConfig` -
PartialEq for HarnessConfig"}} />
+
PartialEq for HarnessConfig"}} />
#### `eq` -
eq(&self, other: &HarnessConfig) -> bool"}} />
+
eq(&self, other: &HarnessConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for HarnessConfig` @@ -99,8 +99,8 @@ Additive normalized harness fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for HarnessConfig` -
StructuralPartialEq for HarnessConfig"}} />
+
StructuralPartialEq for HarnessConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionconfig.mdx new file mode 100644 index 000000000..860d3c757 --- /dev/null +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionconfig.mdx @@ -0,0 +1,102 @@ +--- +title: "Struct Instruction Config" +sidebar-title: "InstructionConfig" +description: "One portable instruction value." +position: 17 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p nemo-fabric-core`. + +
String,\n    pub mode: InstructionMode,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +One portable instruction value. + +## Fields + +### `content: String` + +Instruction text. + +### `mode: InstructionMode` + +How the instruction is applied. + +### `extensions: BTreeMap` + +Additive instruction fields. + +## Trait Implementations + +### `impl Clone for InstructionConfig` + +
Clone for InstructionConfig"}} />
+ +#### `clone` + +
clone(&self) -> InstructionConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for InstructionConfig` + +
Debug for InstructionConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl<'de> Deserialize<'de> for InstructionConfig` + +
Deserialize<'de> for InstructionConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for InstructionConfig` + +
InstructionConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for InstructionConfig` + +
PartialEq for InstructionConfig"}} />
+ +#### `eq` + +
eq(&self, other: &InstructionConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for InstructionConfig` + +
Serialize for InstructionConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for InstructionConfig` + +
StructuralPartialEq for InstructionConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionsconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionsconfig.mdx new file mode 100644 index 000000000..dae75c34e --- /dev/null +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-instructionsconfig.mdx @@ -0,0 +1,106 @@ +--- +title: "Struct Instructions Config" +sidebar-title: "InstructionsConfig" +description: "Harness-neutral agent instruction configuration." +position: 19 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p nemo-fabric-core`. + +
Option<InstructionConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Harness-neutral agent instruction configuration. + +## Fields + +### `system: Option` + +System instructions for the selected harness. + +### `extensions: BTreeMap` + +Additive instruction categories. + +## Trait Implementations + +### `impl Clone for InstructionsConfig` + +
Clone for InstructionsConfig"}} />
+ +#### `clone` + +
clone(&self) -> InstructionsConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for InstructionsConfig` + +
Debug for InstructionsConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for InstructionsConfig` + +
Default for InstructionsConfig"}} />
+ +#### `default` + +
default() -> InstructionsConfig"}} />
+ +### `impl<'de> Deserialize<'de> for InstructionsConfig` + +
Deserialize<'de> for InstructionsConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for InstructionsConfig` + +
InstructionsConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for InstructionsConfig` + +
PartialEq for InstructionsConfig"}} />
+ +#### `eq` + +
eq(&self, other: &InstructionsConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for InstructionsConfig` + +
Serialize for InstructionsConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for InstructionsConfig` + +
StructuralPartialEq for InstructionsConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx index 233f96b4f..40c0c7245 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpconfig.mdx @@ -2,14 +2,14 @@ title: "Struct McpConfig" sidebar-title: "McpConfig" description: "MCP capability configuration." -position: 16 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
BTreeMap<String, McpServerConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
BTreeMap<String, McpServerConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
MCP capability configuration. @@ -27,31 +27,31 @@ Additive MCP fields. ### `impl Clone for McpConfig` -
Clone for McpConfig"}} />
+
Clone for McpConfig"}} />
#### `clone` -
clone(&self) -> McpConfig"}} />
+
clone(&self) -> McpConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for McpConfig` -
Debug for McpConfig"}} />
+
Debug for McpConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for McpConfig` -
Default for McpConfig"}} />
+
Default for McpConfig"}} />
#### `default` -
default() -> McpConfig"}} />
+
default() -> McpConfig"}} />
### `impl<'de> Deserialize<'de> for McpConfig` @@ -59,7 +59,7 @@ Additive MCP fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for McpConfig` @@ -67,11 +67,11 @@ Additive MCP fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive MCP fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for McpConfig` -
PartialEq for McpConfig"}} />
+
PartialEq for McpConfig"}} />
#### `eq` -
eq(&self, other: &McpConfig) -> bool"}} />
+
eq(&self, other: &McpConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for McpConfig` @@ -99,8 +99,8 @@ Additive MCP fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for McpConfig` -
StructuralPartialEq for McpConfig"}} />
+
StructuralPartialEq for McpConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx index ccfb9595f..a678a04cf 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverconfig.mdx @@ -2,14 +2,14 @@ title: "Struct McpServer Config" sidebar-title: "McpServerConfig" description: "MCP server configuration." -position: 14 +position: 16 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub url: String,\n    pub exposure: McpExposure,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub url: String,\n    pub exposure: McpExposure,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
MCP server configuration. @@ -35,23 +35,23 @@ Additive MCP server fields. ### `impl Clone for McpServerConfig` -
Clone for McpServerConfig"}} />
+
Clone for McpServerConfig"}} />
#### `clone` -
clone(&self) -> McpServerConfig"}} />
+
clone(&self) -> McpServerConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for McpServerConfig` -
Debug for McpServerConfig"}} />
+
Debug for McpServerConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for McpServerConfig` @@ -59,7 +59,7 @@ Additive MCP server fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for McpServerConfig` @@ -67,11 +67,11 @@ Additive MCP server fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive MCP server fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for McpServerConfig` -
PartialEq for McpServerConfig"}} />
+
PartialEq for McpServerConfig"}} />
#### `eq` -
eq(&self, other: &McpServerConfig) -> bool"}} />
+
eq(&self, other: &McpServerConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for McpServerConfig` @@ -99,8 +99,8 @@ Additive MCP server fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for McpServerConfig` -
StructuralPartialEq for McpServerConfig"}} />
+
StructuralPartialEq for McpServerConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx index 12f41a21c..110af3d80 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-mcpserverplan.mdx @@ -2,14 +2,14 @@ title: "Struct McpServer Plan" sidebar-title: "McpServerPlan" description: "Resolved MCP server exposure." -position: 18 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub url: String,\n    pub exposure: McpExposure,\n}"}} />
+
String,\n    pub url: String,\n    pub exposure: McpExposure,\n}"}} />
Resolved MCP server exposure. @@ -31,23 +31,23 @@ Exposure strategy. ### `impl Clone for McpServerPlan` -
Clone for McpServerPlan"}} />
+
Clone for McpServerPlan"}} />
#### `clone` -
clone(&self) -> McpServerPlan"}} />
+
clone(&self) -> McpServerPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for McpServerPlan` -
Debug for McpServerPlan"}} />
+
Debug for McpServerPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for McpServerPlan` @@ -55,7 +55,7 @@ Exposure strategy. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for McpServerPlan` @@ -63,11 +63,11 @@ Exposure strategy. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -75,19 +75,19 @@ Exposure strategy. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for McpServerPlan` -
PartialEq for McpServerPlan"}} />
+
PartialEq for McpServerPlan"}} />
#### `eq` -
eq(&self, other: &McpServerPlan) -> bool"}} />
+
eq(&self, other: &McpServerPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for McpServerPlan` @@ -95,8 +95,8 @@ Exposure strategy. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for McpServerPlan` -
StructuralPartialEq for McpServerPlan"}} />
+
StructuralPartialEq for McpServerPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx index a0d45ea93..2fa649ab6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-metadataconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Metadata Config" sidebar-title: "MetadataConfig" description: "Human-readable metadata." -position: 19 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub description: Option<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub description: Option<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Human-readable metadata. @@ -31,23 +31,23 @@ Additive metadata fields. ### `impl Clone for MetadataConfig` -
Clone for MetadataConfig"}} />
+
Clone for MetadataConfig"}} />
#### `clone` -
clone(&self) -> MetadataConfig"}} />
+
clone(&self) -> MetadataConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for MetadataConfig` -
Debug for MetadataConfig"}} />
+
Debug for MetadataConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for MetadataConfig` @@ -55,7 +55,7 @@ Additive metadata fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for MetadataConfig` @@ -63,11 +63,11 @@ Additive metadata fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -75,19 +75,19 @@ Additive metadata fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for MetadataConfig` -
PartialEq for MetadataConfig"}} />
+
PartialEq for MetadataConfig"}} />
#### `eq` -
eq(&self, other: &MetadataConfig) -> bool"}} />
+
eq(&self, other: &MetadataConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for MetadataConfig` @@ -95,8 +95,8 @@ Additive metadata fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for MetadataConfig` -
StructuralPartialEq for MetadataConfig"}} />
+
StructuralPartialEq for MetadataConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx index 970d192eb..974820a8f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-modelconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Model Config" sidebar-title: "ModelConfig" description: "Model configuration." -position: 20 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub model: String,\n    pub temperature: Option<f64>,\n    pub api_key_env: Option<String>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub model: String,\n    pub temperature: Option<f64>,\n    pub api_key_env: Option<String>,\n    pub base_url: Option<String>,\n    pub settings: Map<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Model configuration. @@ -31,6 +31,10 @@ Optional temperature. Optional environment variable containing an API key. +### `base_url: Option` + +Optional provider endpoint URL. + ### `settings: Map` Provider-specific settings. @@ -43,23 +47,23 @@ Additive normalized model fields. ### `impl Clone for ModelConfig` -
Clone for ModelConfig"}} />
+
Clone for ModelConfig"}} />
#### `clone` -
clone(&self) -> ModelConfig"}} />
+
clone(&self) -> ModelConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ModelConfig` -
Debug for ModelConfig"}} />
+
Debug for ModelConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ModelConfig` @@ -67,7 +71,7 @@ Additive normalized model fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ModelConfig` @@ -75,11 +79,11 @@ Additive normalized model fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +91,19 @@ Additive normalized model fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ModelConfig` -
PartialEq for ModelConfig"}} />
+
PartialEq for ModelConfig"}} />
#### `eq` -
eq(&self, other: &ModelConfig) -> bool"}} />
+
eq(&self, other: &ModelConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ModelConfig` @@ -107,8 +111,8 @@ Additive normalized model fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ModelConfig` -
StructuralPartialEq for ModelConfig"}} />
+
StructuralPartialEq for ModelConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx index 08f71834d..b13051b42 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatifconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Atif Config" sidebar-title: "RelayAtifConfig" description: "Relay ATIF export configuration." -position: 18 +position: 20 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub agent_name: String,\n    pub agent_version: Option<String>,\n    pub model_name: String,\n    pub tool_definitions: Option<Vec<Value>>,\n    pub extra: Option<Value>,\n    pub output_directory: Option<PathBuf>,\n    pub filename_template: String,\n    pub storage: Option<Vec<RelayAtifStorageConfig>>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub agent_name: String,\n    pub agent_version: Option<String>,\n    pub model_name: String,\n    pub tool_definitions: Option<Vec<Value>>,\n    pub extra: Option<Value>,\n    pub output_directory: Option<PathBuf>,\n    pub filename_template: String,\n    pub storage: Option<Vec<RelayAtifStorageConfig>>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Relay ATIF export configuration. @@ -59,31 +59,31 @@ Additive ATIF fields. ### `impl Clone for RelayAtifConfig` -
Clone for RelayAtifConfig"}} />
+
Clone for RelayAtifConfig"}} />
#### `clone` -
clone(&self) -> RelayAtifConfig"}} />
+
clone(&self) -> RelayAtifConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtifConfig` -
Debug for RelayAtifConfig"}} />
+
Debug for RelayAtifConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayAtifConfig` -
Default for RelayAtifConfig"}} />
+
Default for RelayAtifConfig"}} />
#### `default` -
default() -> Self"}} />
+
default() -> Self"}} />
### `impl<'de> Deserialize<'de> for RelayAtifConfig` @@ -91,7 +91,7 @@ Additive ATIF fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtifConfig` @@ -99,11 +99,11 @@ Additive ATIF fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -111,19 +111,19 @@ Additive ATIF fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtifConfig` -
PartialEq for RelayAtifConfig"}} />
+
PartialEq for RelayAtifConfig"}} />
#### `eq` -
eq(&self, other: &RelayAtifConfig) -> bool"}} />
+
eq(&self, other: &RelayAtifConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtifConfig` @@ -131,8 +131,8 @@ Additive ATIF fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayAtifConfig` -
StructuralPartialEq for RelayAtifConfig"}} />
+
StructuralPartialEq for RelayAtifConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx index aa4c6d788..e78abe02e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayatofconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Atof Config" sidebar-title: "RelayAtofConfig" description: "Relay ATOF export configuration." -position: 19 +position: 21 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub sinks: Vec<RelayAtofSinkConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub sinks: Vec<RelayAtofSinkConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Relay ATOF export configuration. @@ -31,31 +31,31 @@ Additive ATOF fields. ### `impl Clone for RelayAtofConfig` -
Clone for RelayAtofConfig"}} />
+
Clone for RelayAtofConfig"}} />
#### `clone` -
clone(&self) -> RelayAtofConfig"}} />
+
clone(&self) -> RelayAtofConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayAtofConfig` -
Debug for RelayAtofConfig"}} />
+
Debug for RelayAtofConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayAtofConfig` -
Default for RelayAtofConfig"}} />
+
Default for RelayAtofConfig"}} />
#### `default` -
default() -> RelayAtofConfig"}} />
+
default() -> RelayAtofConfig"}} />
### `impl<'de> Deserialize<'de> for RelayAtofConfig` @@ -63,7 +63,7 @@ Additive ATOF fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayAtofConfig` @@ -71,11 +71,11 @@ Additive ATOF fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Additive ATOF fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayAtofConfig` -
PartialEq for RelayAtofConfig"}} />
+
PartialEq for RelayAtofConfig"}} />
#### `eq` -
eq(&self, other: &RelayAtofConfig) -> bool"}} />
+
eq(&self, other: &RelayAtofConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayAtofConfig` @@ -103,8 +103,8 @@ Additive ATOF fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayAtofConfig` -
StructuralPartialEq for RelayAtofConfig"}} />
+
StructuralPartialEq for RelayAtofConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx index f1a4673c2..83149a4ce 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relaycomponentconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Component Config" sidebar-title: "RelayComponentConfig" description: "Generic NeMo Relay plugin component configuration." -position: 20 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub enabled: bool,\n    pub config: BTreeMap<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub enabled: bool,\n    pub config: BTreeMap<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Generic NeMo Relay plugin component configuration. @@ -35,23 +35,23 @@ Additive component fields. ### `impl Clone for RelayComponentConfig` -
Clone for RelayComponentConfig"}} />
+
Clone for RelayComponentConfig"}} />
#### `clone` -
clone(&self) -> RelayComponentConfig"}} />
+
clone(&self) -> RelayComponentConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayComponentConfig` -
Debug for RelayComponentConfig"}} />
+
Debug for RelayComponentConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RelayComponentConfig` @@ -59,7 +59,7 @@ Additive component fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayComponentConfig` @@ -67,11 +67,11 @@ Additive component fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive component fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayComponentConfig` -
PartialEq for RelayComponentConfig"}} />
+
PartialEq for RelayComponentConfig"}} />
#### `eq` -
eq(&self, other: &RelayComponentConfig) -> bool"}} />
+
eq(&self, other: &RelayComponentConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayComponentConfig` @@ -99,8 +99,8 @@ Additive component fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayComponentConfig` -
StructuralPartialEq for RelayComponentConfig"}} />
+
StructuralPartialEq for RelayComponentConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx index ab1392cee..54d4d066f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Config" sidebar-title: "RelayConfig" description: "NeMo Relay integration configuration." -position: 21 +position: 23 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Option<String>,\n    pub output_dir: Option<PathBuf>,\n    pub observability: Option<RelayObservabilityConfig>,\n    pub components: Vec<RelayComponentConfig>,\n    pub policy: Option<RelayConfigPolicy>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Option<String>,\n    pub output_dir: Option<PathBuf>,\n    pub observability: Option<RelayObservabilityConfig>,\n    pub components: Vec<RelayComponentConfig>,\n    pub policy: Option<RelayConfigPolicy>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
NeMo Relay integration configuration. @@ -43,31 +43,31 @@ Additive Relay fields. ### `impl Clone for RelayConfig` -
Clone for RelayConfig"}} />
+
Clone for RelayConfig"}} />
#### `clone` -
clone(&self) -> RelayConfig"}} />
+
clone(&self) -> RelayConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayConfig` -
Debug for RelayConfig"}} />
+
Debug for RelayConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayConfig` -
Default for RelayConfig"}} />
+
Default for RelayConfig"}} />
#### `default` -
default() -> RelayConfig"}} />
+
default() -> RelayConfig"}} />
### `impl<'de> Deserialize<'de> for RelayConfig` @@ -75,7 +75,7 @@ Additive Relay fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayConfig` @@ -83,11 +83,11 @@ Additive Relay fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +95,19 @@ Additive Relay fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayConfig` -
PartialEq for RelayConfig"}} />
+
PartialEq for RelayConfig"}} />
#### `eq` -
eq(&self, other: &RelayConfig) -> bool"}} />
+
eq(&self, other: &RelayConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayConfig` @@ -115,8 +115,8 @@ Additive Relay fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayConfig` -
StructuralPartialEq for RelayConfig"}} />
+
StructuralPartialEq for RelayConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx index 802456c59..ae33c3656 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayconfigpolicy.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config Policy" sidebar-title: "RelayConfigPolicy" description: "Relay validation policy." -position: 22 +position: 24 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -31,31 +31,31 @@ Policy for unsupported values. ### `impl Clone for RelayConfigPolicy` -
Clone for RelayConfigPolicy"}} />
+
Clone for RelayConfigPolicy"}} />
#### `clone` -
clone(&self) -> RelayConfigPolicy"}} />
+
clone(&self) -> RelayConfigPolicy"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayConfigPolicy` -
Debug for RelayConfigPolicy"}} />
+
Debug for RelayConfigPolicy"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayConfigPolicy` -
Default for RelayConfigPolicy"}} />
+
Default for RelayConfigPolicy"}} />
#### `default` -
default() -> Self"}} />
+
default() -> Self"}} />
### `impl<'de> Deserialize<'de> for RelayConfigPolicy` @@ -63,7 +63,7 @@ Policy for unsupported values. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayConfigPolicy` @@ -71,11 +71,11 @@ Policy for unsupported values. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Policy for unsupported values. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayConfigPolicy` -
PartialEq for RelayConfigPolicy"}} />
+
PartialEq for RelayConfigPolicy"}} />
#### `eq` -
eq(&self, other: &RelayConfigPolicy) -> bool"}} />
+
eq(&self, other: &RelayConfigPolicy) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayConfigPolicy` @@ -103,8 +103,8 @@ Policy for unsupported values. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayConfigPolicy` -
StructuralPartialEq for RelayConfigPolicy"}} />
+
StructuralPartialEq for RelayConfigPolicy"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx index c54c33245..1e31241b6 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayobservabilityconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Observability Config" sidebar-title: "RelayObservabilityConfig" description: "NeMo Relay observability component configuration." -position: 23 +position: 25 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
u32,\n    pub atof: Option<RelayAtofConfig>,\n    pub atif: Option<RelayAtifConfig>,\n    pub opentelemetry: Option<RelayOtlpConfig>,\n    pub openinference: Option<RelayOtlpConfig>,\n    pub policy: Option<RelayConfigPolicy>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
u32,\n    pub atof: Option<RelayAtofConfig>,\n    pub atif: Option<RelayAtifConfig>,\n    pub opentelemetry: Option<RelayOtlpConfig>,\n    pub openinference: Option<RelayOtlpConfig>,\n    pub policy: Option<RelayConfigPolicy>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
NeMo Relay observability component configuration. @@ -47,31 +47,31 @@ Additive observability fields. ### `impl Clone for RelayObservabilityConfig` -
Clone for RelayObservabilityConfig"}} />
+
Clone for RelayObservabilityConfig"}} />
#### `clone` -
clone(&self) -> RelayObservabilityConfig"}} />
+
clone(&self) -> RelayObservabilityConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayObservabilityConfig` -
Debug for RelayObservabilityConfig"}} />
+
Debug for RelayObservabilityConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayObservabilityConfig` -
Default for RelayObservabilityConfig"}} />
+
Default for RelayObservabilityConfig"}} />
#### `default` -
default() -> Self"}} />
+
default() -> Self"}} />
### `impl<'de> Deserialize<'de> for RelayObservabilityConfig` @@ -79,7 +79,7 @@ Additive observability fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayObservabilityConfig` @@ -87,11 +87,11 @@ Additive observability fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -99,19 +99,19 @@ Additive observability fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayObservabilityConfig` -
PartialEq for RelayObservabilityConfig"}} />
+
PartialEq for RelayObservabilityConfig"}} />
#### `eq` -
eq(&self, other: &RelayObservabilityConfig) -> bool"}} />
+
eq(&self, other: &RelayObservabilityConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayObservabilityConfig` @@ -119,8 +119,8 @@ Additive observability fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayObservabilityConfig` -
StructuralPartialEq for RelayObservabilityConfig"}} />
+
StructuralPartialEq for RelayObservabilityConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx index c837ed45f..c12913403 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-relayotlpconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Relay Otlp Config" sidebar-title: "RelayOtlpConfig" description: "Relay OpenTelemetry/OpenInference export configuration." -position: 24 +position: 26 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub transport: RelayOtlpTransport,\n    pub endpoint: Option<String>,\n    pub headers: BTreeMap<String, String>,\n    pub resource_attributes: BTreeMap<String, String>,\n    pub service_name: String,\n    pub service_namespace: Option<String>,\n    pub service_version: Option<String>,\n    pub instrumentation_scope: Option<String>,\n    pub timeout_millis: u64,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub transport: RelayOtlpTransport,\n    pub endpoint: Option<String>,\n    pub headers: BTreeMap<String, String>,\n    pub resource_attributes: BTreeMap<String, String>,\n    pub service_name: String,\n    pub service_namespace: Option<String>,\n    pub service_version: Option<String>,\n    pub instrumentation_scope: Option<String>,\n    pub timeout_millis: u64,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Relay OpenTelemetry/OpenInference export configuration. @@ -63,31 +63,31 @@ Additive OTLP fields. ### `impl Clone for RelayOtlpConfig` -
Clone for RelayOtlpConfig"}} />
+
Clone for RelayOtlpConfig"}} />
#### `clone` -
clone(&self) -> RelayOtlpConfig"}} />
+
clone(&self) -> RelayOtlpConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RelayOtlpConfig` -
Debug for RelayOtlpConfig"}} />
+
Debug for RelayOtlpConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RelayOtlpConfig` -
Default for RelayOtlpConfig"}} />
+
Default for RelayOtlpConfig"}} />
#### `default` -
default() -> Self"}} />
+
default() -> Self"}} />
### `impl<'de> Deserialize<'de> for RelayOtlpConfig` @@ -95,7 +95,7 @@ Additive OTLP fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RelayOtlpConfig` @@ -103,11 +103,11 @@ Additive OTLP fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -115,19 +115,19 @@ Additive OTLP fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RelayOtlpConfig` -
PartialEq for RelayOtlpConfig"}} />
+
PartialEq for RelayOtlpConfig"}} />
#### `eq` -
eq(&self, other: &RelayOtlpConfig) -> bool"}} />
+
eq(&self, other: &RelayOtlpConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RelayOtlpConfig` @@ -135,8 +135,8 @@ Additive OTLP fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RelayOtlpConfig` -
StructuralPartialEq for RelayOtlpConfig"}} />
+
StructuralPartialEq for RelayOtlpConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx index 2309c9707..d6b5e5afe 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvecontext.mdx @@ -2,14 +2,14 @@ title: "Struct Resolve Context" sidebar-title: "ResolveContext" description: "Source context used when resolving an in-memory NeMo Fabric config." -position: 22 +position: 26 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf,\n}"}} />
+
PathBuf,\n}"}} />
Source context used when resolving an in-memory NeMo Fabric config. @@ -27,7 +27,7 @@ Base directory used to resolve relative NeMo Fabric paths. #### `new` -
Into<PathBuf>) -> Self"}} />
+
Into<PathBuf>) -> Self"}} />
Build a context with an explicit base directory. @@ -35,36 +35,36 @@ Build a context with an explicit base directory. ### `impl Clone for ResolveContext` -
Clone for ResolveContext"}} />
+
Clone for ResolveContext"}} />
#### `clone` -
clone(&self) -> ResolveContext"}} />
+
clone(&self) -> ResolveContext"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ResolveContext` -
Debug for ResolveContext"}} />
+
Debug for ResolveContext"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl PartialEq for ResolveContext` -
PartialEq for ResolveContext"}} />
+
PartialEq for ResolveContext"}} />
#### `eq` -
eq(&self, other: &ResolveContext) -> bool"}} />
+
eq(&self, other: &ResolveContext) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl StructuralPartialEq for ResolveContext` -
StructuralPartialEq for ResolveContext"}} />
+
StructuralPartialEq for ResolveContext"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx index b6bdefcb3..15ac3e9c2 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-resolvedadapterdescriptor.mdx @@ -2,14 +2,14 @@ title: "Struct Resolved Adapter Descriptor" sidebar-title: "ResolvedAdapterDescriptor" description: "Adapter descriptor selected for a run plan." -position: 23 +position: 27 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
AdapterDescriptorSource,\n    pub path: PathBuf,\n    pub root: PathBuf,\n    pub descriptor: AdapterDescriptor,\n}"}} />
+
AdapterDescriptorSource,\n    pub path: PathBuf,\n    pub root: PathBuf,\n    pub descriptor: AdapterDescriptor,\n}"}} />
Adapter descriptor selected for a run plan. @@ -35,23 +35,23 @@ Adapter-owned compatibility and capability metadata. ### `impl Clone for ResolvedAdapterDescriptor` -
Clone for ResolvedAdapterDescriptor"}} />
+
Clone for ResolvedAdapterDescriptor"}} />
#### `clone` -
clone(&self) -> ResolvedAdapterDescriptor"}} />
+
clone(&self) -> ResolvedAdapterDescriptor"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ResolvedAdapterDescriptor` -
Debug for ResolvedAdapterDescriptor"}} />
+
Debug for ResolvedAdapterDescriptor"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ResolvedAdapterDescriptor` @@ -59,7 +59,7 @@ Adapter-owned compatibility and capability metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ResolvedAdapterDescriptor` @@ -67,11 +67,11 @@ Adapter-owned compatibility and capability metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Adapter-owned compatibility and capability metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ResolvedAdapterDescriptor` -
PartialEq for ResolvedAdapterDescriptor"}} />
+
PartialEq for ResolvedAdapterDescriptor"}} />
#### `eq` -
eq(&self, other: &ResolvedAdapterDescriptor) -> bool"}} />
+
eq(&self, other: &ResolvedAdapterDescriptor) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ResolvedAdapterDescriptor` @@ -99,8 +99,8 @@ Adapter-owned compatibility and capability metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ResolvedAdapterDescriptor` -
StructuralPartialEq for ResolvedAdapterDescriptor"}} />
+
StructuralPartialEq for ResolvedAdapterDescriptor"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx index b765bf710..4b6d442c7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runplan.mdx @@ -2,14 +2,14 @@ title: "Struct RunPlan" sidebar-title: "RunPlan" description: "Resolved NeMo Fabric run plan." -position: 24 +position: 28 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n}"}} />
+
String,\n    pub base_dir: PathBuf,\n    pub config: FabricConfig,\n    pub adapter_descriptor: Option<ResolvedAdapterDescriptor>,\n    pub resolution: Option<ResolutionStrategy>,\n    pub environment_plan: Option<EnvironmentPlan>,\n    pub capability_plan: CapabilityPlan,\n    pub capabilities: RuntimeCapabilities,\n    pub telemetry_plan: Option<TelemetryPlan>,\n}"}} />
Resolved NeMo Fabric run plan. @@ -55,23 +55,23 @@ Resolved telemetry pass-through plan. ### `impl Clone for RunPlan` -
Clone for RunPlan"}} />
+
Clone for RunPlan"}} />
#### `clone` -
clone(&self) -> RunPlan"}} />
+
clone(&self) -> RunPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RunPlan` -
Debug for RunPlan"}} />
+
Debug for RunPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RunPlan` @@ -79,7 +79,7 @@ Resolved telemetry pass-through plan. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RunPlan` @@ -87,11 +87,11 @@ Resolved telemetry pass-through plan. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -99,19 +99,19 @@ Resolved telemetry pass-through plan. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RunPlan` -
PartialEq for RunPlan"}} />
+
PartialEq for RunPlan"}} />
#### `eq` -
eq(&self, other: &RunPlan) -> bool"}} />
+
eq(&self, other: &RunPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RunPlan` @@ -119,8 +119,8 @@ Resolved telemetry pass-through plan. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RunPlan` -
StructuralPartialEq for RunPlan"}} />
+
StructuralPartialEq for RunPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx index 4599b11ca..77dbf7458 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimecapabilities.mdx @@ -2,14 +2,14 @@ title: "Struct Runtime Capabilities" sidebar-title: "RuntimeCapabilities" description: "Lifecycle behavior implemented by a resolved runtime path." -position: 25 +position: 29 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub streaming: bool,\n    pub updates: bool,\n    pub cancellation: bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub streaming: bool,\n    pub updates: bool,\n    pub cancellation: bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Lifecycle behavior implemented by a resolved runtime path. @@ -39,31 +39,31 @@ Additional adapter-specific capability metadata. ### `impl Clone for RuntimeCapabilities` -
Clone for RuntimeCapabilities"}} />
+
Clone for RuntimeCapabilities"}} />
#### `clone` -
clone(&self) -> RuntimeCapabilities"}} />
+
clone(&self) -> RuntimeCapabilities"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RuntimeCapabilities` -
Debug for RuntimeCapabilities"}} />
+
Debug for RuntimeCapabilities"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RuntimeCapabilities` -
Default for RuntimeCapabilities"}} />
+
Default for RuntimeCapabilities"}} />
#### `default` -
default() -> RuntimeCapabilities"}} />
+
default() -> RuntimeCapabilities"}} />
### `impl<'de> Deserialize<'de> for RuntimeCapabilities` @@ -71,7 +71,7 @@ Additional adapter-specific capability metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RuntimeCapabilities` @@ -79,11 +79,11 @@ Additional adapter-specific capability metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -91,19 +91,19 @@ Additional adapter-specific capability metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RuntimeCapabilities` -
PartialEq for RuntimeCapabilities"}} />
+
PartialEq for RuntimeCapabilities"}} />
#### `eq` -
eq(&self, other: &RuntimeCapabilities) -> bool"}} />
+
eq(&self, other: &RuntimeCapabilities) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RuntimeCapabilities` @@ -111,8 +111,8 @@ Additional adapter-specific capability metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RuntimeCapabilities` -
StructuralPartialEq for RuntimeCapabilities"}} />
+
StructuralPartialEq for RuntimeCapabilities"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx index 3bfdab868..fe5158125 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-runtimeconfig.mdx @@ -1,17 +1,17 @@ --- title: "Struct Runtime Config" sidebar-title: "RuntimeConfig" -description: "Runtime input/output contract." -position: 26 +description: "Invocation runtime contract." +position: 30 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub output_schema: String,\n    pub artifacts: Option<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub output_schema: String,\n    pub artifacts: Option<PathBuf>,\n    pub timeout_seconds: Option<f64>,\n    pub max_turns: Option<u32>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
-Runtime input/output contract. +Invocation runtime contract. ## Fields @@ -27,6 +27,14 @@ Output schema label. Artifact directory. +### `timeout_seconds: Option` + +Maximum duration of one invocation in seconds. + +### `max_turns: Option` + +Maximum number of harness turns within one invocation. + ### `extensions: BTreeMap` Additive normalized runtime fields. @@ -35,23 +43,23 @@ Additive normalized runtime fields. ### `impl Clone for RuntimeConfig` -
Clone for RuntimeConfig"}} />
+
Clone for RuntimeConfig"}} />
#### `clone` -
clone(&self) -> RuntimeConfig"}} />
+
clone(&self) -> RuntimeConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RuntimeConfig` -
Debug for RuntimeConfig"}} />
+
Debug for RuntimeConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RuntimeConfig` @@ -59,7 +67,7 @@ Additive normalized runtime fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RuntimeConfig` @@ -67,11 +75,11 @@ Additive normalized runtime fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +87,19 @@ Additive normalized runtime fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RuntimeConfig` -
PartialEq for RuntimeConfig"}} />
+
PartialEq for RuntimeConfig"}} />
#### `eq` -
eq(&self, other: &RuntimeConfig) -> bool"}} />
+
eq(&self, other: &RuntimeConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RuntimeConfig` @@ -99,8 +107,8 @@ Additive normalized runtime fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RuntimeConfig` -
StructuralPartialEq for RuntimeConfig"}} />
+
StructuralPartialEq for RuntimeConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx index 97f954b5a..aed265b6a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-skillconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Skill Config" sidebar-title: "SkillConfig" description: "Skill capability configuration." -position: 27 +position: 31 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Vec<PathBuf>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Skill capability configuration. @@ -27,31 +27,31 @@ Additive skill fields. ### `impl Clone for SkillConfig` -
Clone for SkillConfig"}} />
+
Clone for SkillConfig"}} />
#### `clone` -
clone(&self) -> SkillConfig"}} />
+
clone(&self) -> SkillConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for SkillConfig` -
Debug for SkillConfig"}} />
+
Debug for SkillConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for SkillConfig` -
Default for SkillConfig"}} />
+
Default for SkillConfig"}} />
#### `default` -
default() -> SkillConfig"}} />
+
default() -> SkillConfig"}} />
### `impl<'de> Deserialize<'de> for SkillConfig` @@ -59,7 +59,7 @@ Additive skill fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for SkillConfig` @@ -67,11 +67,11 @@ Additive skill fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive skill fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for SkillConfig` -
PartialEq for SkillConfig"}} />
+
PartialEq for SkillConfig"}} />
#### `eq` -
eq(&self, other: &SkillConfig) -> bool"}} />
+
eq(&self, other: &SkillConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for SkillConfig` @@ -99,8 +99,8 @@ Additive skill fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for SkillConfig` -
StructuralPartialEq for SkillConfig"}} />
+
StructuralPartialEq for SkillConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx index 9d38547b1..fb2876211 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Telemetry Config" sidebar-title: "TelemetryConfig" description: "Telemetry configuration." -position: 28 +position: 32 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
BTreeMap<TelemetryProvider, TelemetryProviderConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
BTreeMap<TelemetryProvider, TelemetryProviderConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Telemetry configuration. @@ -27,23 +27,23 @@ Additive telemetry fields. ### `impl Clone for TelemetryConfig` -
Clone for TelemetryConfig"}} />
+
Clone for TelemetryConfig"}} />
#### `clone` -
clone(&self) -> TelemetryConfig"}} />
+
clone(&self) -> TelemetryConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for TelemetryConfig` -
Debug for TelemetryConfig"}} />
+
Debug for TelemetryConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for TelemetryConfig` @@ -51,7 +51,7 @@ Additive telemetry fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for TelemetryConfig` @@ -59,11 +59,11 @@ Additive telemetry fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -71,19 +71,19 @@ Additive telemetry fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for TelemetryConfig` -
PartialEq for TelemetryConfig"}} />
+
PartialEq for TelemetryConfig"}} />
#### `eq` -
eq(&self, other: &TelemetryConfig) -> bool"}} />
+
eq(&self, other: &TelemetryConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for TelemetryConfig` @@ -91,8 +91,8 @@ Additive telemetry fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for TelemetryConfig` -
StructuralPartialEq for TelemetryConfig"}} />
+
StructuralPartialEq for TelemetryConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx index 54e815959..a7718b157 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryplan.mdx @@ -2,14 +2,14 @@ title: "Struct Telemetry Plan" sidebar-title: "TelemetryPlan" description: "Resolved telemetry plan." -position: 29 +position: 33 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<TelemetryProvider>,\n    pub relay_enabled: bool,\n    pub relay_project: Option<String>,\n    pub relay_output_dir: Option<PathBuf>,\n    pub relay_config: Option<Value>,\n    pub native_config: Option<Value>,\n    pub adapter_outputs: Vec<String>,\n}"}} />
+
Vec<TelemetryProvider>,\n    pub relay_enabled: bool,\n    pub relay_project: Option<String>,\n    pub relay_output_dir: Option<PathBuf>,\n    pub relay_config: Option<Value>,\n    pub native_config: Option<Value>,\n    pub adapter_outputs: Vec<String>,\n}"}} />
Resolved telemetry plan. @@ -47,23 +47,23 @@ Telemetry outputs declared by the selected adapter descriptor. ### `impl Clone for TelemetryPlan` -
Clone for TelemetryPlan"}} />
+
Clone for TelemetryPlan"}} />
#### `clone` -
clone(&self) -> TelemetryPlan"}} />
+
clone(&self) -> TelemetryPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for TelemetryPlan` -
Debug for TelemetryPlan"}} />
+
Debug for TelemetryPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for TelemetryPlan` @@ -71,7 +71,7 @@ Telemetry outputs declared by the selected adapter descriptor. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for TelemetryPlan` @@ -79,11 +79,11 @@ Telemetry outputs declared by the selected adapter descriptor. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -91,19 +91,19 @@ Telemetry outputs declared by the selected adapter descriptor. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for TelemetryPlan` -
PartialEq for TelemetryPlan"}} />
+
PartialEq for TelemetryPlan"}} />
#### `eq` -
eq(&self, other: &TelemetryPlan) -> bool"}} />
+
eq(&self, other: &TelemetryPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for TelemetryPlan` @@ -111,8 +111,8 @@ Telemetry outputs declared by the selected adapter descriptor. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for TelemetryPlan` -
StructuralPartialEq for TelemetryPlan"}} />
+
StructuralPartialEq for TelemetryPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx index 46719aa04..ffd028bff 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-telemetryproviderconfig.mdx @@ -2,14 +2,14 @@ title: "Struct Telemetry Provider Config" sidebar-title: "TelemetryProviderConfig" description: "Provider-specific telemetry configuration." -position: 31 +position: 35 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Option<Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Option<Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Provider-specific telemetry configuration. @@ -27,31 +27,31 @@ Additive provider fields. ### `impl Clone for TelemetryProviderConfig` -
Clone for TelemetryProviderConfig"}} />
+
Clone for TelemetryProviderConfig"}} />
#### `clone` -
clone(&self) -> TelemetryProviderConfig"}} />
+
clone(&self) -> TelemetryProviderConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for TelemetryProviderConfig` -
Debug for TelemetryProviderConfig"}} />
+
Debug for TelemetryProviderConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for TelemetryProviderConfig` -
Default for TelemetryProviderConfig"}} />
+
Default for TelemetryProviderConfig"}} />
#### `default` -
default() -> TelemetryProviderConfig"}} />
+
default() -> TelemetryProviderConfig"}} />
### `impl<'de> Deserialize<'de> for TelemetryProviderConfig` @@ -59,7 +59,7 @@ Additive provider fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for TelemetryProviderConfig` @@ -67,11 +67,11 @@ Additive provider fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additive provider fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for TelemetryProviderConfig` -
PartialEq for TelemetryProviderConfig"}} />
+
PartialEq for TelemetryProviderConfig"}} />
#### `eq` -
eq(&self, other: &TelemetryProviderConfig) -> bool"}} />
+
eq(&self, other: &TelemetryProviderConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for TelemetryProviderConfig` @@ -99,8 +99,8 @@ Additive provider fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for TelemetryProviderConfig` -
StructuralPartialEq for TelemetryProviderConfig"}} />
+
StructuralPartialEq for TelemetryProviderConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx index 9905666b1..acb6ae5dc 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsconfig.mdx @@ -2,22 +2,26 @@ title: "Struct Tools Config" sidebar-title: "ToolsConfig" description: "Harness-neutral tool capability configuration." -position: 34 +position: 36 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
Option<Vec<String>>,\n    pub blocked: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Harness-neutral tool capability configuration. ## Fields +### `enabled: Option>` + +Adapter-native tool names to expose. `None` preserves the harness default. + ### `blocked: Vec` -Adapter-native tool names or toolset names to block. +Adapter-native tool names to block. ### `extensions: BTreeMap` @@ -27,31 +31,31 @@ Additive tool configuration fields. ### `impl Clone for ToolsConfig` -
Clone for ToolsConfig"}} />
+
Clone for ToolsConfig"}} />
#### `clone` -
clone(&self) -> ToolsConfig"}} />
+
clone(&self) -> ToolsConfig"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ToolsConfig` -
Debug for ToolsConfig"}} />
+
Debug for ToolsConfig"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for ToolsConfig` -
Default for ToolsConfig"}} />
+
Default for ToolsConfig"}} />
#### `default` -
default() -> ToolsConfig"}} />
+
default() -> ToolsConfig"}} />
### `impl<'de> Deserialize<'de> for ToolsConfig` @@ -59,7 +63,7 @@ Additive tool configuration fields. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ToolsConfig` @@ -67,11 +71,11 @@ Additive tool configuration fields. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +83,19 @@ Additive tool configuration fields. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ToolsConfig` -
PartialEq for ToolsConfig"}} />
+
PartialEq for ToolsConfig"}} />
#### `eq` -
eq(&self, other: &ToolsConfig) -> bool"}} />
+
eq(&self, other: &ToolsConfig) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ToolsConfig` @@ -99,8 +103,8 @@ Additive tool configuration fields. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ToolsConfig` -
StructuralPartialEq for ToolsConfig"}} />
+
StructuralPartialEq for ToolsConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx index 3172830a7..ece23412f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/config/struct-toolsplan.mdx @@ -2,52 +2,56 @@ title: "Struct Tools Plan" sidebar-title: "ToolsPlan" description: "Normalized tool policy for a run." -position: 35 +position: 37 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Vec<String>,\n}"}} />
+
Option<Vec<String>>,\n    pub blocked: Vec<String>,\n}"}} />
Normalized tool policy for a run. ## Fields +### `enabled: Option>` + +Adapter-native tool names to expose. `None` preserves the harness default. + ### `blocked: Vec` -Adapter-native tool names or toolset names to block. +Adapter-native tool names to block. ## Trait Implementations ### `impl Clone for ToolsPlan` -
Clone for ToolsPlan"}} />
+
Clone for ToolsPlan"}} />
#### `clone` -
clone(&self) -> ToolsPlan"}} />
+
clone(&self) -> ToolsPlan"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ToolsPlan` -
Debug for ToolsPlan"}} />
+
Debug for ToolsPlan"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for ToolsPlan` -
Default for ToolsPlan"}} />
+
Default for ToolsPlan"}} />
#### `default` -
default() -> ToolsPlan"}} />
+
default() -> ToolsPlan"}} />
### `impl<'de> Deserialize<'de> for ToolsPlan` @@ -55,7 +59,7 @@ Adapter-native tool names or toolset names to block. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ToolsPlan` @@ -63,11 +67,11 @@ Adapter-native tool names or toolset names to block. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -75,19 +79,19 @@ Adapter-native tool names or toolset names to block. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ToolsPlan` -
PartialEq for ToolsPlan"}} />
+
PartialEq for ToolsPlan"}} />
#### `eq` -
eq(&self, other: &ToolsPlan) -> bool"}} />
+
eq(&self, other: &ToolsPlan) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ToolsPlan` @@ -95,8 +99,8 @@ Adapter-native tool names or toolset names to block. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ToolsPlan` -
StructuralPartialEq for ToolsPlan"}} />
+
StructuralPartialEq for ToolsPlan"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx index 522c6d126..92325691e 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/enum-doctorstatus.mdx @@ -2,7 +2,7 @@ title: "Enum Doctor Status" sidebar-title: "DoctorStatus" description: "Diagnostic status." -position: 36 +position: 41 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -43,23 +43,23 @@ Check failed. ### `impl Clone for DoctorStatus` -
Clone for DoctorStatus"}} />
+
Clone for DoctorStatus"}} />
#### `clone` -
clone(&self) -> DoctorStatus"}} />
+
clone(&self) -> DoctorStatus"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for DoctorStatus` -
Debug for DoctorStatus"}} />
+
Debug for DoctorStatus"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for DoctorStatus` @@ -67,7 +67,7 @@ Check failed. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for DoctorStatus` @@ -75,11 +75,11 @@ Check failed. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +87,19 @@ Check failed. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for DoctorStatus` -
PartialEq for DoctorStatus"}} />
+
PartialEq for DoctorStatus"}} />
#### `eq` -
eq(&self, other: &DoctorStatus) -> bool"}} />
+
eq(&self, other: &DoctorStatus) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for DoctorStatus` @@ -107,16 +107,16 @@ Check failed. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for DoctorStatus` -
Copy for DoctorStatus"}} />
+
Copy for DoctorStatus"}} />
### `impl Eq for DoctorStatus` -
Eq for DoctorStatus"}} />
+
Eq for DoctorStatus"}} />
### `impl StructuralPartialEq for DoctorStatus` -
StructuralPartialEq for DoctorStatus"}} />
+
StructuralPartialEq for DoctorStatus"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx index 94ee7e2ef..b853dc1c3 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/fn-doctor-plan.mdx @@ -2,7 +2,7 @@ title: "Function doctor_plan" sidebar-title: "doctor_plan" description: "Inspect a resolved run plan without mutating the environment." -position: 37 +position: 42 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx index 339158ce5..2315d28e1 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/index.mdx @@ -2,7 +2,7 @@ title: "Module doctor" sidebar-title: "doctor" description: "Plan diagnostics for NeMo Fabric." -position: 66 +position: 71 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx index a379216bf..677291c5c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorcheck.mdx @@ -2,14 +2,14 @@ title: "Struct Doctor Check" sidebar-title: "DoctorCheck" description: "Diagnostic check result." -position: 34 +position: 39 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub status: DoctorStatus,\n    pub message: String,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub status: DoctorStatus,\n    pub message: String,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Diagnostic check result. @@ -35,23 +35,23 @@ Optional structured metadata. ### `impl Clone for DoctorCheck` -
Clone for DoctorCheck"}} />
+
Clone for DoctorCheck"}} />
#### `clone` -
clone(&self) -> DoctorCheck"}} />
+
clone(&self) -> DoctorCheck"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for DoctorCheck` -
Debug for DoctorCheck"}} />
+
Debug for DoctorCheck"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for DoctorCheck` @@ -59,7 +59,7 @@ Optional structured metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for DoctorCheck` @@ -67,11 +67,11 @@ Optional structured metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Optional structured metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for DoctorCheck` -
PartialEq for DoctorCheck"}} />
+
PartialEq for DoctorCheck"}} />
#### `eq` -
eq(&self, other: &DoctorCheck) -> bool"}} />
+
eq(&self, other: &DoctorCheck) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for DoctorCheck` @@ -99,8 +99,8 @@ Optional structured metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for DoctorCheck` -
StructuralPartialEq for DoctorCheck"}} />
+
StructuralPartialEq for DoctorCheck"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx index c0c63f593..36cb6ffd8 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/doctor/struct-doctorreport.mdx @@ -2,14 +2,14 @@ title: "Struct Doctor Report" sidebar-title: "DoctorReport" description: "Diagnostic report for a resolved run plan." -position: 35 +position: 40 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub status: DoctorStatus,\n    pub checks: Vec<DoctorCheck>,\n}"}} />
+
String,\n    pub status: DoctorStatus,\n    pub checks: Vec<DoctorCheck>,\n}"}} />
Diagnostic report for a resolved run plan. @@ -31,23 +31,23 @@ Checks. ### `impl Clone for DoctorReport` -
Clone for DoctorReport"}} />
+
Clone for DoctorReport"}} />
#### `clone` -
clone(&self) -> DoctorReport"}} />
+
clone(&self) -> DoctorReport"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for DoctorReport` -
Debug for DoctorReport"}} />
+
Debug for DoctorReport"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for DoctorReport` @@ -55,7 +55,7 @@ Checks. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for DoctorReport` @@ -63,11 +63,11 @@ Checks. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -75,19 +75,19 @@ Checks. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for DoctorReport` -
PartialEq for DoctorReport"}} />
+
PartialEq for DoctorReport"}} />
#### `eq` -
eq(&self, other: &DoctorReport) -> bool"}} />
+
eq(&self, other: &DoctorReport) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for DoctorReport` @@ -95,8 +95,8 @@ Checks. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for DoctorReport` -
StructuralPartialEq for DoctorReport"}} />
+
StructuralPartialEq for DoctorReport"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx index 45be87627..4281f2a47 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/enum-fabricerror.mdx @@ -2,14 +2,14 @@ title: "Enum Fabric Error" sidebar-title: "FabricError" description: "Errors raised by NeMo Fabric config loading and validation." -position: 38 +position: 43 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
PathBuf,\n        source: Error,\n    },\n    PathNotFound(PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    AdapterLifecycleOperation {\n        operation: &'static str,\n        runtime_id: String,\n        code: String,\n        message: String,\n        diagnostics: String,\n    },\n    UnsupportedToolsPolicy {\n        harness: String,\n        reason: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
+
PathBuf,\n        source: Error,\n    },\n    PathNotFound(PathBuf),\n    UnknownAdapter {\n        adapter_id: String,\n        available: Vec<String>,\n    },\n    AdapterDescriptorMismatch {\n        path: PathBuf,\n        field: &'static str,\n        expected: String,\n        actual: String,\n    },\n    AdapterDescriptorUnsupported {\n        adapter_id: String,\n        field: &'static str,\n        value: String,\n    },\n    InvalidAdapterDescriptor {\n        path: PathBuf,\n        message: String,\n    },\n    InvalidConfig {\n        field: String,\n        reason: String,\n    },\n    AdapterCompatibility {\n        adapter_id: String,\n        field: String,\n        reason: String,\n    },\n    UnknownSchema {\n        schema: String,\n        available: Vec<String>,\n    },\n    UnsupportedRuntimeAdapter {\n        harness: String,\n        adapter_kind: AdapterKind,\n    },\n    AdapterLifecycleOperation {\n        operation: &'static str,\n        runtime_id: String,\n        code: String,\n        message: String,\n        diagnostics: String,\n    },\n    RuntimeHandleMismatch {\n        field: &'static str,\n        expected: String,\n        actual: String,\n        runtime_id: String,\n    },\n    UnsupportedEnvironmentProvider {\n        provider: String,\n        adapter_kind: AdapterKind,\n    },\n    InvalidProcessSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    InvalidPythonSettings {\n        path: PathBuf,\n        source: Error,\n    },\n    PythonInterpreterUnavailable {\n        path: PathBuf,\n        origin: String,\n        reason: String,\n    },\n    ProcessRunner {\n        command: String,\n        source: Error,\n    },\n    SerializeJson(Error),\n    Read {\n        path: PathBuf,\n        source: Error,\n    },\n    Write {\n        path: PathBuf,\n        source: Error,\n    },\n    ParseJson {\n        path: PathBuf,\n        source: Error,\n    },\n}"}} />
Errors raised by NeMo Fabric config loading and validation. @@ -33,7 +33,7 @@ Underlying path-resolution error. ### `PathNotFound(PathBuf)` -
PathBuf)"}} />
+
PathBuf)"}} />
The requested path does not exist. @@ -113,6 +113,42 @@ Adapter descriptor path. Validation message. +### `InvalidConfig` + +
+ +A normalized Fabric config field is invalid. + +#### Fields + +### `field: String` + +Canonical configuration path. + +### `reason: String` + +Validation failure. + +### `AdapterCompatibility` + +
+ +A valid normalized field cannot be implemented by the selected adapter. + +#### Fields + +### `adapter_id: String` + +Selected adapter id. + +### `field: String` + +Canonical configuration path. + +### `reason: String` + +Compatibility failure. + ### `UnknownSchema`
@@ -173,22 +209,6 @@ Human-readable failure message. Bounded adapter-host diagnostics. -### `UnsupportedToolsPolicy` - -
- -The selected harness cannot enforce the configured blocked-tools policy. - -#### Fields - -### `harness: String` - -Harness type. - -### `reason: String` - -Capability-routing explanation. - ### `RuntimeHandleMismatch`
@@ -355,36 +375,36 @@ Underlying JSON error. ### `impl Debug for FabricError` -
Debug for FabricError"}} />
+
Debug for FabricError"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Display for FabricError` -
Display for FabricError"}} />
+
Display for FabricError"}} />
#### `fmt` -
fmt(&self, __formatter: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, __formatter: &mut Formatter<'_>) -> Result"}} />
### `impl Error for FabricError` -
Error for FabricError"}} />
+
Error for FabricError"}} />
#### `source` -
source(&self) -> Option<&(dyn Error + 'static)>"}} />
+
source(&self) -> Option<&(dyn Error + 'static)>"}} />
#### `description` -
description(&self) -> &str"}} />
+
description(&self) -> &str"}} />
#### `cause` -
cause(&self) -> Option<&dyn Error>"}} />
+
cause(&self) -> Option<&dyn Error>"}} />
#### `provide` -
provide<'a>(&'a self, request: &mut Request<'a>)"}} />
+
provide<'a>(&'a self, request: &mut Request<'a>)"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx index aeedf7670..4d130d963 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/index.mdx @@ -2,7 +2,7 @@ title: "Module error" sidebar-title: "error" description: "Error types for NeMo Fabric core." -position: 67 +position: 72 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx index e1f0f1fde..5812415f7 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/error/type-result.mdx @@ -2,14 +2,14 @@ title: "Type Alias Result" sidebar-title: "Result" description: "Core NeMo Fabric result type." -position: 39 +position: 44 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Result<T, FabricError>;"}} />
+
Result<T, FabricError>;"}} />
Core NeMo Fabric result type. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx index b9f5b49fb..7cc834ade 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/fn-version.mdx @@ -2,13 +2,13 @@ title: "Function version" sidebar-title: "version" description: "Returns the crate version compiled into this build." -position: 70 +position: 75 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
str"}} />
+
str"}} />
Returns the crate version compiled into this build. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx index 7816da585..808547014 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/index.mdx @@ -14,6 +14,7 @@ Core config and runtime contract for NeMo Fabric. ## Re-exports - `pub use config::ADAPTER_CONTRACT_VERSION;` +- `pub use config::AdapterConfigField;` - `pub use config::AdapterConfigSupport;` - `pub use config::AdapterDescriptor;` - `pub use config::AdapterDescriptorSource;` @@ -28,6 +29,9 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::EnvironmentPlan;` - `pub use config::FabricConfig;` - `pub use config::HarnessConfig;` +- `pub use config::InstructionConfig;` +- `pub use config::InstructionMode;` +- `pub use config::InstructionsConfig;` - `pub use config::McpConfig;` - `pub use config::McpExposure;` - `pub use config::McpServerPlan;` @@ -44,6 +48,7 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::TelemetryPlan;` - `pub use config::TelemetryProvider;` - `pub use config::TelemetryProviderConfig;` +- `pub use config::ToolsConfig;` - `pub use config::load_adapter_descriptor;` - `pub use config::resolve_run_plan_from_config;` - `pub use doctor::DoctorCheck;` diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx index af8e4e584..45003addc 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-errorstage.mdx @@ -78,23 +78,23 @@ Artifact collection or writing failed. ### `impl Clone for ErrorStage` -
Clone for ErrorStage"}} />
+
Clone for ErrorStage"}} />
#### `clone` -
clone(&self) -> ErrorStage"}} />
+
clone(&self) -> ErrorStage"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ErrorStage` -
Debug for ErrorStage"}} />
+
Debug for ErrorStage"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ErrorStage` @@ -102,7 +102,7 @@ Artifact collection or writing failed. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ErrorStage` @@ -110,11 +110,11 @@ Artifact collection or writing failed. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -122,19 +122,19 @@ Artifact collection or writing failed. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ErrorStage` -
PartialEq for ErrorStage"}} />
+
PartialEq for ErrorStage"}} />
#### `eq` -
eq(&self, other: &ErrorStage) -> bool"}} />
+
eq(&self, other: &ErrorStage) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ErrorStage` @@ -142,16 +142,16 @@ Artifact collection or writing failed. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for ErrorStage` -
Copy for ErrorStage"}} />
+
Copy for ErrorStage"}} />
### `impl Eq for ErrorStage` -
Eq for ErrorStage"}} />
+
Eq for ErrorStage"}} />
### `impl StructuralPartialEq for ErrorStage` -
StructuralPartialEq for ErrorStage"}} />
+
StructuralPartialEq for ErrorStage"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-runstatus.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-runstatus.mdx index a4cefeb0d..853a2375c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-runstatus.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/enum-runstatus.mdx @@ -43,23 +43,23 @@ The invocation or runtime was cancelled. ### `impl Clone for RunStatus` -
Clone for RunStatus"}} />
+
Clone for RunStatus"}} />
#### `clone` -
clone(&self) -> RunStatus"}} />
+
clone(&self) -> RunStatus"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RunStatus` -
Debug for RunStatus"}} />
+
Debug for RunStatus"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RunStatus` @@ -67,7 +67,7 @@ The invocation or runtime was cancelled. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RunStatus` @@ -75,11 +75,11 @@ The invocation or runtime was cancelled. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +87,19 @@ The invocation or runtime was cancelled. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RunStatus` -
PartialEq for RunStatus"}} />
+
PartialEq for RunStatus"}} />
#### `eq` -
eq(&self, other: &RunStatus) -> bool"}} />
+
eq(&self, other: &RunStatus) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RunStatus` @@ -107,16 +107,16 @@ The invocation or runtime was cancelled. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl Copy for RunStatus` -
Copy for RunStatus"}} />
+
Copy for RunStatus"}} />
### `impl Eq for RunStatus` -
Eq for RunStatus"}} />
+
Eq for RunStatus"}} />
### `impl StructuralPartialEq for RunStatus` -
StructuralPartialEq for RunStatus"}} />
+
StructuralPartialEq for RunStatus"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/fn-stop-runtime.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/fn-stop-runtime.mdx index 3d5bae34d..5987188a4 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/fn-stop-runtime.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/fn-stop-runtime.mdx @@ -9,6 +9,6 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
RunPlan,\n    runtime: &RuntimeHandle,\n) -> Result<Vec<FabricEvent>>"}} />
+
RunPlan,\n    runtime: &RuntimeHandle,\n) -> Result<Vec<FabricEvent>>"}} />
Stop or detach from a harness runtime. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx index 28e72ed50..5131bbd06 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/index.mdx @@ -2,7 +2,7 @@ title: "Module runtime" sidebar-title: "runtime" description: "Runtime invocation helpers." -position: 68 +position: 73 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx index 299dc460f..f810c4502 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-adapterinvocation.mdx @@ -27,23 +27,23 @@ Typed caller request for this invocation. ### `impl Clone for AdapterInvocation` -
Clone for AdapterInvocation"}} />
+
Clone for AdapterInvocation"}} />
#### `clone` -
clone(&self) -> AdapterInvocation"}} />
+
clone(&self) -> AdapterInvocation"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for AdapterInvocation` -
Debug for AdapterInvocation"}} />
+
Debug for AdapterInvocation"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for AdapterInvocation` @@ -51,7 +51,7 @@ Typed caller request for this invocation. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for AdapterInvocation` @@ -59,11 +59,11 @@ Typed caller request for this invocation. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -71,19 +71,19 @@ Typed caller request for this invocation. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for AdapterInvocation` -
PartialEq for AdapterInvocation"}} />
+
PartialEq for AdapterInvocation"}} />
#### `eq` -
eq(&self, other: &AdapterInvocation) -> bool"}} />
+
eq(&self, other: &AdapterInvocation) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for AdapterInvocation` @@ -91,8 +91,8 @@ Typed caller request for this invocation. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for AdapterInvocation` -
StructuralPartialEq for AdapterInvocation"}} />
+
StructuralPartialEq for AdapterInvocation"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactmanifest.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactmanifest.mdx index acb71cbbd..7b3bbbf7c 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactmanifest.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactmanifest.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Option<PathBuf>,\n    pub artifacts: Vec<ArtifactRef>,\n}"}} />
+
Option<PathBuf>,\n    pub artifacts: Vec<ArtifactRef>,\n}"}} />
Manifest of run artifacts. @@ -27,31 +27,31 @@ Artifact entries. ### `impl Clone for ArtifactManifest` -
Clone for ArtifactManifest"}} />
+
Clone for ArtifactManifest"}} />
#### `clone` -
clone(&self) -> ArtifactManifest"}} />
+
clone(&self) -> ArtifactManifest"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ArtifactManifest` -
Debug for ArtifactManifest"}} />
+
Debug for ArtifactManifest"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for ArtifactManifest` -
Default for ArtifactManifest"}} />
+
Default for ArtifactManifest"}} />
#### `default` -
default() -> ArtifactManifest"}} />
+
default() -> ArtifactManifest"}} />
### `impl<'de> Deserialize<'de> for ArtifactManifest` @@ -59,7 +59,7 @@ Artifact entries. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ArtifactManifest` @@ -67,11 +67,11 @@ Artifact entries. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Artifact entries. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ArtifactManifest` -
PartialEq for ArtifactManifest"}} />
+
PartialEq for ArtifactManifest"}} />
#### `eq` -
eq(&self, other: &ArtifactManifest) -> bool"}} />
+
eq(&self, other: &ArtifactManifest) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ArtifactManifest` @@ -99,8 +99,8 @@ Artifact entries. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ArtifactManifest` -
StructuralPartialEq for ArtifactManifest"}} />
+
StructuralPartialEq for ArtifactManifest"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactref.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactref.mdx index d8b2dd5a5..49ac4e92b 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactref.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-artifactref.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub kind: String,\n    pub path: PathBuf,\n    pub media_type: Option<String>,\n}"}} />
+
String,\n    pub kind: String,\n    pub path: PathBuf,\n    pub media_type: Option<String>,\n}"}} />
Reference to one artifact. @@ -35,23 +35,23 @@ Optional media type. ### `impl Clone for ArtifactRef` -
Clone for ArtifactRef"}} />
+
Clone for ArtifactRef"}} />
#### `clone` -
clone(&self) -> ArtifactRef"}} />
+
clone(&self) -> ArtifactRef"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ArtifactRef` -
Debug for ArtifactRef"}} />
+
Debug for ArtifactRef"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ArtifactRef` @@ -59,7 +59,7 @@ Optional media type. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ArtifactRef` @@ -67,11 +67,11 @@ Optional media type. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Optional media type. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ArtifactRef` -
PartialEq for ArtifactRef"}} />
+
PartialEq for ArtifactRef"}} />
#### `eq` -
eq(&self, other: &ArtifactRef) -> bool"}} />
+
eq(&self, other: &ArtifactRef) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ArtifactRef` @@ -99,8 +99,8 @@ Optional media type. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ArtifactRef` -
StructuralPartialEq for ArtifactRef"}} />
+
StructuralPartialEq for ArtifactRef"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-environmenthandle.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-environmenthandle.mdx index f354af017..e3a719266 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-environmenthandle.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-environmenthandle.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub provider: String,\n    pub control_location: ControlLocation,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub ownership: EnvironmentOwnership,\n    pub connection: BTreeMap<String, Value>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub provider: String,\n    pub control_location: ControlLocation,\n    pub workspace: Option<PathBuf>,\n    pub artifacts: Option<PathBuf>,\n    pub env: BTreeMap<String, String>,\n    pub ownership: EnvironmentOwnership,\n    pub connection: BTreeMap<String, Value>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Resolved execution environment context. @@ -35,6 +35,10 @@ Workspace visible to the harness runtime. Artifact root visible to the harness runtime. +### `env: BTreeMap` + +Environment variables visible to the harness and its tools. + ### `ownership: EnvironmentOwnership` Whether NeMo Fabric owns the environment resource. @@ -51,23 +55,23 @@ Provider-specific metadata. ### `impl Clone for EnvironmentHandle` -
Clone for EnvironmentHandle"}} />
+
Clone for EnvironmentHandle"}} />
#### `clone` -
clone(&self) -> EnvironmentHandle"}} />
+
clone(&self) -> EnvironmentHandle"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for EnvironmentHandle` -
Debug for EnvironmentHandle"}} />
+
Debug for EnvironmentHandle"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for EnvironmentHandle` @@ -75,7 +79,7 @@ Provider-specific metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for EnvironmentHandle` @@ -83,11 +87,11 @@ Provider-specific metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -95,19 +99,19 @@ Provider-specific metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for EnvironmentHandle` -
PartialEq for EnvironmentHandle"}} />
+
PartialEq for EnvironmentHandle"}} />
#### `eq` -
eq(&self, other: &EnvironmentHandle) -> bool"}} />
+
eq(&self, other: &EnvironmentHandle) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for EnvironmentHandle` @@ -115,8 +119,8 @@ Provider-specific metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for EnvironmentHandle` -
StructuralPartialEq for EnvironmentHandle"}} />
+
StructuralPartialEq for EnvironmentHandle"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-errorinfo.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-errorinfo.mdx index 9468739a0..961340a85 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-errorinfo.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-errorinfo.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
ErrorStage,\n    pub code: String,\n    pub message: String,\n    pub retryable: bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
ErrorStage,\n    pub code: String,\n    pub message: String,\n    pub retryable: bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Normalized error metadata. @@ -39,23 +39,23 @@ Adapter or runtime metadata useful for diagnostics. ### `impl Clone for ErrorInfo` -
Clone for ErrorInfo"}} />
+
Clone for ErrorInfo"}} />
#### `clone` -
clone(&self) -> ErrorInfo"}} />
+
clone(&self) -> ErrorInfo"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for ErrorInfo` -
Debug for ErrorInfo"}} />
+
Debug for ErrorInfo"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for ErrorInfo` @@ -63,7 +63,7 @@ Adapter or runtime metadata useful for diagnostics. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for ErrorInfo` @@ -71,11 +71,11 @@ Adapter or runtime metadata useful for diagnostics. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Adapter or runtime metadata useful for diagnostics. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for ErrorInfo` -
PartialEq for ErrorInfo"}} />
+
PartialEq for ErrorInfo"}} />
#### `eq` -
eq(&self, other: &ErrorInfo) -> bool"}} />
+
eq(&self, other: &ErrorInfo) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for ErrorInfo` @@ -103,8 +103,8 @@ Adapter or runtime metadata useful for diagnostics. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for ErrorInfo` -
StructuralPartialEq for ErrorInfo"}} />
+
StructuralPartialEq for ErrorInfo"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-fabricevent.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-fabricevent.mdx index 5330e632a..b55252841 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-fabricevent.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-fabricevent.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub timestamp_millis: u128,\n    pub kind: String,\n    pub message: String,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub timestamp_millis: u128,\n    pub kind: String,\n    pub message: String,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
NeMo Fabric lifecycle or progress event. @@ -39,23 +39,23 @@ Event metadata. ### `impl Clone for FabricEvent` -
Clone for FabricEvent"}} />
+
Clone for FabricEvent"}} />
#### `clone` -
clone(&self) -> FabricEvent"}} />
+
clone(&self) -> FabricEvent"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for FabricEvent` -
Debug for FabricEvent"}} />
+
Debug for FabricEvent"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for FabricEvent` @@ -63,7 +63,7 @@ Event metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for FabricEvent` @@ -71,11 +71,11 @@ Event metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -83,19 +83,19 @@ Event metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for FabricEvent` -
PartialEq for FabricEvent"}} />
+
PartialEq for FabricEvent"}} />
#### `eq` -
eq(&self, other: &FabricEvent) -> bool"}} />
+
eq(&self, other: &FabricEvent) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for FabricEvent` @@ -103,8 +103,8 @@ Event metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for FabricEvent` -
StructuralPartialEq for FabricEvent"}} />
+
StructuralPartialEq for FabricEvent"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-invocationhandle.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-invocationhandle.mdx index 3f3ac0d42..3b603f62a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-invocationhandle.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-invocationhandle.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub request_id: String,\n    pub runtime_id: String,\n}"}} />
+
String,\n    pub request_id: String,\n    pub runtime_id: String,\n}"}} />
One request sent to a runtime. @@ -31,23 +31,23 @@ Runtime id. ### `impl Clone for InvocationHandle` -
Clone for InvocationHandle"}} />
+
Clone for InvocationHandle"}} />
#### `clone` -
clone(&self) -> InvocationHandle"}} />
+
clone(&self) -> InvocationHandle"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for InvocationHandle` -
Debug for InvocationHandle"}} />
+
Debug for InvocationHandle"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for InvocationHandle` @@ -55,7 +55,7 @@ Runtime id. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for InvocationHandle` @@ -63,11 +63,11 @@ Runtime id. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -75,19 +75,19 @@ Runtime id. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for InvocationHandle` -
PartialEq for InvocationHandle"}} />
+
PartialEq for InvocationHandle"}} />
#### `eq` -
eq(&self, other: &InvocationHandle) -> bool"}} />
+
eq(&self, other: &InvocationHandle) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for InvocationHandle` @@ -95,8 +95,8 @@ Runtime id. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for InvocationHandle` -
StructuralPartialEq for InvocationHandle"}} />
+
StructuralPartialEq for InvocationHandle"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx index 513777d2c..17d74a30f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runrequest.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub input: Value,\n    pub context: BTreeMap<String, Value>,\n    pub overrides: Option<Value>,\n}"}} />
+
String,\n    pub input: Value,\n    pub context: BTreeMap<String, Value>,\n    pub overrides: Option<Value>,\n}"}} />
A request passed to a NeMo Fabric-managed harness runtime. @@ -39,7 +39,7 @@ Per-invocation overrides allowed by the resolved config. #### `text` -
Into<String>) -> Self"}} />
+
Into<String>) -> Self"}} />
Build a text request. @@ -47,31 +47,31 @@ Build a text request. ### `impl Clone for RunRequest` -
Clone for RunRequest"}} />
+
Clone for RunRequest"}} />
#### `clone` -
clone(&self) -> RunRequest"}} />
+
clone(&self) -> RunRequest"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RunRequest` -
Debug for RunRequest"}} />
+
Debug for RunRequest"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl Default for RunRequest` -
Default for RunRequest"}} />
+
Default for RunRequest"}} />
#### `default` -
default() -> RunRequest"}} />
+
default() -> RunRequest"}} />
### `impl<'de> Deserialize<'de> for RunRequest` @@ -79,7 +79,7 @@ Build a text request. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RunRequest` @@ -87,11 +87,11 @@ Build a text request. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -99,19 +99,19 @@ Build a text request. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RunRequest` -
PartialEq for RunRequest"}} />
+
PartialEq for RunRequest"}} />
#### `eq` -
eq(&self, other: &RunRequest) -> bool"}} />
+
eq(&self, other: &RunRequest) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RunRequest` @@ -119,8 +119,8 @@ Build a text request. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RunRequest` -
StructuralPartialEq for RunRequest"}} />
+
StructuralPartialEq for RunRequest"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx index 07939487d..999a9d313 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runresult.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub runtime_id: String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub status: RunStatus,\n    pub output: Value,\n    pub error: Option<ErrorInfo>,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<TelemetryRef>,\n    pub events: Vec<FabricEvent>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub runtime_id: String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub status: RunStatus,\n    pub output: Value,\n    pub error: Option<ErrorInfo>,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<TelemetryRef>,\n    pub events: Vec<FabricEvent>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Result from a NeMo Fabric-managed harness invocation. @@ -75,23 +75,23 @@ Adapter-specific metadata. ### `impl Clone for RunResult` -
Clone for RunResult"}} />
+
Clone for RunResult"}} />
#### `clone` -
clone(&self) -> RunResult"}} />
+
clone(&self) -> RunResult"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RunResult` -
Debug for RunResult"}} />
+
Debug for RunResult"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RunResult` @@ -99,7 +99,7 @@ Adapter-specific metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RunResult` @@ -107,11 +107,11 @@ Adapter-specific metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -119,19 +119,19 @@ Adapter-specific metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RunResult` -
PartialEq for RunResult"}} />
+
PartialEq for RunResult"}} />
#### `eq` -
eq(&self, other: &RunResult) -> bool"}} />
+
eq(&self, other: &RunResult) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RunResult` @@ -139,8 +139,8 @@ Adapter-specific metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RunResult` -
StructuralPartialEq for RunResult"}} />
+
StructuralPartialEq for RunResult"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimecontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimecontext.mdx index 070c793a8..134ab5a1a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimecontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimecontext.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub environment: EnvironmentHandle,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<RuntimeTelemetryContext>,\n}"}} />
+
String,\n    pub invocation_id: String,\n    pub request_id: String,\n    pub environment: EnvironmentHandle,\n    pub artifacts: ArtifactManifest,\n    pub telemetry: Option<RuntimeTelemetryContext>,\n}"}} />
Context generated for one invocation of a started runtime. @@ -43,23 +43,23 @@ Runtime telemetry context generated for this invocation. ### `impl Clone for RuntimeContext` -
Clone for RuntimeContext"}} />
+
Clone for RuntimeContext"}} />
#### `clone` -
clone(&self) -> RuntimeContext"}} />
+
clone(&self) -> RuntimeContext"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RuntimeContext` -
Debug for RuntimeContext"}} />
+
Debug for RuntimeContext"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RuntimeContext` @@ -67,7 +67,7 @@ Runtime telemetry context generated for this invocation. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RuntimeContext` @@ -75,11 +75,11 @@ Runtime telemetry context generated for this invocation. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -87,19 +87,19 @@ Runtime telemetry context generated for this invocation. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RuntimeContext` -
PartialEq for RuntimeContext"}} />
+
PartialEq for RuntimeContext"}} />
#### `eq` -
eq(&self, other: &RuntimeContext) -> bool"}} />
+
eq(&self, other: &RuntimeContext) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RuntimeContext` @@ -107,8 +107,8 @@ Runtime telemetry context generated for this invocation. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RuntimeContext` -
StructuralPartialEq for RuntimeContext"}} />
+
StructuralPartialEq for RuntimeContext"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimehandle.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimehandle.mdx index 49394e7d1..b7e37f57a 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimehandle.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimehandle.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
String,\n    pub runtime_binding: String,\n    pub agent_name: String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub environment: EnvironmentHandle,\n}"}} />
+
String,\n    pub runtime_binding: String,\n    pub agent_name: String,\n    pub harness: String,\n    pub adapter_kind: AdapterKind,\n    pub adapter_id: Option<String>,\n    pub environment: EnvironmentHandle,\n}"}} />
Active or resumable harness runtime. @@ -47,23 +47,23 @@ Prepared environment. ### `impl Clone for RuntimeHandle` -
Clone for RuntimeHandle"}} />
+
Clone for RuntimeHandle"}} />
#### `clone` -
clone(&self) -> RuntimeHandle"}} />
+
clone(&self) -> RuntimeHandle"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RuntimeHandle` -
Debug for RuntimeHandle"}} />
+
Debug for RuntimeHandle"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RuntimeHandle` @@ -71,7 +71,7 @@ Prepared environment. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RuntimeHandle` @@ -79,11 +79,11 @@ Prepared environment. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -91,19 +91,19 @@ Prepared environment. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RuntimeHandle` -
PartialEq for RuntimeHandle"}} />
+
PartialEq for RuntimeHandle"}} />
#### `eq` -
eq(&self, other: &RuntimeHandle) -> bool"}} />
+
eq(&self, other: &RuntimeHandle) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RuntimeHandle` @@ -111,8 +111,8 @@ Prepared environment. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RuntimeHandle` -
StructuralPartialEq for RuntimeHandle"}} />
+
StructuralPartialEq for RuntimeHandle"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimetelemetrycontext.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimetelemetrycontext.mdx index ff03e8ee5..7039df5e0 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimetelemetrycontext.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-runtimetelemetrycontext.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub config_path: Option<PathBuf>,\n    pub env: BTreeMap<String, String>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub config_path: Option<PathBuf>,\n    pub env: BTreeMap<String, String>,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Runtime telemetry config passed to adapters. @@ -35,23 +35,23 @@ Additional telemetry metadata surfaced to consumers and adapters. ### `impl Clone for RuntimeTelemetryContext` -
Clone for RuntimeTelemetryContext"}} />
+
Clone for RuntimeTelemetryContext"}} />
#### `clone` -
clone(&self) -> RuntimeTelemetryContext"}} />
+
clone(&self) -> RuntimeTelemetryContext"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for RuntimeTelemetryContext` -
Debug for RuntimeTelemetryContext"}} />
+
Debug for RuntimeTelemetryContext"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for RuntimeTelemetryContext` @@ -59,7 +59,7 @@ Additional telemetry metadata surfaced to consumers and adapters. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for RuntimeTelemetryContext` @@ -67,11 +67,11 @@ Additional telemetry metadata surfaced to consumers and adapters. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -79,19 +79,19 @@ Additional telemetry metadata surfaced to consumers and adapters. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for RuntimeTelemetryContext` -
PartialEq for RuntimeTelemetryContext"}} />
+
PartialEq for RuntimeTelemetryContext"}} />
#### `eq` -
eq(&self, other: &RuntimeTelemetryContext) -> bool"}} />
+
eq(&self, other: &RuntimeTelemetryContext) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for RuntimeTelemetryContext` @@ -99,8 +99,8 @@ Additional telemetry metadata surfaced to consumers and adapters. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for RuntimeTelemetryContext` -
StructuralPartialEq for RuntimeTelemetryContext"}} />
+
StructuralPartialEq for RuntimeTelemetryContext"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-telemetryref.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-telemetryref.mdx index 1ae9136e5..c80997d65 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-telemetryref.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/runtime/struct-telemetryref.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
+
bool,\n    pub metadata: BTreeMap<String, Value>,\n}"}} />
Reference to telemetry emitted by Relay or another configured telemetry path. @@ -27,23 +27,23 @@ Telemetry metadata. ### `impl Clone for TelemetryRef` -
Clone for TelemetryRef"}} />
+
Clone for TelemetryRef"}} />
#### `clone` -
clone(&self) -> TelemetryRef"}} />
+
clone(&self) -> TelemetryRef"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for TelemetryRef` -
Debug for TelemetryRef"}} />
+
Debug for TelemetryRef"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl<'de> Deserialize<'de> for TelemetryRef` @@ -51,7 +51,7 @@ Telemetry metadata. #### `deserialize` -
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
### `impl JsonSchema for TelemetryRef` @@ -59,11 +59,11 @@ Telemetry metadata. #### `schema_name` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `schema_id` -
Cow<'static, str>"}} />
+
Cow<'static, str>"}} />
#### `json_schema` @@ -71,19 +71,19 @@ Telemetry metadata. #### `inline_schema` -
bool"}} />
+
bool"}} />
### `impl PartialEq for TelemetryRef` -
PartialEq for TelemetryRef"}} />
+
PartialEq for TelemetryRef"}} />
#### `eq` -
eq(&self, other: &TelemetryRef) -> bool"}} />
+
eq(&self, other: &TelemetryRef) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Serialize for TelemetryRef` @@ -91,8 +91,8 @@ Telemetry metadata. #### `serialize` -
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
### `impl StructuralPartialEq for TelemetryRef` -
StructuralPartialEq for TelemetryRef"}} />
+
StructuralPartialEq for TelemetryRef"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx index 2460d6f0d..1a2baf9fb 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/enum-schemaname.mdx @@ -117,25 +117,25 @@ NeMo Fabric lifecycle event schema. #### `ALL` -
13]"}} />
+
13]"}} />
All public schemas in stable output order. #### `as_str` -
str"}} />
+
str"}} />
Stable file stem for this schema. #### `filename` -
String"}} />
+
String"}} />
Snapshot filename for this schema. #### `parse` -
str) -> Result<Self>"}} />
+
str) -> Result<Self>"}} />
Parse a schema name from CLI/user input. @@ -143,44 +143,44 @@ Parse a schema name from CLI/user input. ### `impl Clone for SchemaName` -
Clone for SchemaName"}} />
+
Clone for SchemaName"}} />
#### `clone` -
clone(&self) -> SchemaName"}} />
+
clone(&self) -> SchemaName"}} />
#### `clone_from` -
clone_from(&mut self, source: &Self)"}} />
+
clone_from(&mut self, source: &Self)"}} />
### `impl Debug for SchemaName` -
Debug for SchemaName"}} />
+
Debug for SchemaName"}} />
#### `fmt` -
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
### `impl PartialEq for SchemaName` -
PartialEq for SchemaName"}} />
+
PartialEq for SchemaName"}} />
#### `eq` -
eq(&self, other: &SchemaName) -> bool"}} />
+
eq(&self, other: &SchemaName) -> bool"}} />
#### `ne` -
ne(&self, other: &Rhs) -> bool"}} />
+
ne(&self, other: &Rhs) -> bool"}} />
### `impl Copy for SchemaName` -
Copy for SchemaName"}} />
+
Copy for SchemaName"}} />
### `impl Eq for SchemaName` -
Eq for SchemaName"}} />
+
Eq for SchemaName"}} />
### `impl StructuralPartialEq for SchemaName` -
StructuralPartialEq for SchemaName"}} />
+
StructuralPartialEq for SchemaName"}} />
diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-all-schemas.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-all-schemas.mdx index b66c74e6d..6814b4cd4 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-all-schemas.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-all-schemas.mdx @@ -9,6 +9,6 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
Result<BTreeMap<String, Value>>"}} />
+
Result<BTreeMap<String, Value>>"}} />
Generate all schemas keyed by stable schema name. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-schema-json.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-schema-json.mdx index 74e2b0c1f..a309cde57 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-schema-json.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-generate-schema-json.mdx @@ -9,6 +9,6 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
SchemaName) -> Result<String>"}} />
+
SchemaName) -> Result<String>"}} />
Generate one schema as pretty JSON. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-write-schema-snapshots.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-write-schema-snapshots.mdx index 0f33a5804..d97fca21f 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-write-schema-snapshots.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/fn-write-schema-snapshots.mdx @@ -9,6 +9,6 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p nemo-fabric-core`. -
AsRef<Path>,\n) -> Result<Vec<PathBuf>>"}} />
+
AsRef<Path>,\n) -> Result<Vec<PathBuf>>"}} />
Write all schema snapshots to `directory`. diff --git a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx index 1ac349917..9f9937838 100644 --- a/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx +++ b/docs/reference/api/rust-library-reference/nemo-fabric-core/schema/index.mdx @@ -2,7 +2,7 @@ title: "Module schema" sidebar-title: "schema" description: "JSON Schema generation for the public NeMo Fabric contract." -position: 69 +position: 74 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/docs/sdk/python.mdx b/docs/sdk/python.mdx index ea1eb9322..a6ca58fa1 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -40,13 +40,23 @@ from nemo_fabric import ( Fabric, FabricConfig, HarnessConfig, + InstructionConfig, + InstructionsConfig, MetadataConfig, ModelConfig, + RuntimeConfig, ) config = FabricConfig( metadata=MetadataConfig(name="review-agent"), harness=HarnessConfig(adapter_id="nvidia.fabric.hermes"), + instructions=InstructionsConfig( + system=InstructionConfig( + content="Review code for concrete correctness risks.", + mode="replace", + ) + ), + runtime=RuntimeConfig(max_turns=8), models={ "default": ModelConfig( provider="nvidia", @@ -130,28 +140,6 @@ 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`. -### Bundled Adapter Capability Matrix - -The following matrix summarizes the current bundled adapter descriptors. The -descriptor selected in `RunPlan` remains authoritative. Telemetry output names -use the descriptor contract values. - -| Adapter ID | Models | Tools / Blocked Tools | MCP | Skills | Subagents | Telemetry | Persistent Local Host | Remote Service | -| --- | --- | --- | --- | --- | --- | --- | --- | --- | -| `nvidia.fabric.claude` | Anthropic provider and Claude models | `allowed_tools` adapter setting / normalized `tools.blocked` | Normalized | Normalized | Not exposed | NVIDIA NeMo Relay: `atif`, `otel`, and `openinference` through hooks and gateway | Yes: connected `ClaudeSDKClient`, session, and optional NeMo Relay gateway | Not implemented | -| `nvidia.fabric.codex` | Built-in OpenAI and custom NVIDIA Responses providers with Codex-compatible models | Not normalized | Normalized: stdio, HTTP, and streamable HTTP | Normalized: `SKILL.md` directories | Not exposed | NeMo Relay: `atif`, `otel`, and `openinference` through hooks and gateway; native `otel` | Yes: `AsyncCodex` app-server client, thread, and optional NeMo Relay gateway | Not implemented | -| `nvidia.fabric.langchain.deepagents` | NVIDIA, OpenAI, OpenAI-compatible, and other LangChain providers | Built-ins and MCP / normalized middleware block list | Normalized | Normalized | Constrained: declarative local subagents inherit parent capabilities | NeMo Relay SDK: `atif`, `otel`, and `openinference`; native `otel` and `openinference` | Yes: compiled graph and async LangGraph checkpointer | Not implemented | -| `nvidia.fabric.hermes` | Normalized provider, model, and base URL | Toolsets / normalized disabled toolsets | Normalized | Normalized | Not exposed | NeMo Relay plugin: `atif`, `otel`, and `openinference` | Yes: `AIAgent`, `SessionDB`, and NeMo Relay plugin context | Not implemented | - -"Normalized" means the adapter accepts the corresponding `FabricConfig` -field and maps it to the harness. "Not normalized" does not mean that the -underlying harness lacks the feature; it means that NeMo Fabric does not expose a -portable configuration surface for it. NeMo Fabric currently normalizes -`tools.blocked`, not a portable tool definition catalog. Deep Agents supports -only JSON-shaped, in-process subagent definitions through -`harness.settings.deepagents.subagents`; independently configured or remote -subagent capabilities are not exposed. - 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 @@ -167,6 +155,59 @@ 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: + +| `FabricConfig` Field | Claude | Codex | Deep Agents | Hermes Agent | +| --- | --- | --- | --- | --- | +| `schema_version` | Core | Core | Core | Core | +| `metadata.name`, `.description` | Core | Core | Core | Core | +| `harness.adapter_id`, `.resolution` | Core | Core | Core | Core | +| `harness.settings` | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch | Adapter-owned escape hatch | +| `models..provider` | `anthropic` uses native auth; custom names require an Anthropic Messages-compatible `base_url` and `api_key_env` | `openai` uses native auth; custom names require a Responses-compatible `base_url` and `api_key_env` | Dynamic LangChain provider; custom OpenAI-compatible endpoints require `base_url` and `api_key_env` | Dynamic Hermes provider | +| `models..model` | Yes | Yes | Yes | Yes | +| `models..api_key_env` | Yes | Yes | Yes | Yes | +| `models..base_url` | Yes | Yes | Yes | Yes | +| `models..temperature` | No | No | Yes | Yes | +| `models..settings.` | No keys declared | No keys declared | No keys declared | No keys declared | +| `instructions.system` | Yes | Yes; maps to Codex base instructions | Yes | Yes | +| `runtime.input_schema`, `.output_schema` | Core | Core | Core | Core | +| `runtime.artifacts`, `.timeout_seconds` | Core | Core | Core | Core | +| `runtime.max_turns` | Yes | No | No | Yes; maps to Hermes iterations | +| `environment.provider`, `.control_location`, `.ownership` | Core | Core | Core | Core | +| `environment.workspace`, `.artifacts`, `.env` | Core | Core | Core | Core | +| `environment.connection`, `.metadata`, `.settings` | Environment-provider-owned | Environment-provider-owned | Environment-provider-owned | Environment-provider-owned | +| `tools.enabled`, `.blocked` | Yes | No | Yes | Yes; native selectors are Hermes toolset names | +| `skills.paths` | Yes | Yes | Yes | Yes | +| `mcp.servers..transport`, `.url` with `harness_native` exposure | Yes | Yes | Yes | Yes | +| `mcp.servers..exposure = "fabric_managed"` | No; not implemented | No; not implemented | No; not implemented | No; not implemented | +| `telemetry.providers.relay` | Yes | Yes | Yes | Yes | +| `telemetry.providers.native` | No | Yes; OpenTelemetry | Yes; OpenTelemetry and OpenInference | No | +| `telemetry.providers..config` | Declared-provider pass-through | Declared-provider pass-through | Declared-provider pass-through | Declared-provider pass-through | +| `relay.project`, `.output_dir`, `.observability` | Yes | Yes | Yes | Yes | +| `relay.components`, `.policy` | Yes | Yes | Yes | Yes | +| Additive `extensions` on typed config objects | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | Preserved; no portable adapter semantics | + +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: @@ -186,9 +227,30 @@ capability_config.enable_relay( ) ``` -Config helpers edit the typed config before planning or starting a runtime. They -do not modify already-started runtimes. Use `remove_mcp_server(name)` and -`remove_skill_path(path)` to remove capabilities from a copied config. +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: + +```python +from nemo_fabric import ToolsConfig + +def with_tool_policy(base): + tool_config = base.model_copy(deep=True) + tool_config.tools = ToolsConfig( + enabled=["Read", "Edit", "Bash"], + blocked=["WebFetch"], + ) + return tool_config +``` + +`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. @@ -549,6 +611,14 @@ 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 @@ -571,12 +641,18 @@ Runtime compatibility checks should validate: ## Custom Fields And Adapter Settings -Use normalized NeMo Fabric fields for portable behavior: models, runtime, -environment, skills, MCP, telemetry, tools, artifacts, and request context. +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` for adapter-owned configuration that the selected adapter -understands. Examples include Hermes Agent-specific launch options, Codex controls, -or adapter-specific launch paths. +Use `harness.settings` only for stable, harness-native behavior that the selected +adapter understands, such as Claude permission policy, Codex sandbox controls, +or Deep Agents subagent definitions. 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 additive caller-owned annotations. NeMo Fabric preserves these values in the resolved @@ -585,10 +661,9 @@ config but does not copy them into `RunResult.metadata`. Use `request_id` and contains adapter-specific result details. Adapter settings are not portable by default. An adapter must explicitly read -and implement a setting before it affects runtime behavior. Validate -adapter-owned settings against the selected adapter's documentation; -`doctor(...)` does not generically detect unsupported, ignored, or malformed -adapter settings. +and implement a setting before it affects runtime behavior. Planning and doctor +fail for normalized fields the selected adapter does not support. Validation of +all adapter-specific `harness.settings` keys remains adapter-owned. ## Errors diff --git a/examples/code_review_agent/config.py b/examples/code_review_agent/config.py index 906f8e169..91614be63 100644 --- a/examples/code_review_agent/config.py +++ b/examples/code_review_agent/config.py @@ -10,6 +10,8 @@ from nemo_fabric import EnvironmentConfig from nemo_fabric import FabricConfig from nemo_fabric import HarnessConfig +from nemo_fabric import InstructionConfig +from nemo_fabric import InstructionsConfig from nemo_fabric import MetadataConfig from nemo_fabric import ModelConfig from nemo_fabric import RelayAtifConfig @@ -19,6 +21,7 @@ from nemo_fabric import RelayOtlpConfig from nemo_fabric import RuntimeConfig from nemo_fabric import TelemetryConfig +from nemo_fabric import ToolsConfig BASE_DIR = Path(__file__).resolve().parent WORKSPACE = "./repos/my-service" @@ -36,7 +39,7 @@ def base_config() -> FabricConfig: harness=HarnessConfig( adapter_id="nvidia.fabric.hermes", resolution="preinstalled", - settings={"workspace": WORKSPACE}, + settings={}, ), models={ "default": ModelConfig( @@ -70,21 +73,22 @@ def hermes_config() -> FabricConfig: adapter_id="nvidia.fabric.hermes", resolution="preinstalled", settings={ - "workspace": WORKSPACE, - "hermes_home": "./artifacts/hermes-home", - "base_url": "https://integrate.api.nvidia.com/v1", - "max_iterations": 1, "max_tokens": 512, - "temperature": 0.0, "reasoning_config": {"effort": "none"}, - "enabled_toolsets": [], - "system_prompt": "You are a concise smoke test assistant.", }, ) + model = config.models["default"] + assert isinstance(model, ModelConfig) + model.base_url = "https://integrate.api.nvidia.com/v1" + config.instructions = InstructionsConfig( + system=InstructionConfig(content="You are a concise smoke test assistant.") + ) + config.tools = ToolsConfig(enabled=[]) config.runtime = RuntimeConfig( input_schema="chat", output_schema="message", artifacts="./artifacts/hermes", + max_turns=1, ) config.environment = EnvironmentConfig( provider="local", @@ -128,10 +132,10 @@ def deepagents_config() -> FabricConfig: config.harness = HarnessConfig( adapter_id="nvidia.fabric.langchain.deepagents", resolution="preinstalled", - settings={ - "workspace": WORKSPACE, - "system_prompt": "You are a concise smoke test assistant.", - }, + settings={}, + ) + config.instructions = InstructionsConfig( + system=InstructionConfig(content="You are a concise smoke test assistant.") ) config.runtime = RuntimeConfig( input_schema="chat", @@ -151,8 +155,9 @@ def claude_config() -> FabricConfig: """Return the complete Claude adapter variant. The Claude adapter reads the working directory from ``environment.workspace`` - and rejects ``cwd`` in ``harness.settings``; only Claude-specific controls - such as ``system_prompt`` and ``permission_mode`` belong there. + and reads portable instructions from ``FabricConfig.instructions.system``. + Claude-specific controls such as ``permission_mode`` stay in + ``harness.settings``. """ config = base_config().model_copy(deep=True) @@ -160,10 +165,16 @@ def claude_config() -> FabricConfig: adapter_id="nvidia.fabric.claude", resolution="preinstalled", settings={ - "system_prompt": "You are a concise code reviewer. Point out correctness bugs and risks.", "permission_mode": "dontAsk", }, ) + config.instructions = InstructionsConfig( + system=InstructionConfig( + content=( + "You are a concise code reviewer. Point out correctness bugs and risks." + ) + ) + ) config.models = { "default": ModelConfig( provider="anthropic", diff --git a/examples/harbor/README.md b/examples/harbor/README.md index edb4dc495..ed0bd4fca 100644 --- a/examples/harbor/README.md +++ b/examples/harbor/README.md @@ -75,9 +75,15 @@ container boundary: | `--model` | `models.default` | | `--skill` | `skills.paths` | | `--mcp-config` | `mcp.servers` | +| `--ak fabric_telemetry=relay` | `telemetry.providers.relay` and `relay.observability` | +| `--ak fabric_model_base_url=` | `models.default.base_url` | +| `--ak fabric_system_instruction=` | `instructions.system` | +| `--ak fabric_max_turns=` | `runtime.max_turns` | +| `--ak fabric_runtime_timeout_seconds=` | `runtime.timeout_seconds` | +| `--ak fabric_environment_env='{...}'` | `environment.env` | | `--ak fabric_blocked_tools='[...]'` | `tools.blocked` | -| `--ak fabric_telemetry=relay` | `telemetry` and Relay ATOF/ATIF configuration | -| `--ak fabric_harness_settings='{...}'` | `harness.settings` for adapter-specific runtime controls | +| `--ak fabric_enabled_tools='[...]'` | `tools.enabled` | +| `--ak fabric_harness_settings='{...}'` | `harness.settings` | The result is the complete `FabricConfig` uploaded with the `RunRequest` and task-local `base_dir`. The container-side runner deserializes that payload and diff --git a/examples/harbor/calculator/README.md b/examples/harbor/calculator/README.md index a95887c4f..6eee3d260 100644 --- a/examples/harbor/calculator/README.md +++ b/examples/harbor/calculator/README.md @@ -85,7 +85,8 @@ uv run --extra runtime --extra harbor --extra hermes-agent harbor run \ --ak fabric_adapter_id=nvidia.fabric.hermes \ --ak fabric_config_base_dir=/opt/fabric-calculator \ --ak fabric_workspace=/app \ - --ak 'fabric_harness_settings={"base_url":"https://integrate.api.nvidia.com/v1","max_iterations":20}' \ + --ak fabric_model_base_url=https://integrate.api.nvidia.com/v1 \ + --ak fabric_max_turns=20 \ --ae "NVIDIA_API_KEY=$NVIDIA_API_KEY" \ --job-name fabric-hermes \ --jobs-dir "$RUNS_DIR" \ @@ -110,7 +111,9 @@ uv run --extra runtime --extra harbor --extra hermes-agent --extra relay harbor --ak fabric_config_base_dir=/opt/fabric-calculator \ --ak fabric_workspace=/app \ --ak fabric_telemetry=relay \ - --ak 'fabric_harness_settings={"base_url":"https://integrate.api.nvidia.com/v1","max_iterations":4,"terminal_timeout":120}' \ + --ak fabric_model_base_url=https://integrate.api.nvidia.com/v1 \ + --ak fabric_max_turns=4 \ + --ak 'fabric_harness_settings={"terminal_timeout":120}' \ --ae "NVIDIA_API_KEY=$NVIDIA_API_KEY" \ --job-name fabric-hermes-relay \ --jobs-dir "$RUNS_DIR" \ @@ -149,7 +152,8 @@ uv run --extra runtime --extra harbor --extra claude harbor run \ --ak fabric_adapter_id=nvidia.fabric.claude \ --ak fabric_config_base_dir=/opt/fabric-calculator \ --ak fabric_workspace=/app \ - --ak 'fabric_harness_settings={"max_turns":20,"timeout_seconds":600}' \ + --ak fabric_max_turns=20 \ + --ak fabric_runtime_timeout_seconds=600 \ --ae "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \ --job-name fabric-claude \ --jobs-dir "$RUNS_DIR" \ diff --git a/examples/harbor/swebench/README.md b/examples/harbor/swebench/README.md index f6eaff2e7..729639ceb 100644 --- a/examples/harbor/swebench/README.md +++ b/examples/harbor/swebench/README.md @@ -87,8 +87,8 @@ uv run --extra runtime --extra harbor harbor run \ --max-retries 1 ``` -For a self-hosted OpenAI-compatible model, change `--model` and add a -`base_url` to `fabric_harness_settings`. The server must support automatic tool +For a self-hosted OpenAI-compatible model, change `--model` and add +`--ak fabric_model_base_url=`. The server must support automatic tool calling; a successful plain chat completion is not sufficient for SWE-Bench. ## Run the Same Task with Claude @@ -107,8 +107,8 @@ uv run --extra runtime --extra harbor harbor run \ --ak fabric_adapter_id=nvidia.fabric.claude \ --ak fabric_config_bundle="$FABRIC_BUNDLE" \ --ak fabric_telemetry=relay \ - --ak 'fabric_harness_settings={"nemo_relay_command":"/tmp/nemo-fabric-config/.relay/bin/nemo-relay"}' \ --ak "fabric_package=$FABRIC_PACKAGE" \ + --ae "PATH=/tmp/nemo-fabric-config/.relay/bin:$PATH" \ --ae "ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" \ --job-name django-13741-claude \ --jobs-dir "$RUNS_DIR" \ diff --git a/examples/harbor/swebench/adapters/claude/fabric-adapter.json b/examples/harbor/swebench/adapters/claude/fabric-adapter.json index 9f8996f94..52b11a520 100644 --- a/examples/harbor/swebench/adapters/claude/fabric-adapter.json +++ b/examples/harbor/swebench/adapters/claude/fabric-adapter.json @@ -7,7 +7,16 @@ "module": "nemo_fabric_adapters.claude.adapter" }, "config": { - "accepts": ["models", "tools", "tools.blocked", "mcp", "skills", "telemetry"] + "accepts": [ + "models", + "models.base_url", + "instructions.system", + "runtime.max_turns", + "tools.enabled", + "tools.blocked", + "mcp", + "skills" + ] }, "telemetry": { "providers": { diff --git a/examples/harbor/swebench/adapters/hermes/fabric-adapter.json b/examples/harbor/swebench/adapters/hermes/fabric-adapter.json index ef60fb920..c77f3836d 100644 --- a/examples/harbor/swebench/adapters/hermes/fabric-adapter.json +++ b/examples/harbor/swebench/adapters/hermes/fabric-adapter.json @@ -6,19 +6,18 @@ "runner": { "module": "nemo_fabric_adapters.hermes.adapter" }, - "requirements": { - "env": [ - "NVIDIA_API_KEY" - ] - }, + "requirements": {}, "config": { "accepts": [ "models", - "tools", + "models.base_url", + "models.temperature", + "instructions.system", + "runtime.max_turns", + "tools.enabled", "tools.blocked", "mcp", - "skills", - "telemetry" + "skills" ] }, "telemetry": { diff --git a/examples/notebooks/01_quickstart.ipynb b/examples/notebooks/01_quickstart.ipynb index 5ef54f5e4..3f1f2820f 100644 --- a/examples/notebooks/01_quickstart.ipynb +++ b/examples/notebooks/01_quickstart.ipynb @@ -169,9 +169,9 @@ "with the meaning of each block explained inline:\n", "\n", "- `metadata` -- the agent's identity.\n", - "- `harness` -- which harness to use (`adapter_id`) plus that harness's own\n", - " `settings`, which NeMo Fabric passes through untouched.\n", - "- `models` -- named model aliases; `api_key_env` names the credential.\n", + "- `harness` -- which harness to use (`adapter_id`) plus harness-specific\n", + " `settings`; portable behavior stays in typed Fabric fields.\n", + "- `models` -- named model roles; `api_key_env` names the credential.\n", "- `runtime` -- the input/output contract and where artifacts are written." ] }, @@ -183,12 +183,16 @@ "outputs": [], "source": [ "from nemo_fabric import (\n", + " EnvironmentConfig,\n", " Fabric,\n", " FabricConfig,\n", " HarnessConfig,\n", + " InstructionConfig,\n", + " InstructionsConfig,\n", " MetadataConfig,\n", " ModelConfig,\n", " RuntimeConfig,\n", + " ToolsConfig,\n", ")\n", "\n", "WORKSPACE = REPO_ROOT / \"examples\" / \"code_review_agent\" / \"repos\" / \"my-service\"\n", @@ -201,36 +205,40 @@ " description=\"Reviews Python code for correctness issues.\",\n", " ),\n", " # Which harness runs it. `adapter_id` selects the harness integration;\n", - " # everything under `settings` is handed straight to that harness.\n", + " # only Hermes-specific options remain under `settings`.\n", " harness=HarnessConfig(\n", " adapter_id=\"nvidia.fabric.hermes\",\n", " resolution=\"preinstalled\",\n", " settings={\n", - " \"workspace\": str(WORKSPACE),\n", - " \"hermes_home\": str(ARTIFACTS / \"hermes-home\"),\n", - " \"base_url\": \"https://integrate.api.nvidia.com/v1\",\n", - " \"system_prompt\": \"You are a code reviewer. Point out correctness bugs and risks, concisely.\",\n", - " \"max_iterations\": 1,\n", " \"max_tokens\": 1024,\n", " \"reasoning_config\": {\"effort\": \"none\"},\n", - " \"enabled_toolsets\": [],\n", " },\n", " ),\n", - " # Named model aliases. `api_key_env` names the environment variable that\n", + " # Named model roles. `api_key_env` names the environment variable that\n", " # holds the credential, so the key itself never lives in the config.\n", " models={\n", " \"default\": ModelConfig(\n", " provider=\"nvidia\",\n", " model=\"nvidia/nemotron-3-nano-30b-a3b\",\n", + " base_url=\"https://integrate.api.nvidia.com/v1\",\n", " temperature=0.0,\n", " api_key_env=\"NVIDIA_API_KEY\",\n", " )\n", " },\n", + " instructions=InstructionsConfig(\n", + " system=InstructionConfig(\n", + " content=\"You are a code reviewer. Point out correctness bugs and risks, concisely.\",\n", + " mode=\"replace\",\n", + " ),\n", + " ),\n", + " environment=EnvironmentConfig(provider=\"local\", workspace=WORKSPACE),\n", + " tools=ToolsConfig(enabled=[]),\n", " # The logical input/output contract and the artifact root for this agent.\n", " runtime=RuntimeConfig(\n", " input_schema=\"chat\",\n", " output_schema=\"message\",\n", " artifacts=str(ARTIFACTS),\n", + " max_turns=1,\n", " ),\n", ")\n", "print(\"configured:\", config.metadata.name)" diff --git a/examples/notebooks/02_variations.ipynb b/examples/notebooks/02_variations.ipynb index 1089a5652..593e8123f 100644 --- a/examples/notebooks/02_variations.ipynb +++ b/examples/notebooks/02_variations.ipynb @@ -104,6 +104,8 @@ "from nemo_fabric import (\n", " Fabric,\n", " HarnessConfig,\n", + " InstructionConfig,\n", + " InstructionsConfig,\n", " ModelConfig,\n", " RelayAtofConfig,\n", " RelayAtofFileSinkConfig,\n", @@ -152,8 +154,9 @@ " (Hermes Agent and Deep Agents use an NVIDIA-hosted model; Codex uses OpenAI;\n", " Claude uses Anthropic).\n", "- **Input schema** — the Codex adapter takes `text`; the others take `chat`.\n", - "- **Instruction delivery** — Hermes Agent, Deep Agents, and Claude take the system\n", - " prompt via `harness.settings`; Codex takes its instruction from the input.\n", + "- **Instruction delivery** — all four harnesses receive normalized system\n", + " instructions through `instructions.system`; Codex maps them to base instructions\n", + " while its per-turn prompt remains `text` input.\n", "- **Capabilities** — `base_config` ships a code-review skill, but not every\n", " harness accepts the same capabilities, so `for_harness` drops it here. That\n", " difference across harnesses is exactly why the\n", @@ -184,7 +187,11 @@ " adapter_id=harness[\"adapter_id\"], resolution=\"preinstalled\",\n", " settings={**harness[\"settings\"], \"python\": harness[\"python\"]},\n", " )\n", + " cfg.instructions = InstructionsConfig(\n", + " system=InstructionConfig(content=INSTRUCTION, mode=\"replace\"),\n", + " )\n", " cfg.models = {\"default\": harness[\"model\"]}\n", + " cfg.runtime.max_turns = harness.get(\"max_turns\")\n", " cfg.runtime.input_schema = harness[\"input_schema\"] # Codex requires \"text\"\n", " return cfg\n", "\n", @@ -198,14 +205,13 @@ " {\"name\": \"Hermes\", \"adapter_id\": \"nvidia.fabric.hermes\", \"python\": HERMES_PY,\n", " \"input_schema\": \"chat\", \"model\": NVIDIA_MODEL, \"key\": \"NVIDIA_API_KEY\",\n", " \"needs\": \"a Hermes install (repo README's Hermes quick start) + NVIDIA_API_KEY\",\n", - " \"settings\": {\"system_prompt\": INSTRUCTION, \"workspace\": WORKSPACE,\n", - " \"base_url\": \"https://integrate.api.nvidia.com/v1\",\n", - " \"max_iterations\": 1, \"max_tokens\": 512,\n", - " \"reasoning_config\": {\"effort\": \"none\"}, \"enabled_toolsets\": []}},\n", + " \"max_turns\": 1,\n", + " \"settings\": {\"max_tokens\": 512,\n", + " \"reasoning_config\": {\"effort\": \"none\"}}},\n", " {\"name\": \"Deep Agents\", \"adapter_id\": \"nvidia.fabric.langchain.deepagents\", \"python\": FABRIC_PY,\n", " \"input_schema\": \"chat\", \"model\": NVIDIA_MODEL, \"key\": \"NVIDIA_API_KEY\",\n", " \"needs\": \"the Deep Agents adapter (nemo-fabric[deepagents]) + NVIDIA_API_KEY\",\n", - " \"settings\": {\"system_prompt\": INSTRUCTION, \"workspace\": WORKSPACE}},\n", + " \"settings\": {}},\n", " {\"name\": \"Codex\", \"adapter_id\": \"nvidia.fabric.codex\", \"python\": FABRIC_PY,\n", " \"input_schema\": \"text\",\n", " \"model\": ModelConfig(provider=\"openai\", model=\"openai/gpt-5.4\"),\n", @@ -216,7 +222,8 @@ " \"model\": ModelConfig(provider=\"anthropic\", model=\"anthropic/claude-sonnet-4-5\",\n", " api_key_env=\"ANTHROPIC_API_KEY\"),\n", " \"needs\": \"the Claude adapter + ANTHROPIC_API_KEY\",\n", - " \"settings\": {\"system_prompt\": INSTRUCTION, \"permission_mode\": \"dontAsk\"}},\n", + " \"max_turns\": 1,\n", + " \"settings\": {\"permission_mode\": \"dontAsk\"}},\n", "]\n", "\n", "\n", diff --git a/python/src/nemo_fabric/__init__.py b/python/src/nemo_fabric/__init__.py index 54c1f40aa..79bfd8d84 100644 --- a/python/src/nemo_fabric/__init__.py +++ b/python/src/nemo_fabric/__init__.py @@ -14,6 +14,8 @@ from nemo_fabric.models import FabricBaseModel from nemo_fabric.models import FabricConfig from nemo_fabric.models import HarnessConfig +from nemo_fabric.models import InstructionConfig +from nemo_fabric.models import InstructionsConfig from nemo_fabric.models import McpConfig from nemo_fabric.models import McpServerConfig from nemo_fabric.models import MetadataConfig @@ -68,6 +70,8 @@ "FabricError", "FabricEvent", "HarnessConfig", + "InstructionConfig", + "InstructionsConfig", "InvokeStream", "McpConfig", "McpServerConfig", diff --git a/python/src/nemo_fabric/client.py b/python/src/nemo_fabric/client.py index 46f53e506..f80e3683e 100644 --- a/python/src/nemo_fabric/client.py +++ b/python/src/nemo_fabric/client.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Native Python client for resolving and running NeMo Fabric agents.""" +"""Native Python client for resolving and running NVIDIA NeMo Fabric agents.""" from __future__ import annotations diff --git a/python/src/nemo_fabric/integrations/harbor/fabric_agent.py b/python/src/nemo_fabric/integrations/harbor/fabric_agent.py index e017093b5..12ad830a5 100644 --- a/python/src/nemo_fabric/integrations/harbor/fabric_agent.py +++ b/python/src/nemo_fabric/integrations/harbor/fabric_agent.py @@ -19,6 +19,8 @@ from nemo_fabric import EnvironmentConfig from nemo_fabric import FabricConfig from nemo_fabric import HarnessConfig +from nemo_fabric import InstructionConfig +from nemo_fabric import InstructionsConfig from nemo_fabric import MetadataConfig from nemo_fabric import ModelConfig from nemo_fabric import RelayAtifConfig @@ -99,7 +101,13 @@ def __init__( fabric_config_target: str = "/tmp/nemo-fabric-config", fabric_workspace: str = HARBOR_DEFAULT_WORKSPACE, fabric_harness_settings: dict[str, Any] | None = None, + fabric_model_base_url: str | None = None, + fabric_system_instruction: str | None = None, + fabric_max_turns: int | None = None, + fabric_runtime_timeout_seconds: float | None = None, + fabric_environment_env: dict[str, str] | None = None, fabric_blocked_tools: list[str] | None = None, + fabric_enabled_tools: list[str] | None = None, fabric_telemetry: Literal["none", "relay"] = "none", fabric_python: str = "python3", fabric_package: str | None = None, @@ -136,7 +144,17 @@ def __init__( self.fabric_config_target = fabric_config_target self.fabric_workspace = str(workspace) self.fabric_harness_settings = dict(fabric_harness_settings or {}) + self.fabric_model_base_url = fabric_model_base_url + self.fabric_system_instruction = fabric_system_instruction + self.fabric_max_turns = fabric_max_turns + self.fabric_runtime_timeout_seconds = fabric_runtime_timeout_seconds + self.fabric_environment_env = dict(fabric_environment_env or {}) self.fabric_blocked_tools = blocked_tools + self.fabric_enabled_tools = ( + list(fabric_enabled_tools) + if fabric_enabled_tools is not None + else None + ) self.fabric_telemetry = fabric_telemetry self.fabric_python = fabric_python self.fabric_package = fabric_package @@ -259,7 +277,13 @@ def _build_config(self) -> FabricConfig: adapter_id=self.fabric_adapter_id, workspace=self.fabric_workspace, harness_settings=self.fabric_harness_settings, + model_base_url=self.fabric_model_base_url, + system_instruction=self.fabric_system_instruction, + max_turns=self.fabric_max_turns, + timeout_seconds=self.fabric_runtime_timeout_seconds, + environment_env=self.fabric_environment_env, blocked_tools=self.fabric_blocked_tools, + enabled_tools=self.fabric_enabled_tools, telemetry=self.fabric_telemetry, model_name=self.model_name, skills_dir=self.skills_dir, @@ -340,7 +364,13 @@ def build_harbor_config( adapter_id: str, workspace: str, harness_settings: dict[str, Any] | None = None, + model_base_url: str | None = None, + system_instruction: str | None = None, + max_turns: int | None = None, + timeout_seconds: float | None = None, + environment_env: dict[str, str] | None = None, blocked_tools: list[str] | None = None, + enabled_tools: list[str] | None = None, telemetry: Literal["none", "relay"] = "none", model_name: str | None = None, skills_dir: str | Path | None = None, @@ -352,6 +382,15 @@ def build_harbor_config( artifact_root = f"{HARBOR_ARTIFACT_ROOT}/{name}" settings = harbor_harness_defaults(adapter_id) settings.update(harness_settings or {}) + if adapter_id == "nvidia.fabric.claude": + if max_turns is None: + max_turns = 75 + if timeout_seconds is None: + timeout_seconds = 1800 + environment_env = { + "IS_SANDBOX": "1", + **(environment_env or {}), + } config = FabricConfig( metadata=MetadataConfig( name=name, @@ -366,19 +405,39 @@ def build_harbor_config( input_schema="text", output_schema="message", artifacts=artifact_root, + timeout_seconds=timeout_seconds, + max_turns=max_turns, ), environment=EnvironmentConfig( provider="local", workspace=workspace, artifacts=artifact_root, + env=dict(environment_env or {}), + ), + instructions=( + InstructionsConfig( + system=InstructionConfig(content=system_instruction), + ) + if system_instruction is not None + else None + ), + tools=( + ToolsConfig( + enabled=enabled_tools, + blocked=list(blocked_tools or []), + ) + if blocked_tools or enabled_tools is not None + else None ), - tools=(ToolsConfig(blocked=list(blocked_tools)) if blocked_tools else None), ) if model_name: config.models["default"] = ModelConfig( provider=model_provider(model_name), model=model_name, + base_url=model_base_url, ) + elif model_base_url is not None: + raise ValueError("model_base_url requires model_name") for server in mcp_servers: if server.transport == "stdio": config.add_mcp_server( @@ -434,15 +493,11 @@ def harbor_harness_defaults(adapter_id: str) -> dict[str, Any]: if adapter_id == "nvidia.fabric.hermes": return { - "hermes_home": "/tmp/fabric-hermes", "terminal_timeout": 300, } if adapter_id == "nvidia.fabric.claude": return { "permission_mode": "bypassPermissions", - "max_turns": 75, - "timeout_seconds": 1800, - "env": {"IS_SANDBOX": "1"}, } return {} diff --git a/python/src/nemo_fabric/models.py b/python/src/nemo_fabric/models.py index 732f9eaa0..7a33095bc 100644 --- a/python/src/nemo_fabric/models.py +++ b/python/src/nemo_fabric/models.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. # SPDX-License-Identifier: Apache-2.0 -"""Pydantic SDK models for NeMo Fabric configuration and requests. +"""Pydantic SDK models for NVIDIA NeMo Fabric configuration and requests. The Rust core remains the source of truth for persisted schema snapshots. These models provide the Python SDK's typed authoring surface and intentionally keep @@ -24,9 +24,7 @@ from pydantic import BaseModel from pydantic import ConfigDict from pydantic import Field -from pydantic import SerializerFunctionWrapHandler from pydantic import field_validator -from pydantic import model_serializer from pydantic import model_validator @@ -107,12 +105,38 @@ class HarnessConfig(FabricBaseModel): settings: dict[str, Any] = Field(default_factory=dict) +class InstructionConfig(FabricBaseModel): + """One portable instruction value.""" + + content: str = Field(min_length=1, pattern=r"\S") + mode: Literal["replace"] = "replace" + + @field_validator("content") + @classmethod + def _validate_content(cls, value: str) -> str: + if not value.strip(): + raise ValueError("instruction content must be a non-empty string") + return value + + +class InstructionsConfig(FabricBaseModel): + """Harness-neutral agent instructions.""" + + system: InstructionConfig | None = None + + class RuntimeConfig(FabricBaseModel): - """Runtime input/output contract.""" + """Invocation runtime contract.""" input_schema: str | None = None output_schema: str | None = None artifacts: str | Path | None = None + timeout_seconds: float | None = Field( + default=None, + gt=0, + allow_inf_nan=False, + ) + max_turns: int | None = Field(default=None, gt=0, le=(1 << 32) - 1) class EnvironmentConfig(FabricBaseModel): @@ -141,6 +165,15 @@ class EnvironmentConfig(FabricBaseModel): default=None, description="Environment-specific artifact path.", ) + env: dict[str, str] = Field( + default_factory=dict, + description=( + "Environment variables visible to the harness and its tools. Values are " + "serialized into configuration and run plans; prefer api_key_env-style " + "environment-variable-name indirection for credentials." + ), + json_schema_extra={"propertyNames": {"pattern": r"\S"}}, + ) settings: dict[str, Any] = Field( default_factory=dict, description="Provider-specific configuration interpreted by the environment provider.", @@ -162,16 +195,45 @@ class EnvironmentConfig(FabricBaseModel): description="Whether NeMo Fabric control code runs outside or inside the environment.", ) + @field_validator("env") + @classmethod + def _validate_env_names(cls, value: dict[str, str]) -> dict[str, str]: + if any(not name.strip() for name in value): + raise ValueError("environment.env variable names must not be empty") + return value + class ModelConfig(FabricBaseModel): - """Model alias configuration.""" + """Configuration for one model role.""" provider: str = Field(min_length=1) model: str = Field(min_length=1) api_key_env: str | None = None temperature: float | None = None + base_url: str | None = Field(default=None, min_length=1) settings: dict[str, Any] = Field(default_factory=dict) + @field_validator("provider") + @classmethod + def _validate_provider(cls, value: str) -> str: + if not value.strip() or value != value.strip() or value != value.lower(): + raise ValueError("provider must be a non-empty lowercase identifier") + return value + + @field_validator("model", "api_key_env") + @classmethod + def _validate_nonempty_model_fields(cls, value: str | None) -> str | None: + if value is not None and not value.strip(): + raise ValueError("model fields must be non-empty strings") + return value + + @field_validator("base_url") + @classmethod + def _validate_base_url(cls, value: str | None) -> str | None: + if value is not None and not value.strip(): + raise ValueError("base_url must be a non-empty string") + return value + class SkillConfig(FabricBaseModel): """Skill capability configuration.""" @@ -257,23 +319,16 @@ class RelayAtofStreamSinkConfig(FabricBaseModel): type: Literal["stream"] = "stream" url: str transport: Literal["http_post", "websocket", "ndjson"] = "http_post" - headers: dict[str, str] = Field(default_factory=dict) - header_env: dict[str, str] = Field(default_factory=dict) + headers: dict[str, str] = Field( + default_factory=dict, exclude_if=lambda value: not value + ) + header_env: dict[str, str] = Field( + default_factory=dict, exclude_if=lambda value: not value + ) timeout_millis: int = 3000 field_name_policy: Literal["preserve", "replace_dots"] = "preserve" name: str | None = None - @model_serializer(mode="wrap") - def _omit_empty_header_maps(self, handler: SerializerFunctionWrapHandler) -> dict[str, Any]: - """Omit optional header maps when they are empty.""" - - data = handler(self) - if not self.headers: - data.pop("headers", None) - if not self.header_env: - data.pop("header_env", None) - return data - class RelayAtofConfig(FabricBaseModel): """NeMo Relay ATOF export configuration.""" @@ -378,7 +433,9 @@ class RelayConfig(FabricBaseModel): project: str | None = None output_dir: str | Path | None = None observability: RelayObservabilityConfig | dict[str, Any] | None = None - components: list[RelayComponentConfig | dict[str, Any]] = Field(default_factory=list) + components: list[RelayComponentConfig | dict[str, Any]] = Field( + default_factory=list + ) policy: RelayConfigPolicy | dict[str, Any] | None = None @@ -391,7 +448,9 @@ class TelemetryProviderConfig(FabricBaseModel): class TelemetryConfig(FabricBaseModel): """Telemetry configuration.""" - providers: dict[Literal["relay", "native"], TelemetryProviderConfig | dict[str, Any]] = Field(default_factory=dict) + providers: dict[ + Literal["relay", "native"], TelemetryProviderConfig | dict[str, Any] + ] = Field(default_factory=dict) def enable_relay( self, @@ -422,23 +481,56 @@ def remove_provider(self, provider: Literal["relay", "native"]) -> Self: class ToolsConfig(FabricBaseModel): """Harness-neutral tool capability configuration.""" - blocked: list[str] = Field(default_factory=list) + enabled: list[str] | None = Field( + default=None, + description=( + "Adapter-native tools to expose. None preserves the harness default; " + "an empty list exposes no tools." + ), + ) + blocked: list[str] = Field( + default_factory=list, + description="Adapter-native tool names to deny.", + ) + + @field_validator("enabled", "blocked") + @classmethod + def _validate_tools(cls, value: list[str] | None) -> list[str] | None: + if value is not None and any(not tool.strip() for tool in value): + raise ValueError("tool names must not be empty") + return value + + @model_validator(mode="after") + def _validate_policy(self) -> Self: + if self.enabled is not None: + overlap = set(self.enabled).intersection(self.blocked) + if overlap: + name = sorted(overlap)[0] + raise ValueError(f"tool {name!r} cannot be both enabled and blocked") + return self class FabricConfig(FabricBaseModel): - """SDK-facing typed NeMo Fabric agent configuration.""" + """SDK-facing typed NeMo Fabric agent configuration. + + NeMo Fabric-owned fields apply uniformly. Adapter-translated fields are + checked against the selected descriptor; refer to the [normalized + configuration compatibility + table](../../../sdk/python.mdx#normalized-configuration-compatibility). + """ schema_version: str = "fabric.agent/v1alpha1" metadata: MetadataConfig harness: HarnessConfig runtime: RuntimeConfig = Field(default_factory=RuntimeConfig) environment: EnvironmentConfig | None = None - models: dict[str, ModelConfig | dict[str, Any]] = Field(default_factory=dict) + models: dict[str, ModelConfig] = Field(default_factory=dict) + instructions: InstructionsConfig | None = None mcp: McpConfig | None = None skills: SkillConfig | None = None telemetry: TelemetryConfig | None = None relay: RelayConfig | dict[str, Any] | None = None - tools: ToolsConfig | dict[str, Any] | None = None + tools: ToolsConfig | None = None @classmethod def from_mapping(cls, value: Mapping[str, Any]) -> Self: @@ -503,10 +595,10 @@ def remove_skill_path(self, path: str | Path) -> Self: return self def block_tools(self, *tools: str) -> Self: - """Block adapter-native tool names or toolsets and return this config.""" + """Block adapter-native tool names and return this config.""" - if self.tools is None or isinstance(self.tools, dict): - self.tools = ToolsConfig.model_validate(self.tools or {}) + if self.tools is None: + self.tools = ToolsConfig() existing = list(self.tools.blocked) for tool in tools: if tool not in existing: @@ -540,12 +632,19 @@ def enable_relay( relay.output_dir = output_dir if observability is not None: relay.observability = ( - observability if isinstance(observability, RelayObservabilityConfig) else dict(observability) + observability + if isinstance(observability, RelayObservabilityConfig) + else dict(observability) ) if components is not None: - relay.components = [item if isinstance(item, RelayComponentConfig) else dict(item) for item in components] + relay.components = [ + item if isinstance(item, RelayComponentConfig) else dict(item) + for item in components + ] if policy is not None: - relay.policy = policy if isinstance(policy, RelayConfigPolicy) else dict(policy) + relay.policy = ( + policy if isinstance(policy, RelayConfigPolicy) else dict(policy) + ) self.relay = relay return self diff --git a/python/src/nemo_fabric/types.py b/python/src/nemo_fabric/types.py index 35ea09cd0..ad8a66e8f 100644 --- a/python/src/nemo_fabric/types.py +++ b/python/src/nemo_fabric/types.py @@ -214,17 +214,82 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_HarnessConfig": ) +class _InstructionConfig(_ConfigMapping): + """One portable instruction value.""" + + _fields = frozenset({"content", "mode"}) + + def __init__( + self, + *, + content: str, + mode: str = "replace", + extra_fields: Mapping[str, Any] | None = None, + ) -> None: + if mode != "replace": + raise FabricConfigError("instruction mode must be replace") + super().__init__( + { + "content": _required_text(content, "instruction content"), + "mode": mode, + }, + extra_fields=extra_fields, + ) + + @classmethod + def from_mapping(cls, value: Mapping[str, Any]) -> "_InstructionConfig": + """Validate an instruction mapping and preserve extension fields.""" + + data = _mapping(value, "instruction") + return cls( + content=data.get("content"), + mode=data.get("mode", "replace"), + extra_fields={key: item for key, item in data.items() if key not in cls._fields}, + ) + + +class _InstructionsConfig(_ConfigMapping): + """Harness-neutral agent instructions.""" + + _fields = frozenset({"system"}) + + def __init__( + self, + *, + system: _InstructionConfig | Mapping[str, Any] | None = None, + extra_fields: Mapping[str, Any] | None = None, + ) -> None: + values: dict[str, Any] = {} + if system is not None: + values["system"] = _coerce(_InstructionConfig, system, "system instruction") + super().__init__(values, extra_fields=extra_fields) + + @classmethod + def from_mapping(cls, value: Mapping[str, Any]) -> "_InstructionsConfig": + """Validate an instructions mapping and preserve extension fields.""" + + data = _mapping(value, "instructions") + return cls( + system=data.get("system"), + extra_fields={key: item for key, item in data.items() if key not in cls._fields}, + ) + + class _RuntimeConfig(_ConfigMapping): - """Runtime input/output contract. + """Invocation runtime contract. Attributes: input_schema: Optional logical input contract identifier. output_schema: Optional logical output contract identifier. artifacts: Optional artifact-root path. + timeout_seconds: Optional invocation deadline in seconds. + max_turns: Optional harness turn limit. extra_fields: Preserved extension fields not recognized by this SDK. """ - _fields = frozenset({"input_schema", "output_schema", "artifacts"}) + _fields = frozenset( + {"input_schema", "output_schema", "artifacts", "timeout_seconds", "max_turns"} + ) def __init__( self, @@ -232,6 +297,8 @@ def __init__( input_schema: str | None = None, output_schema: str | None = None, artifacts: str | Path | None = None, + timeout_seconds: float | None = None, + max_turns: int | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: values: dict[str, Any] = {} @@ -242,6 +309,28 @@ def __init__( ): if item is not None: values[key] = item + if timeout_seconds is not None: + if ( + isinstance(timeout_seconds, bool) + or not isinstance(timeout_seconds, (int, float)) + or not math.isfinite(timeout_seconds) + or timeout_seconds <= 0 + ): + raise FabricConfigError( + "runtime timeout_seconds must be a finite number greater than zero" + ) + values["timeout_seconds"] = float(timeout_seconds) + if max_turns is not None: + if ( + isinstance(max_turns, bool) + or not isinstance(max_turns, int) + or max_turns <= 0 + or max_turns > (1 << 32) - 1 + ): + raise FabricConfigError( + "runtime max_turns must be between 1 and 4294967295" + ) + values["max_turns"] = max_turns super().__init__(values, extra_fields=extra_fields) @classmethod @@ -253,6 +342,8 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_RuntimeConfig": input_schema=data.get("input_schema"), output_schema=data.get("output_schema"), artifacts=data.get("artifacts"), + timeout_seconds=data.get("timeout_seconds"), + max_turns=data.get("max_turns"), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, ) @@ -264,13 +355,14 @@ class _EnvironmentConfig(_ConfigMapping): provider: Environment provider identifier; defaults to ``local``. workspace: Optional workspace path visible to the harness. artifacts: Optional environment-specific artifact path. + env: Environment variables visible to the harness and its tools. settings: JSON-compatible provider settings. metadata: JSON-compatible caller metadata. extra_fields: Preserved extension fields not recognized by this SDK. """ - _fields = frozenset({"provider", "workspace", "artifacts", "settings", "metadata"}) - _omit_if_empty = frozenset({"settings", "metadata"}) + _fields = frozenset({"provider", "workspace", "artifacts", "env", "settings", "metadata"}) + _omit_if_empty = frozenset({"env", "settings", "metadata"}) def __init__( self, @@ -278,12 +370,14 @@ def __init__( provider: str = "local", workspace: str | Path | None = None, artifacts: str | Path | None = None, + env: Mapping[str, str] | None = None, settings: Mapping[str, Any] | None = None, metadata: Mapping[str, Any] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: values: dict[str, Any] = { "provider": _required_text(provider, "environment provider"), + "env": {}, "settings": _mapping( {} if settings is None else settings, "environment settings", @@ -293,6 +387,13 @@ def __init__( "environment metadata", ), } + raw_env = _mapping({} if env is None else env, "environment env") + if any(not isinstance(value, str) for value in raw_env.values()): + raise FabricConfigError("environment env values must be strings") + values["env"] = { + _required_text(name, "environment variable name"): value + for name, value in raw_env.items() + } if workspace is not None: values["workspace"] = workspace if artifacts is not None: @@ -308,6 +409,7 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_EnvironmentConfig": provider=data.get("provider", "local"), workspace=data.get("workspace"), artifacts=data.get("artifacts"), + env=data.get("env"), settings=data.get("settings"), metadata=data.get("metadata"), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, @@ -364,35 +466,53 @@ def remove_path(self, path: str | Path) -> "_SkillConfig": class _ToolsConfig(_ConfigMapping): """Harness-neutral tool capability configuration.""" - _fields = frozenset({"blocked"}) + _fields = frozenset({"enabled", "blocked"}) _omit_if_empty = frozenset({"blocked"}) def __init__( self, *, + enabled: Sequence[str] | None = None, blocked: Sequence[str] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: - if blocked is not None and (isinstance(blocked, (str, bytes)) or not isinstance(blocked, Sequence)): - raise FabricConfigError("tools blocked must be an ordered sequence of strings") - values: dict[str, Any] = {"blocked": [_required_text(tool, "blocked tool") for tool in (blocked or [])]} + for name, values in (("enabled", enabled), ("blocked", blocked)): + if values is not None and ( + isinstance(values, (str, bytes)) or not isinstance(values, Sequence) + ): + raise FabricConfigError( + f"tools {name} must be an ordered sequence of strings" + ) + enabled_values = ( + None + if enabled is None + else [_required_text(tool, "enabled tool") for tool in enabled] + ) + blocked_values = [ + _required_text(tool, "blocked tool") for tool in (blocked or []) + ] + overlap = set(enabled_values or []).intersection(blocked_values) + if overlap: + name = sorted(overlap)[0] + raise FabricConfigError(f"tool {name!r} cannot be both enabled and blocked") + values: dict[str, Any] = {"blocked": blocked_values} + if enabled_values is not None: + values["enabled"] = enabled_values super().__init__(values, extra_fields=extra_fields) @classmethod - def from_mapping(cls, value: Mapping[str, Any]) -> _ToolsConfig: + def from_mapping(cls, value: Mapping[str, Any]) -> "_ToolsConfig": """Validate a tools mapping and preserve extension fields.""" data = _mapping(value, "tools") - blocked = data.get("blocked", []) - if isinstance(blocked, (str, bytes)) or not isinstance(blocked, Sequence): - raise FabricConfigError("tools blocked must be an ordered sequence of strings") return cls( - blocked=blocked, + enabled=data.get("enabled"), + blocked=data.get("blocked", []), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, ) def block(self, *tools: str) -> _ToolsConfig: - """Block adapter-native tool names or toolsets.""" + """Block adapter-native tool names.""" blocked = list(self.get("blocked", [])) for tool in tools: @@ -553,9 +673,10 @@ class _FabricConfigSnapshot(_ConfigMapping): schema_version: Agent schema identifier. metadata: Required ``MetadataConfig`` agent identity. harness: Required ``HarnessConfig`` adapter selection. - runtime: Runtime input/output configuration. + runtime: Invocation runtime configuration. environment: Optional execution environment configuration. models: Named, JSON-compatible model configurations. + instructions: Optional portable agent instructions. mcp: Optional MCP configuration. skills: Optional skill configuration. telemetry: Optional telemetry configuration. @@ -572,6 +693,7 @@ class _FabricConfigSnapshot(_ConfigMapping): "runtime", "environment", "models", + "instructions", "mcp", "skills", "telemetry", @@ -590,6 +712,7 @@ def __init__( schema_version: str = "fabric.agent/v1alpha1", environment: _EnvironmentConfig | Mapping[str, Any] | None = None, models: Mapping[str, Any] | None = None, + instructions: _InstructionsConfig | Mapping[str, Any] | None = None, mcp: Mapping[str, Any] | None = None, skills: Mapping[str, Any] | None = None, telemetry: Mapping[str, Any] | None = None, @@ -605,6 +728,11 @@ def __init__( "runtime", ) environment_value = None if environment is None else _coerce(_EnvironmentConfig, environment, "environment") + instructions_value = ( + None + if instructions is None + else _coerce(_InstructionsConfig, instructions, "instructions") + ) mcp_value = None if mcp is None else _coerce(_McpConfig, mcp, "mcp") skills_value = None if skills is None else _coerce(_SkillConfig, skills, "skills") telemetry_value = None if telemetry is None else _coerce(_TelemetryConfig, telemetry, "telemetry") @@ -619,6 +747,7 @@ def __init__( } for key, item in ( ("environment", environment_value), + ("instructions", instructions_value), ("mcp", mcp_value), ("skills", skills_value), ("telemetry", telemetry_value), @@ -650,6 +779,7 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_FabricConfigSnapshot": runtime=data.get("runtime"), environment=data.get("environment"), models=data.get("models"), + instructions=data.get("instructions"), mcp=data.get("mcp"), skills=data.get("skills"), telemetry=data.get("telemetry"), @@ -735,7 +865,7 @@ def add_skill_path(self, path: str | Path) -> "_FabricConfigSnapshot": return self def block_tools(self, *tools: str) -> _FabricConfigSnapshot: - """Block adapter-native tool names or toolsets and return this config.""" + """Block adapter-native tool names and return this config.""" self.tools.block(*tools) return self diff --git a/schemas/adapter-descriptor.schema.json b/schemas/adapter-descriptor.schema.json index 2dc95cbf9..ba1a2e44d 100644 --- a/schemas/adapter-descriptor.schema.json +++ b/schemas/adapter-descriptor.schema.json @@ -1,13 +1,63 @@ { "$defs": { + "AdapterConfigField": { + "description": "Adapter-translated normalized NVIDIA NeMo Fabric configuration fields.", + "oneOf": [ + { + "const": "models", + "description": "Normalized model selection and credentials.", + "type": "string" + }, + { + "const": "models.base_url", + "description": "Custom model endpoint.", + "type": "string" + }, + { + "const": "models.temperature", + "description": "Model temperature.", + "type": "string" + }, + { + "const": "instructions.system", + "description": "Portable system instructions.", + "type": "string" + }, + { + "const": "runtime.max_turns", + "description": "Per-invocation harness turn limit.", + "type": "string" + }, + { + "const": "tools.enabled", + "description": "Adapter-native tool names to expose.", + "type": "string" + }, + { + "const": "tools.blocked", + "description": "Adapter-native tool names to block.", + "type": "string" + }, + { + "const": "mcp", + "description": "Harness-native MCP servers.", + "type": "string" + }, + { + "const": "skills", + "description": "Harness-native skills.", + "type": "string" + } + ] + }, "AdapterConfigSupport": { "additionalProperties": true, "description": "Adapter config support.", "properties": { "accepts": { - "description": "NeMo Fabric config areas or policy paths accepted by this adapter.", + "description": "Normalized NVIDIA NeMo Fabric config areas or policy paths accepted by this adapter.", "items": { - "type": "string" + "$ref": "#/$defs/AdapterConfigField" }, "type": "array" }, diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index 90e795c0a..d4bea7f72 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -84,6 +84,13 @@ "$ref": "#/$defs/ControlLocation", "description": "Where NeMo Fabric control code runs." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.", + "type": "object" + }, "environment_id": { "description": "Environment handle id.", "type": "string" diff --git a/schemas/agent.schema.json b/schemas/agent.schema.json index 3ba524346..f42c2a570 100644 --- a/schemas/agent.schema.json +++ b/schemas/agent.schema.json @@ -36,6 +36,16 @@ "default": "in_env_control", "description": "Where NeMo Fabric control code runs relative to the environment." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.\n\nValues are serialized into the run plan and can appear wherever configs\nor plans are logged or persisted. Prefer `api_key_env`-style\nenvironment-variable-name indirection for credentials.", + "propertyNames": { + "pattern": "\\S" + }, + "type": "object" + }, "metadata": { "additionalProperties": true, "description": "Consumer-provided environment metadata.", @@ -113,6 +123,55 @@ ], "type": "object" }, + "InstructionConfig": { + "additionalProperties": true, + "description": "One portable instruction value.", + "properties": { + "content": { + "description": "Instruction text.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "mode": { + "$ref": "#/$defs/InstructionMode", + "default": "replace", + "description": "How the instruction is applied." + } + }, + "required": [ + "content" + ], + "type": "object" + }, + "InstructionMode": { + "description": "How an instruction value is applied to the selected harness.", + "oneOf": [ + { + "const": "replace", + "description": "Replace the harness default instruction value.", + "type": "string" + } + ] + }, + "InstructionsConfig": { + "additionalProperties": true, + "description": "Harness-neutral agent instruction configuration.", + "properties": { + "system": { + "anyOf": [ + { + "$ref": "#/$defs/InstructionConfig" + }, + { + "type": "null" + } + ], + "description": "System instructions for the selected harness." + } + }, + "type": "object" + }, "McpConfig": { "additionalProperties": true, "description": "MCP capability configuration.", @@ -198,6 +257,13 @@ "null" ] }, + "base_url": { + "description": "Optional provider endpoint URL.", + "type": [ + "string", + "null" + ] + }, "model": { "description": "Provider model identifier.", "type": "string" @@ -877,7 +943,7 @@ }, "RuntimeConfig": { "additionalProperties": true, - "description": "Runtime input/output contract.", + "description": "Invocation runtime contract.", "properties": { "artifacts": { "description": "Artifact directory.", @@ -891,10 +957,29 @@ "description": "Input schema label.", "type": "string" }, + "max_turns": { + "description": "Maximum number of harness turns within one invocation.", + "format": "uint32", + "maximum": 4294967295, + "minimum": 1, + "type": [ + "integer", + "null" + ] + }, "output_schema": { "default": "text", "description": "Output schema label.", "type": "string" + }, + "timeout_seconds": { + "description": "Maximum duration of one invocation in seconds.", + "exclusiveMinimum": 0.0, + "format": "double", + "type": [ + "number", + "null" + ] } }, "type": "object" @@ -942,11 +1027,21 @@ "description": "Harness-neutral tool capability configuration.", "properties": { "blocked": { - "description": "Adapter-native tool names or toolset names to block.", + "description": "Adapter-native tool names to block.", "items": { "type": "string" }, "type": "array" + }, + "enabled": { + "description": "Adapter-native tool names to expose. `None` preserves the harness default.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] } }, "type": "object" @@ -954,7 +1049,7 @@ }, "$schema": "https://json-schema.org/draft/2020-12/schema", "additionalProperties": true, - "description": "Versioned NeMo Fabric agent config.", + "description": "Versioned NVIDIA NeMo Fabric agent config.\n\nNeMo Fabric-owned fields apply uniformly, while adapter-translated fields are\nvalidated against the selected adapter descriptor. See the\n[configuration compatibility matrix](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/sdk/python.mdx#normalized-configuration-compatibility).", "properties": { "environment": { "anyOf": [ @@ -971,6 +1066,17 @@ "$ref": "#/$defs/HarnessConfig", "description": "Harness selection and harness-specific settings." }, + "instructions": { + "anyOf": [ + { + "$ref": "#/$defs/InstructionsConfig" + }, + { + "type": "null" + } + ], + "description": "Portable agent instructions for the selected harness." + }, "mcp": { "anyOf": [ { @@ -990,7 +1096,7 @@ "additionalProperties": { "$ref": "#/$defs/ModelConfig" }, - "description": "Model aliases.", + "description": "Named model roles.", "type": "object" }, "relay": { @@ -1006,7 +1112,7 @@ }, "runtime": { "$ref": "#/$defs/RuntimeConfig", - "description": "Runtime input/output contract." + "description": "Invocation runtime contract." }, "schema_version": { "description": "Config schema version.", diff --git a/schemas/environment-handle.schema.json b/schemas/environment-handle.schema.json index ee83adb3c..fe07bcaa8 100644 --- a/schemas/environment-handle.schema.json +++ b/schemas/environment-handle.schema.json @@ -50,6 +50,13 @@ "$ref": "#/$defs/ControlLocation", "description": "Where NeMo Fabric control code runs." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.", + "type": "object" + }, "environment_id": { "description": "Environment handle id.", "type": "string" diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index 148cdec30..957d2b5b0 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -1,13 +1,63 @@ { "$defs": { + "AdapterConfigField": { + "description": "Adapter-translated normalized NVIDIA NeMo Fabric configuration fields.", + "oneOf": [ + { + "const": "models", + "description": "Normalized model selection and credentials.", + "type": "string" + }, + { + "const": "models.base_url", + "description": "Custom model endpoint.", + "type": "string" + }, + { + "const": "models.temperature", + "description": "Model temperature.", + "type": "string" + }, + { + "const": "instructions.system", + "description": "Portable system instructions.", + "type": "string" + }, + { + "const": "runtime.max_turns", + "description": "Per-invocation harness turn limit.", + "type": "string" + }, + { + "const": "tools.enabled", + "description": "Adapter-native tool names to expose.", + "type": "string" + }, + { + "const": "tools.blocked", + "description": "Adapter-native tool names to block.", + "type": "string" + }, + { + "const": "mcp", + "description": "Harness-native MCP servers.", + "type": "string" + }, + { + "const": "skills", + "description": "Harness-native skills.", + "type": "string" + } + ] + }, "AdapterConfigSupport": { "additionalProperties": true, "description": "Adapter config support.", "properties": { "accepts": { - "description": "NeMo Fabric config areas or policy paths accepted by this adapter.", + "description": "Normalized NVIDIA NeMo Fabric config areas or policy paths accepted by this adapter.", "items": { - "type": "string" + "$ref": "#/$defs/AdapterConfigField" }, "type": "array" }, @@ -279,7 +329,7 @@ "type": "object" }, "CapabilityRoute": { - "description": "One capability routing decision.", + "description": "One capability execution assignment.\n\nRoutes apply to executable tool, skill, and MCP capabilities. Adapter-translated\nscalar configuration is validated separately against [`AdapterConfigSupport`].", "properties": { "kind": { "$ref": "#/$defs/CapabilityKind", @@ -295,7 +345,7 @@ }, "target": { "$ref": "#/$defs/CapabilityTarget", - "description": "Routing target." + "description": "Component responsible for executing the capability." } }, "required": [ @@ -307,21 +357,21 @@ "type": "object" }, "CapabilityTarget": { - "description": "Capability routing target.", + "description": "Component responsible for executing a configured capability.\n\nThis target describes execution ownership, not network routing.", "oneOf": [ { "const": "harness_native", - "description": "Adapter maps the capability into harness-native config.", + "description": "The selected adapter maps and executes the capability through its harness.", "type": "string" }, { "const": "fabric_managed", - "description": "NeMo Fabric exposes or manages the capability around the harness.", + "description": "NeMo Fabric executes the capability outside the harness-native surface.", "type": "string" }, { "const": "unsupported", - "description": "Capability is configured but no executable surface exists.", + "description": "Neither the adapter nor NeMo Fabric can execute the configured capability.", "type": "string" } ] @@ -387,6 +437,16 @@ "default": "in_env_control", "description": "Where NeMo Fabric control code runs relative to the environment." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.\n\nValues are serialized into the run plan and can appear wherever configs\nor plans are logged or persisted. Prefer `api_key_env`-style\nenvironment-variable-name indirection for credentials.", + "propertyNames": { + "pattern": "\\S" + }, + "type": "object" + }, "metadata": { "additionalProperties": true, "description": "Consumer-provided environment metadata.", @@ -453,6 +513,13 @@ "$ref": "#/$defs/ControlLocation", "description": "NeMo Fabric control location." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.", + "type": "object" + }, "metadata": { "additionalProperties": true, "description": "Consumer-provided environment metadata.", @@ -488,7 +555,7 @@ }, "FabricConfig": { "additionalProperties": true, - "description": "Versioned NeMo Fabric agent config.", + "description": "Versioned NVIDIA NeMo Fabric agent config.\n\nNeMo Fabric-owned fields apply uniformly, while adapter-translated fields are\nvalidated against the selected adapter descriptor. See the\n[configuration compatibility matrix](https://github.com/NVIDIA/NeMo-Fabric/blob/main/docs/sdk/python.mdx#normalized-configuration-compatibility).", "properties": { "environment": { "anyOf": [ @@ -505,6 +572,17 @@ "$ref": "#/$defs/HarnessConfig", "description": "Harness selection and harness-specific settings." }, + "instructions": { + "anyOf": [ + { + "$ref": "#/$defs/InstructionsConfig" + }, + { + "type": "null" + } + ], + "description": "Portable agent instructions for the selected harness." + }, "mcp": { "anyOf": [ { @@ -524,7 +602,7 @@ "additionalProperties": { "$ref": "#/$defs/ModelConfig" }, - "description": "Model aliases.", + "description": "Named model roles.", "type": "object" }, "relay": { @@ -540,7 +618,7 @@ }, "runtime": { "$ref": "#/$defs/RuntimeConfig", - "description": "Runtime input/output contract." + "description": "Invocation runtime contract." }, "schema_version": { "description": "Config schema version.", @@ -618,6 +696,55 @@ ], "type": "object" }, + "InstructionConfig": { + "additionalProperties": true, + "description": "One portable instruction value.", + "properties": { + "content": { + "description": "Instruction text.", + "minLength": 1, + "pattern": "\\S", + "type": "string" + }, + "mode": { + "$ref": "#/$defs/InstructionMode", + "default": "replace", + "description": "How the instruction is applied." + } + }, + "required": [ + "content" + ], + "type": "object" + }, + "InstructionMode": { + "description": "How an instruction value is applied to the selected harness.", + "oneOf": [ + { + "const": "replace", + "description": "Replace the harness default instruction value.", + "type": "string" + } + ] + }, + "InstructionsConfig": { + "additionalProperties": true, + "description": "Harness-neutral agent instruction configuration.", + "properties": { + "system": { + "anyOf": [ + { + "$ref": "#/$defs/InstructionConfig" + }, + { + "type": "null" + } + ], + "description": "System instructions for the selected harness." + } + }, + "type": "object" + }, "McpConfig": { "additionalProperties": true, "description": "MCP capability configuration.", @@ -726,6 +853,13 @@ "null" ] }, + "base_url": { + "description": "Optional provider endpoint URL.", + "type": [ + "string", + "null" + ] + }, "model": { "description": "Provider model identifier.", "type": "string" @@ -1464,7 +1598,7 @@ }, "RuntimeConfig": { "additionalProperties": true, - "description": "Runtime input/output contract.", + "description": "Invocation runtime contract.", "properties": { "artifacts": { "description": "Artifact directory.", @@ -1478,10 +1612,29 @@ "description": "Input schema label.", "type": "string" }, + "max_turns": { + "description": "Maximum number of harness turns within one invocation.", + "format": "uint32", + "maximum": 4294967295, + "minimum": 1, + "type": [ + "integer", + "null" + ] + }, "output_schema": { "default": "text", "description": "Output schema label.", "type": "string" + }, + "timeout_seconds": { + "description": "Maximum duration of one invocation in seconds.", + "exclusiveMinimum": 0.0, + "format": "double", + "type": [ + "number", + "null" + ] } }, "type": "object" @@ -1592,11 +1745,21 @@ "description": "Harness-neutral tool capability configuration.", "properties": { "blocked": { - "description": "Adapter-native tool names or toolset names to block.", + "description": "Adapter-native tool names to block.", "items": { "type": "string" }, "type": "array" + }, + "enabled": { + "description": "Adapter-native tool names to expose. `None` preserves the harness default.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] } }, "type": "object" @@ -1605,11 +1768,21 @@ "description": "Normalized tool policy for a run.", "properties": { "blocked": { - "description": "Adapter-native tool names or toolset names to block.", + "description": "Adapter-native tool names to block.", "items": { "type": "string" }, "type": "array" + }, + "enabled": { + "description": "Adapter-native tool names to expose. `None` preserves the harness default.", + "items": { + "type": "string" + }, + "type": [ + "array", + "null" + ] } }, "type": "object" diff --git a/schemas/runtime-context.schema.json b/schemas/runtime-context.schema.json index 24dda3cfc..8cc84af37 100644 --- a/schemas/runtime-context.schema.json +++ b/schemas/runtime-context.schema.json @@ -84,6 +84,13 @@ "$ref": "#/$defs/ControlLocation", "description": "Where NeMo Fabric control code runs." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.", + "type": "object" + }, "environment_id": { "description": "Environment handle id.", "type": "string" diff --git a/schemas/runtime-handle.schema.json b/schemas/runtime-handle.schema.json index 272a45efe..57a21c639 100644 --- a/schemas/runtime-handle.schema.json +++ b/schemas/runtime-handle.schema.json @@ -59,6 +59,13 @@ "$ref": "#/$defs/ControlLocation", "description": "Where NeMo Fabric control code runs." }, + "env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment variables visible to the harness and its tools.", + "type": "object" + }, "environment_id": { "description": "Environment handle id.", "type": "string" diff --git a/skills/integrations/consumer/nemo-fabric-integrate/SKILL.md b/skills/integrations/consumer/nemo-fabric-integrate/SKILL.md index 6f32781f9..b3bdf5c06 100644 --- a/skills/integrations/consumer/nemo-fabric-integrate/SKILL.md +++ b/skills/integrations/consumer/nemo-fabric-integrate/SKILL.md @@ -65,12 +65,26 @@ with the public models and helper methods: from nemo_fabric import ( FabricConfig, HarnessConfig, + InstructionConfig, + InstructionsConfig, MetadataConfig, ModelConfig, RuntimeConfig, + ToolsConfig, ) +def to_tools_config(job) -> ToolsConfig | None: + enabled = job.enabled_tools + blocked = list(job.blocked_tools) + if enabled is None and not blocked: + return None + return ToolsConfig( + enabled=None if enabled is None else list(enabled), + blocked=blocked, + ) + + def to_fabric_config(job) -> FabricConfig: config = FabricConfig( metadata=MetadataConfig(name=job.name), @@ -80,9 +94,23 @@ def to_fabric_config(job) -> FabricConfig: provider=job.provider, model=job.model, api_key_env=job.api_key_env, + base_url=job.base_url, ) }, - runtime=RuntimeConfig(input_schema="chat", output_schema="message"), + instructions=( + InstructionsConfig( + system=InstructionConfig(content=job.system_instruction), + ) + if job.system_instruction is not None + else None + ), + runtime=RuntimeConfig( + input_schema="chat", + output_schema="message", + timeout_seconds=job.timeout_seconds, + max_turns=job.max_turns, + ), + tools=to_tools_config(job), ) config.add_skill_path(job.skill_dir) config.add_mcp_server( @@ -94,7 +122,8 @@ def to_fabric_config(job) -> FabricConfig: return config ``` -- Shape capabilities with `add_skill_path`, `remove_skill_path`, +- Shape capabilities with `ToolsConfig`, `block_tools`, `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. diff --git a/skills/integrations/consumer/nemo-fabric-integrate/references/config-mapping.md b/skills/integrations/consumer/nemo-fabric-integrate/references/config-mapping.md index 6d163ea64..e4b1cf706 100644 --- a/skills/integrations/consumer/nemo-fabric-integrate/references/config-mapping.md +++ b/skills/integrations/consumer/nemo-fabric-integrate/references/config-mapping.md @@ -18,9 +18,11 @@ Import these from the top-level `nemo_fabric` package: | `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). | +| `ModelConfig` | Provider, model, credentials (`api_key_env`), endpoint, and sampling. | +| `InstructionsConfig` / `InstructionConfig` | Portable agent instructions and replacement mode. | +| `RuntimeConfig` | Input/output labels, artifact location, invocation timeout, and harness turn limit. | +| `EnvironmentConfig` | Execution environment, workspace, and harness-visible variables. | +| `ToolsConfig` | Adapter-native tool selection and blocking policy. | | `McpConfig` / `McpServerConfig` | MCP servers and exposure. | | `SkillConfig` | Skill directories. | | `TelemetryConfig` | Telemetry providers. | @@ -38,14 +40,28 @@ 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 NeMo Relay observability in the `relay` block +- `enable_relay(...)` for NVIDIA NeMo Relay observability in the `relay` block +- `ToolsConfig(enabled=..., blocked=...)` for tool policy +- `block_tools(...)` for additive deny policy ```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"), + models={"default": ModelConfig(provider=job.provider, model=job.model, api_key_env=job.api_key_env, base_url=job.base_url)}, + instructions=( + InstructionsConfig( + system=InstructionConfig(content=job.system_instruction), + ) + if job.system_instruction is not None + else None + ), + runtime=RuntimeConfig( + input_schema="chat", + output_schema="message", + timeout_seconds=job.timeout_seconds, + max_turns=job.max_turns, + ), ) config.add_skill_path(job.skill_dir) ``` @@ -78,13 +94,16 @@ 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 normalized fields for portable behavior: models, instructions, turn + limit, runtime, environment, tools, skills, MCP, and telemetry. +- Supply request context through `RunRequest.context` for each invocation; + request context is not part of `FabricConfig`. - Use `harness.settings` for adapter-owned configuration the selected adapter - understands (for example Hermes Agent 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 + understands (for example Claude permission policy, Codex sandbox controls, or + Deep Agents subagent definitions). Executable paths, state directories, and + Relay command discovery are runtime implementation details, not adapter + settings. Adapter settings are not portable, and `doctor(...)` does not yet + validate their contents. Validate them against the adapter's docs and your integration tests. - Use `metadata` and extension fields for caller-owned annotations NeMo Fabric carries but does not interpret. Config `metadata` is not echoed into diff --git a/tests/_utils/configs.py b/tests/_utils/configs.py index c72d23ed6..a23be868e 100644 --- a/tests/_utils/configs.py +++ b/tests/_utils/configs.py @@ -14,7 +14,7 @@ def hermes_shim_config() -> FabricConfig: harness=HarnessConfig( adapter_id="test.fabric.hermes_shim", resolution="preinstalled", - settings={"workspace": "./repos/my-service"}, + settings={}, ), models={"default": ModelConfig(provider="test", model="test-model", temperature=0.0)}, runtime=RuntimeConfig(input_schema="chat", output_schema="message", artifacts="./artifacts"), @@ -41,7 +41,6 @@ def swebench_shim_config() -> FabricConfig: resolution="preinstalled", settings={ "mode": "swebench_shim", - "workspace": "./repos/my-service", "target_file": "calculator.py", "expected_before": "return 41", "replacement": "return 42", @@ -70,7 +69,6 @@ def harbor_swebench_config() -> FabricConfig: resolution="preinstalled", settings={ "mode": "swebench_shim", - "workspace": workspace, "target_file": "django/contrib/auth/forms.py", "expected_before": " kwargs.setdefault(\"required\", False)\n super().__init__(*args, **kwargs)", "replacement": " kwargs.setdefault(\"required\", False)\n kwargs.setdefault('disabled', True)\n super().__init__(*args, **kwargs)", diff --git a/tests/adapters/test_adapaters_common_utils.py b/tests/adapters/test_adapaters_common_utils.py index b9ed3ba2a..34f766368 100644 --- a/tests/adapters/test_adapaters_common_utils.py +++ b/tests/adapters/test_adapaters_common_utils.py @@ -71,77 +71,62 @@ def test_virtualenv_subprocess_env_preserves_environment_outside_virtualenv( def test_request_payload(): - assert common_utils.request_payload({"request": {"input": "hello"}}) == {"input": "hello"} + assert common_utils.request_payload({"request": {"input": "hello"}}) == { + "input": "hello" + } assert common_utils.request_payload({}) == {} @pytest.mark.parametrize( - ("provider", "expected"), - [ - ("nvidia", "https://integrate.api.nvidia.com/v1"), - ("openai", None), - (None, None), - ], -) -def test_default_base_url( - provider: str | None, - expected: str | None, -): - assert common_utils.default_base_url(provider) == expected - - -@pytest.mark.parametrize( - ("settings", "model_config", "expected"), + ("model_config", "expected"), [ ( - {"base_url": "https://settings.example/v1"}, - {"provider": "nvidia", "settings": {"base_url": "https://model.example/v1"}}, - "https://settings.example/v1", + {"provider": "nvidia", "base_url": "https://model.example/v1"}, + "https://model.example/v1", ), ( - {}, - {"provider": "openai", "settings": {"base_url": "https://model.example/v1"}}, + {"provider": "openai", "base_url": "https://model.example/v1"}, "https://model.example/v1", ), - ({}, {"provider": "nvidia"}, "https://integrate.api.nvidia.com/v1"), - ({}, {"provider": "other"}, None), + ({"provider": "nvidia"}, None), + ({"provider": "other"}, None), ], ) def test_get_base_url( - settings: dict[str, object], model_config: dict[str, object], expected: str | None, ): - assert common_utils.get_base_url(settings, model_config) == expected + assert common_utils.get_base_url(model_config) == expected @pytest.mark.parametrize( - ("selected_model", "models", "expected"), + ("models", "expected"), [ ( - "fast", {"fast": {"provider": "nvidia", "model": "fast-model"}}, {"provider": "nvidia", "model": "fast-model"}, ), ( - None, {"default": {"provider": "nvidia", "model": "default-model"}}, {"provider": "nvidia", "model": "default-model"}, ), - ("bad", {"bad": "not-a-model-config"}, {}), + ({"bad": "not-a-model-config"}, {}), + ( + { + "fast": {"provider": "nvidia", "model": "fast-model"}, + "slow": {"provider": "nvidia", "model": "slow-model"}, + }, + {}, + ), ], ) def test_selected_model_config( - selected_model: str | None, models: dict[str, object], expected: dict[str, object], ): - settings = {} - if selected_model is not None: - settings["model"] = selected_model payload = { "config": { - "harness": {"settings": settings}, + "harness": {"settings": {}}, "models": models, } } @@ -149,6 +134,31 @@ def test_selected_model_config( assert common_utils.selected_model_config(payload) == expected +def test_normalized_instruction_runtime_and_tool_accessors(): + payload = { + "config": { + "instructions": { + "system": {"content": "Be concise.", "mode": "replace"} + }, + "runtime": {"timeout_seconds": 12.5, "max_turns": 7}, + "tools": { + "enabled": [], + "blocked": ["Bash"], + }, + }, + "runtime_context": { + "environment": {"env": {"VISIBLE": "yes"}}, + }, + } + + assert common_utils.system_instruction(payload) == "Be concise." + assert common_utils.max_turns(payload) == 7 + assert common_utils.timeout_seconds(payload, default=30) == 12.5 + assert common_utils.environment_env(payload) == {"VISIBLE": "yes"} + assert common_utils.blocked_tools(payload) == ["Bash"] + assert common_utils.enabled_tools(payload) == [] + + def test_payload_accessors_use_canonical_plan_fields(tmp_path): base_dir = str(tmp_path / "outer") payload = { @@ -173,10 +183,14 @@ def test_payload_accessors_use_canonical_plan_fields(tmp_path): assert common_utils.agent_name(payload) == "outer-agent" assert common_utils.base_dir(payload) == base_dir assert common_utils.runtime_context(payload) == payload["runtime_context"] - assert common_utils.environment_payload(payload) == {"workspace": "/runtime-workspace"} + assert common_utils.environment_payload(payload) == { + "workspace": "/runtime-workspace" + } assert common_utils.settings_payload(payload) == {"inner": True} assert common_utils.models_payload(payload) == {"inner": {"model": "inner-model"}} - assert common_utils.capability_plan(payload) == {"native": {"skill_paths": ["skills"]}} + assert common_utils.capability_plan(payload) == { + "native": {"skill_paths": ["skills"]} + } @pytest.mark.parametrize("value", [None, "", "relative/path"]) @@ -251,11 +265,15 @@ def fake_import(name: str, *args: object, **kwargs: object) -> object: monkeypatch.setattr(builtins, "__import__", fake_import) - assert common_utils.dump_yaml({"model": {"default": "demo"}}) == json.dumps( - {"model": {"default": "demo"}}, - indent=2, - sort_keys=False, - ) + "\n" + assert ( + common_utils.dump_yaml({"model": {"default": "demo"}}) + == json.dumps( + {"model": {"default": "demo"}}, + indent=2, + sort_keys=False, + ) + + "\n" + ) @pytest.mark.parametrize( @@ -316,9 +334,7 @@ def test_load_relay_plugin_config_wraps_and_normalizes_bare_observability_config previous_atof_dir.mkdir(parents=True) previous_atif_dir.mkdir(parents=True) (previous_atof_dir / "events.atof.jsonl").write_text("{}", encoding="utf-8") - (previous_atif_dir / "trajectory-old.atif.json").write_text( - "{}", encoding="utf-8" - ) + (previous_atif_dir / "trajectory-old.atif.json").write_text("{}", encoding="utf-8") payload = { "agent_name": "review-agent", "base_dir": str(tmp_path), @@ -336,7 +352,9 @@ def test_load_relay_plugin_config_wraps_and_normalizes_bare_observability_config assert plugin_config["components"][0]["kind"] == "observability" assert observability["version"] == 2 file_sink, stream_sink = observability["atof"]["sinks"] - assert file_sink["output_directory"] == str(tmp_path / "custom-relay" / "runtime-current") + assert file_sink["output_directory"] == str( + tmp_path / "custom-relay" / "runtime-current" + ) assert file_sink["filename"] == "events.atof.jsonl" assert file_sink["mode"] == "overwrite" assert Path(file_sink["output_directory"]).is_dir() @@ -344,14 +362,21 @@ def test_load_relay_plugin_config_wraps_and_normalizes_bare_observability_config "type": "stream", "url": "https://example.test/events", } - assert observability["atif"]["output_directory"] == str(tmp_path / "artifacts" / "relay" / "runtime-current") - assert observability["atif"]["filename_template"] == "trajectory-{session_id}.atif.json" + assert observability["atif"]["output_directory"] == str( + tmp_path / "artifacts" / "relay" / "runtime-current" + ) + assert ( + observability["atif"]["filename_template"] + == "trajectory-{session_id}.atif.json" + ) assert observability["atif"]["agent_name"] == "review-agent" assert observability["atif"]["model_name"] == "nvidia/review-model" assert Path(observability["atif"]["output_directory"]).is_dir() atof_file = Path(file_sink["output_directory"]) / "events.atof.jsonl" - atif_file = Path(observability["atif"]["output_directory"]) / "trajectory-current.atif.json" + atif_file = ( + Path(observability["atif"]["output_directory"]) / "trajectory-current.atif.json" + ) atof_file.write_text("{}", encoding="utf-8") atif_file.write_text("{}", encoding="utf-8") diff --git a/tests/adapters/test_adapters_common_relay_gateway.py b/tests/adapters/test_adapters_common_relay_gateway.py index 8180ec7a0..757e1da56 100644 --- a/tests/adapters/test_adapters_common_relay_gateway.py +++ b/tests/adapters/test_adapters_common_relay_gateway.py @@ -70,9 +70,7 @@ def test_relay_cli_contract_selects_compatible_contract( @pytest.mark.parametrize("output", ["nemo-relay 0.5.9", "nemo-relay 0.7.0"]) -def test_relay_cli_contract_rejects_unsupported_version( - monkeypatch, tmp_path, output -): +def test_relay_cli_contract_rejects_unsupported_version(monkeypatch, tmp_path, output): monkeypatch.setattr( relay_gateway.subprocess, "run", @@ -86,9 +84,7 @@ def test_relay_cli_contract_rejects_unsupported_version( relay_gateway.relay_cli_contract(tmp_path / "nemo-relay") -def test_relay_cli_contract_rejects_unparseable_output( - monkeypatch, tmp_path -): +def test_relay_cli_contract_rejects_unparseable_output(monkeypatch, tmp_path): monkeypatch.setattr( relay_gateway.subprocess, "run", @@ -118,6 +114,8 @@ def test_start_relay_gateway_captures_logs_and_waits_for_health(monkeypatch, tmp bind="127.0.0.1:43210", url="http://127.0.0.1:43210", log_path=log_path, + openai_base_url="https://openai.example/v1", + anthropic_base_url="https://anthropic.example", ) started = relay_gateway.start_relay_gateway( @@ -132,6 +130,10 @@ def test_start_relay_gateway_captures_logs_and_waits_for_health(monkeypatch, tmp str(config_path), "--bind", "127.0.0.1:43210", + "--openai-base-url", + "https://openai.example/v1", + "--anthropic-base-url", + "https://anthropic.example", ] assert mock_popen.call_args.kwargs["cwd"] == tmp_path assert mock_popen.call_args.kwargs["stderr"] is subprocess.STDOUT @@ -179,9 +181,7 @@ def test_start_relay_gateway_stops_failed_process_and_preserves_log( assert log_path.exists() -def test_start_relay_gateway_reports_readiness_and_stop_failures( - monkeypatch, tmp_path -): +def test_start_relay_gateway_reports_readiness_and_stop_failures(monkeypatch, tmp_path): config_path = tmp_path / "config.toml" config_path.write_text("", encoding="utf-8") readiness_error = relay_gateway.RelayGatewayError("not ready") diff --git a/tests/adapters/test_claude_adapter.py b/tests/adapters/test_claude_adapter.py index f575fe0b9..a687abba3 100644 --- a/tests/adapters/test_claude_adapter.py +++ b/tests/adapters/test_claude_adapter.py @@ -89,12 +89,14 @@ def test_claude_descriptor_is_narrow_and_versioned(): "config": { "accepts": [ "models", - "tools", + "models.base_url", + "instructions.system", + "runtime.max_turns", + "tools.enabled", "tools.blocked", "mcp", "skills", - "telemetry", - ] + ], }, "telemetry": { "providers": { @@ -121,16 +123,16 @@ def claude_payload_fixture(tmp_path) -> dict[str, Any]: "harness": { "adapter_id": "nvidia.fabric.claude", "settings": { - "system_prompt": "Review carefully.", "allowed_tools": ["Read"], "permission_mode": "dontAsk", - "max_turns": 4, "max_budget_usd": 1.5, "setting_sources": [], - "timeout_seconds": 30, - "env": {"ANTHROPIC_API_KEY": "configured-secret"}, }, }, + "instructions": { + "system": {"content": "Review carefully.", "mode": "replace"} + }, + "runtime": {"timeout_seconds": 30, "max_turns": 4}, "models": { "default": { "provider": "anthropic", @@ -143,7 +145,10 @@ def claude_payload_fixture(tmp_path) -> dict[str, Any]: "runtime_context": { "runtime_id": "runtime-claude-1", "invocation_id": "invocation-1", - "environment": {"workspace": str(workspace)}, + "environment": { + "workspace": str(workspace), + "env": {"ANTHROPIC_API_KEY": "configured-secret"}, + }, "artifacts": {"root": str(tmp_path / "artifacts"), "artifacts": []}, }, "request": {"request_id": "request-1", "input": "Inspect the patch"}, @@ -178,6 +183,7 @@ def test_build_options_maps_normalized_capabilities_and_claude_settings(claude_p assert options.tools is None assert options.allowed_tools == ["Read"] assert options.disallowed_tools == ["Bash"] + assert options.hooks is not None assert options.permission_mode == "dontAsk" assert options.max_turns == 4 assert options.max_budget_usd == 1.5 @@ -201,6 +207,27 @@ def test_build_options_maps_normalized_capabilities_and_claude_settings(claude_p assert "ANTHROPIC_BASE_URL" not in options.env +async def test_tool_policy_hooks_gate_built_in_and_mcp_tools(claude_payload): + claude_payload["config"]["tools"] = { + "enabled": ["Read"], + "blocked": ["Bash"], + } + + options = adapter.build_options(claude_payload) + + assert options.tools == ["Read"] + assert options.hooks is not None + hook = options.hooks["PreToolUse"][0].hooks[0] + assert await hook({"tool_name": "Read"}, None, {"signal": None}) == {} + for tool_name in ("Bash", "mcp__repo__search"): + output = await hook( + {"tool_name": tool_name}, + None, + {"signal": None}, + ) + assert output["hookSpecificOutput"]["permissionDecision"] == "deny" + + @pytest.fixture(name="relay_payload") def relay_payload_fixture(claude_payload, tmp_path) -> dict[str, Any]: relay_intent_path = tmp_path / "relay-config.json" @@ -228,6 +255,12 @@ def relay_payload_fixture(claude_payload, tmp_path) -> dict[str, Any]: def test_prepare_claude_relay_writes_gateway_config_and_complete_hook_plugin( relay_payload, monkeypatch, tmp_path ): + relay_payload["config"]["models"]["default"].update( + { + "provider": "acme", + "base_url": "https://acme.example/v1/", + } + ) executable = tmp_path / "bin" / "nemo-relay" executable.parent.mkdir() executable.touch() @@ -258,6 +291,7 @@ def test_prepare_claude_relay_writes_gateway_config_and_complete_hook_plugin( assert relay.gateway.bind == "127.0.0.1:43210" assert relay.gateway.url == "http://127.0.0.1:43210" assert relay.gateway.log_path == relay.gateway.config_path.parent / "gateway.log" + assert relay.gateway.anthropic_base_url == "https://acme.example" with relay.gateway.config_path.open("rb") as stream: assert tomllib.load(stream) == {"agents": {"claude": {"command": "claude"}}} with (relay.gateway.config_path.parent / "plugins.toml").open("rb") as stream: @@ -370,28 +404,6 @@ def test_build_options_maps_blocked_tools_to_disallowed_tools(claude_payload): assert options.disallowed_tools == ["Bash", "WebFetch"] -@pytest.mark.parametrize( - ("name", "normalized_field"), - [ - ("model_name", "FabricConfig.models"), - ("cwd", "FabricConfig.environment.workspace"), - ("tools", "FabricConfig.tools"), - ("disallowed_tools", "FabricConfig.tools.blocked"), - ("mcp_servers", "FabricConfig.mcp"), - ("skills", "FabricConfig.skills"), - ], -) -def test_build_options_rejects_normalized_capabilities_in_harness_settings( - claude_payload, name, normalized_field -): - claude_payload["config"]["harness"]["settings"][name] = [] - - with pytest.raises( - adapter.AdapterConfigError, match=normalized_field.replace(".", r"\.") - ): - adapter.build_options(claude_payload) - - def test_build_options_rejects_skill_path_without_skill_manifest(claude_payload): skill_path = Path(claude_payload["capability_plan"]["native"]["skill_paths"][0]) (skill_path / "SKILL.md").unlink() @@ -400,88 +412,116 @@ def test_build_options_rejects_skill_path_without_skill_manifest(claude_payload) adapter.build_options(claude_payload) -def test_build_options_maps_nvidia_provider_to_claude_gateway_environment( +def test_build_options_maps_custom_provider_to_claude_gateway_environment( claude_payload, ): model = claude_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", + "provider": "acme", "model": "aws/anthropic/claude-opus-4-5", - "api_key_env": "NVIDIA_API_KEY", - "settings": {"base_url": "https://nvidia.example/v1/"}, + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1/", } ) - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + claude_payload["runtime_context"]["environment"]["env"].pop("ANTHROPIC_API_KEY") + os.environ["ACME_API_KEY"] = "acme-secret" options = adapter.build_options(claude_payload) assert options.model == "aws/anthropic/claude-opus-4-5" - assert options.env["ANTHROPIC_BASE_URL"] == "https://nvidia.example" - assert options.env["ANTHROPIC_API_KEY"] == "nvidia-secret" + assert options.env["ANTHROPIC_BASE_URL"] == "https://acme.example" + assert options.env["ANTHROPIC_API_KEY"] == "acme-secret" assert options.env["ANTHROPIC_AUTH_TOKEN"] == "" -def test_build_options_uses_nvidia_provider_endpoint_and_default_credential( +def test_build_options_requires_custom_provider_api_key_env( claude_payload, ): model = claude_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", + "provider": "acme", "model": "aws/anthropic/claude-opus-4-5", + "base_url": "https://acme.example/v1", } ) model.pop("api_key_env") - claude_payload["config"]["harness"]["settings"].pop("env") - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" - os.environ["NVIDIA_FRONTIER_BASE_URL"] = "https://frontier.example/v1" + claude_payload["runtime_context"]["environment"]["env"].pop("ANTHROPIC_API_KEY") - options = adapter.build_options(claude_payload) - - assert options.env["ANTHROPIC_BASE_URL"] == "https://frontier.example" - assert options.env["ANTHROPIC_API_KEY"] == "nvidia-secret" + with pytest.raises(adapter.AdapterConfigError, match="api_key_env is required"): + adapter.build_options(claude_payload) -def test_build_options_requires_nvidia_provider_endpoint(claude_payload): +def test_build_options_requires_custom_provider_credential(claude_payload): model = claude_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", + "provider": "acme", "model": "aws/anthropic/claude-opus-4-5", - "api_key_env": "NVIDIA_API_KEY", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1", } ) - model.pop("settings", None) - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" - os.environ.pop("NVIDIA_FRONTIER_BASE_URL", None) + os.environ.pop("ACME_API_KEY", None) - with pytest.raises(adapter.AdapterConfigError, match="NVIDIA_FRONTIER_BASE_URL"): + with pytest.raises(adapter.AdapterConfigError, match="ACME_API_KEY is required"): adapter.build_options(claude_payload) -def test_build_options_requires_nvidia_provider_credential(claude_payload): +def test_build_options_requires_custom_provider_endpoint(claude_payload): model = claude_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", + "provider": "acme", "model": "aws/anthropic/claude-opus-4-5", - "api_key_env": "NVIDIA_API_KEY", + "api_key_env": "ACME_API_KEY", } ) - os.environ.pop("NVIDIA_API_KEY", None) + claude_payload["runtime_context"]["environment"]["env"] = { + "ACME_API_KEY": "acme-secret" + } - with pytest.raises(adapter.AdapterConfigError, match="NVIDIA_API_KEY is required"): + with pytest.raises(adapter.AdapterConfigError, match="base_url is required"): adapter.build_options(claude_payload) -def test_selected_model_rejects_unsupported_provider(claude_payload): - model = claude_payload["config"]["models"]["default"] - model["provider"] = "openai" +@pytest.mark.parametrize( + ("name", "value"), + [ + ("ANTHROPIC_API_KEY", "conflicting-secret"), + ("ANTHROPIC_AUTH_TOKEN", "conflicting-token"), + ("ANTHROPIC_BASE_URL", "https://other.example"), + ], +) +def test_build_options_rejects_model_environment_conflicts( + claude_payload, + name, + value, +): + claude_payload["config"]["models"]["default"] = { + "provider": "acme", + "model": "aws/anthropic/claude-opus-4-5", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1", + } + claude_payload["runtime_context"]["environment"]["env"] = { + "ACME_API_KEY": "acme-secret", + name: value, + } with pytest.raises( - adapter.AdapterConfigError, match="provider must be anthropic or nvidia" + adapter.AdapterConfigError, + match=rf"environment\.env\.{name} conflicts", ): + adapter.build_options(claude_payload) + + +def test_selected_model_rejects_empty_provider(claude_payload): + model = claude_payload["config"]["models"]["default"] + model["provider"] = "" + + with pytest.raises(adapter.AdapterConfigError, match="non-empty string"): adapter.selected_model(claude_payload) @@ -1040,8 +1080,7 @@ def test_build_options_forwards_anthropic_auth_environment( ): model = claude_payload["config"]["models"]["default"] model.pop("api_key_env") - settings = claude_payload["config"]["harness"]["settings"] - settings.pop("env") + claude_payload["runtime_context"]["environment"].pop("env") for name in ANTHROPIC_AUTH_ENV_NAMES: os.environ.pop(name, None) os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward" diff --git a/tests/adapters/test_codex_adapter.py b/tests/adapters/test_codex_adapter.py index 51c323cc6..b17a191c8 100644 --- a/tests/adapters/test_codex_adapter.py +++ b/tests/adapters/test_codex_adapter.py @@ -75,6 +75,9 @@ def codex_payload_fixture(tmp_path): "model": "openai/gpt-5.4", } }, + "instructions": { + "system": {"content": "Review carefully.", "mode": "replace"} + }, "runtime": {}, }, "runtime_context": { @@ -179,7 +182,7 @@ def test_single_invocation_uses_native_thread_and_turn_contract( os.environ["CODEX_HOME"] = str(tmp_path / "codex-home") os.environ["CODEX_INTERNAL_ORIGINATOR_OVERRIDE"] = "parent-codex" os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward" - codex_payload["config"]["harness"]["settings"]["env"] = { + codex_payload["runtime_context"]["environment"]["env"] = { "CODEX_EXPLICIT": "forward-me" } @@ -209,6 +212,7 @@ def test_single_invocation_uses_native_thread_and_turn_contract( start = client.thread_start.await_args.kwargs assert start["model"] == "gpt-5.4" assert start["model_provider"] == "openai" + assert start["base_instructions"] == "Review carefully." assert start["sandbox"] == adapter.Sandbox.workspace_write assert start["config"] == { "features": {"web_search": False}, @@ -397,11 +401,13 @@ def test_sdk_rejects_falsy_non_list_skill_paths(codex_payload, mock_codex, skill mock_codex.assert_not_called() -def test_sdk_can_use_an_explicit_codex_runtime(codex_payload, mock_codex, tmp_path): +def test_sdk_test_override_can_use_an_explicit_codex_runtime( + codex_payload, mock_codex, tmp_path, monkeypatch +): codex_bin = tmp_path / "bin" / "codex" codex_bin.parent.mkdir() codex_bin.touch() - codex_payload["config"]["harness"]["settings"]["codex_bin"] = str(codex_bin) + monkeypatch.setenv("FABRIC_TEST_CODEX_BIN", str(codex_bin)) output = invoke_once(codex_payload) @@ -410,8 +416,10 @@ def test_sdk_can_use_an_explicit_codex_runtime(codex_payload, mock_codex, tmp_pa @pytest.mark.parametrize("codex_bin", ["bin/codex", "~/bin/codex"]) -def test_sdk_resolves_relative_codex_runtime_from_base_dir(codex_payload, codex_bin): - codex_payload["config"]["harness"]["settings"]["codex_bin"] = codex_bin +def test_sdk_test_override_resolves_relative_runtime_from_base_dir( + codex_payload, codex_bin, monkeypatch +): + monkeypatch.setenv("FABRIC_TEST_CODEX_BIN", codex_bin) config = adapter.sdk_config(codex_payload, relay=None) @@ -419,9 +427,11 @@ def test_sdk_resolves_relative_codex_runtime_from_base_dir(codex_payload, codex_ assert config.codex_bin == str((base_dir / codex_bin).resolve()) -def test_sdk_keeps_absolute_codex_runtime_path(codex_payload, tmp_path): +def test_sdk_test_override_keeps_absolute_runtime_path( + codex_payload, tmp_path, monkeypatch +): codex_bin = tmp_path / "bin" / ".." / "codex" - codex_payload["config"]["harness"]["settings"]["codex_bin"] = str(codex_bin) + monkeypatch.setenv("FABRIC_TEST_CODEX_BIN", str(codex_bin)) config = adapter.sdk_config(codex_payload, relay=None) @@ -562,68 +572,74 @@ def test_incomplete_sdk_turn_is_failed(codex_payload, mock_codex): assert output["turn_status"] == "completed" -def test_selected_model_rejects_unsupported_provider(codex_payload, mock_codex): +def test_custom_provider_requires_explicit_api_key_env(codex_payload, mock_codex): model = codex_payload["config"]["models"]["default"] - model["provider"] = "anthropic" + model.update( + { + "provider": "acme", + "model": "acme/code-model", + "base_url": "https://acme.example/v1", + } + ) error = runtime_start_error(codex_payload) assert error.code == "codex_invalid_configuration" - assert "provider must be openai or nvidia" in error.message + assert "api_key_env is required" in error.message mock_codex.assert_not_called() -def test_nvidia_provider_uses_responses_api_and_nvidia_credential( +def test_custom_provider_uses_responses_api_and_configured_credential( codex_payload, mock_codex ): model = codex_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", - "model": "openai/gpt-oss-120b", - "api_key_env": "NVIDIA_API_KEY", - "settings": {"base_url": "https://nvidia.example/v1/"}, + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1/", } ) - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ["ACME_API_KEY"] = "acme-secret" output = invoke_once(codex_payload) assert output["completed"] is True client = mock_codex.instances[0] - assert client.config.env["NVIDIA_API_KEY"] == "nvidia-secret" + assert client.config.env["ACME_API_KEY"] == "acme-secret" start = client.thread_start.await_args.kwargs - assert start["model"] == "openai/gpt-oss-120b" - assert start["model_provider"] == "nvidia" + assert start["model"] == "acme/code-model" + assert start["model_provider"] == "acme" assert Path(client.config.env["CODEX_HOME"]).parts[-3:] == ( ".fabric", "codex", - "nvidia-home", + "custom-provider-home", ) assert start["config"]["features"] == {"web_search": False} assert start["config"]["model_providers"] == { - "nvidia": { - "name": "NVIDIA", - "base_url": "https://nvidia.example/v1", - "env_key": "NVIDIA_API_KEY", + "acme": { + "name": "acme", + "base_url": "https://acme.example/v1", + "env_key": "ACME_API_KEY", "wire_api": "responses", } } -def test_nvidia_provider_normalizes_codex_home_creation_failure( +def test_custom_provider_normalizes_codex_home_creation_failure( codex_payload, mock_codex, monkeypatch ): model = codex_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", - "model": "openai/gpt-oss-120b", - "api_key_env": "NVIDIA_API_KEY", - "settings": {"base_url": "https://nvidia.example/v1"}, + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1", } ) - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ["ACME_API_KEY"] = "acme-secret" async def fail_to_create_home(*_args, **_kwargs): raise OSError("read-only filesystem") @@ -636,43 +652,41 @@ async def fail_to_create_home(*_args, **_kwargs): mock_codex.assert_not_called() -def test_nvidia_provider_requires_credential(codex_payload, mock_codex): +def test_custom_provider_requires_credential(codex_payload, mock_codex): model = codex_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", - "model": "openai/gpt-oss-120b", - "api_key_env": "NVIDIA_API_KEY", + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1", } ) - os.environ.pop("NVIDIA_API_KEY", None) + os.environ.pop("ACME_API_KEY", None) error = runtime_start_error(codex_payload) assert error.code == "codex_invalid_configuration" - assert "NVIDIA_API_KEY is required" in error.message - assert not (adapter.state_dir(codex_payload) / "nvidia-home").exists() + assert "ACME_API_KEY is required" in error.message + assert not (adapter.state_dir(codex_payload) / "custom-provider-home").exists() mock_codex.assert_not_called() -def test_nvidia_provider_requires_endpoint(codex_payload, mock_codex): +def test_custom_provider_requires_explicit_endpoint(codex_payload, mock_codex): model = codex_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", - "model": "openai/gpt-oss-120b", - "api_key_env": "NVIDIA_API_KEY", + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", } ) - model.pop("settings", None) - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" - os.environ.pop("NVIDIA_FRONTIER_BASE_URL", None) + os.environ["ACME_API_KEY"] = "acme-secret" error = runtime_start_error(codex_payload) assert error.code == "codex_invalid_configuration" - assert "NVIDIA_FRONTIER_BASE_URL" in error.message - assert not (adapter.state_dir(codex_payload) / "nvidia-home").exists() + assert "base_url is required" in error.message mock_codex.assert_not_called() @@ -738,32 +752,56 @@ def test_relay_uses_gateway_and_request_scoped_sdk_config( stop_gateway.assert_called_once_with(process) -def test_relay_rejects_nvidia_provider(codex_payload, mock_codex): +def test_relay_routes_custom_provider_through_gateway(codex_payload, tmp_path): model = codex_payload["config"]["models"]["default"] model.update( { - "provider": "nvidia", - "model": "openai/gpt-oss-120b", - "api_key_env": "NVIDIA_API_KEY", - "settings": {"base_url": "https://nvidia.example/v1"}, + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1", } ) codex_payload["telemetry_plan"] = { "providers": ["relay"], "relay_enabled": True, } - os.environ["NVIDIA_API_KEY"] = "nvidia-secret" + os.environ["ACME_API_KEY"] = "acme-secret" + gateway = adapter.relay_gateway.RelayGatewayLaunch( + executable=tmp_path / "nemo-relay", + config_path=tmp_path / "relay" / "config.toml", + bind="127.0.0.1:43210", + url="http://127.0.0.1:43210", + log_path=tmp_path / "relay" / "gateway.log", + openai_base_url="https://acme.example/v1", + ) + relay = adapter.CodexRelaySettings( + gateway=gateway, + plugin_config={"version": 1, "components": []}, + ) - error = runtime_start_error(codex_payload) + adapter.validate_runtime_payload(codex_payload) + config = adapter.thread_config(codex_payload, relay) - assert error.code == "codex_invalid_configuration" - assert error.message == ("NeMo Relay requires the built-in openai model provider") - mock_codex.assert_not_called() + assert config["model_providers"]["acme"] == { + "name": "acme", + "base_url": gateway.url, + "env_key": "ACME_API_KEY", + "wire_api": "responses", + } def test_prepare_relay_reuses_one_resolved_executable( codex_payload, monkeypatch, tmp_path ): + codex_payload["config"]["models"]["default"].update( + { + "provider": "acme", + "model": "acme/code-model", + "api_key_env": "ACME_API_KEY", + "base_url": "https://acme.example/v1/", + } + ) codex_payload["telemetry_plan"] = { "providers": ["relay"], "relay_enabled": True, @@ -793,6 +831,7 @@ def test_prepare_relay_reuses_one_resolved_executable( assert relay is not None assert relay.gateway.executable == executable assert relay.gateway.url == "http://127.0.0.1:43210" + assert relay.gateway.openai_base_url == "https://acme.example/v1" resolve.assert_called_once_with( Path(codex_payload["base_dir"]).resolve(), "nemo-relay", @@ -852,7 +891,6 @@ def test_native_sdk_controls_and_telemetry_are_request_scoped( { "personality": "pragmatic", "reasoning_effort": "xhigh", - "service_name": "fabric-codex-test", "output_schema": { "type": "object", "properties": {"summary": {"type": "string"}}, @@ -887,7 +925,7 @@ def test_native_sdk_controls_and_telemetry_are_request_scoped( client = mock_codex.instances[0] start = client.thread_start.await_args.kwargs assert start["personality"] == adapter.Personality.pragmatic - assert start["service_name"] == "fabric-codex-test" + assert "service_name" not in start assert start["config"]["otel"] == { "environment": "test", "trace_exporter": { @@ -910,7 +948,7 @@ async def block(): mock_blocking_thread.handle.run.side_effect = block mock_codex.next_thread = mock_blocking_thread - codex_payload["config"]["harness"]["settings"]["timeout_seconds"] = 0.01 + codex_payload["config"]["runtime"]["timeout_seconds"] = 0.01 output = invoke_once(codex_payload) @@ -920,34 +958,6 @@ async def block(): assert client.closed is True -@pytest.mark.parametrize( - "setting", ["codex_command", "codex_args", "codex_profile", "skip_git_repo_check"] -) -def test_cli_only_settings_are_rejected(codex_payload, setting): - codex_payload["config"]["harness"]["settings"][setting] = "legacy" - - error = runtime_start_error(codex_payload) - - assert error.code == "codex_invalid_configuration" - assert setting in error.message - - -@pytest.mark.parametrize( - ("setting", "normalized_field"), - [("mcp_servers", "FabricConfig.mcp"), ("skills", "FabricConfig.skills")], -) -def test_normalized_capabilities_reject_harness_settings( - codex_payload, mock_codex, setting, normalized_field -): - codex_payload["config"]["harness"]["settings"][setting] = {} - - error = runtime_start_error(codex_payload) - - assert error.code == "codex_invalid_configuration" - assert normalized_field in error.message - mock_codex.assert_not_called() - - def test_adapter_rejects_structured_input(codex_payload): codex_payload["request"]["input"] = { "messages": [{"role": "user", "content": "Inspect the change."}] @@ -971,9 +981,10 @@ def test_descriptor_has_no_codex_binary_requirement(): } assert descriptor["config"]["accepts"] == [ "models", + "models.base_url", + "instructions.system", "mcp", "skills", - "telemetry", ] assert "requirements" not in descriptor @@ -1025,7 +1036,7 @@ def test_environment_preserves_runtime_telemetry_env(codex_payload): "CODEX_EXPLICIT": "telemetry", } } - codex_payload["config"]["harness"]["settings"]["env"] = { + codex_payload["runtime_context"]["environment"]["env"] = { "CODEX_EXPLICIT": "configured" } os.environ["FABRIC_RELAY_CONFIG_PATH"] = "/tmp/parent-relay.json" diff --git a/tests/adapters/test_deepagents.py b/tests/adapters/test_deepagents.py index 15041f190..540e2dba4 100644 --- a/tests/adapters/test_deepagents.py +++ b/tests/adapters/test_deepagents.py @@ -151,12 +151,16 @@ def make(tmp_path: Path, *, runtime_id: str = "run-1") -> dict[str, Any]: return { "base_dir": str(tmp_path), "config": { - "harness": {"settings": {"system_prompt": "be concise"}}, + "harness": {"settings": {}}, + "instructions": { + "system": {"content": "be concise", "mode": "replace"} + }, "models": { "default": { "provider": "nvidia", "model": "nvidia/nemotron-3-nano-30b-a3b", "api_key_env": "NVIDIA_API_KEY", + "base_url": "https://integrate.api.nvidia.com/v1", } }, }, @@ -415,9 +419,7 @@ async def test_native_telemetry_exports_without_artifacts( assert fake_relay["wrapped"] assert fake_relay["plugin_open"] - assert fake_relay["plugin_configs"] == [ - payload["telemetry_plan"]["native_config"] - ] + assert fake_relay["plugin_configs"] == [payload["telemetry_plan"]["native_config"]] assert output["telemetry"] == { "enabled": True, "provider": "native", @@ -620,11 +622,11 @@ async def test_mcp_servers_become_adapter_tools( @pytest.mark.usefixtures("use_real_langgraph") -async def test_blocked_tools_middleware_blocks_configured_tools(): +async def test_tool_policy_middleware_enforces_enabled_and_blocked_tools(): pytest.importorskip("langchain.agents.middleware") from langchain_core.messages import ToolMessage - middleware = adapter.blocked_tools_middleware({"write_file"}) + middleware = adapter.tool_policy_middleware({"read_file"}, {"write_file"}) async def handler(_request: types.SimpleNamespace) -> str: return "executed" @@ -641,6 +643,10 @@ def request(name: str) -> types.SimpleNamespace: allowed = await middleware.awrap_tool_call(request("read_file"), handler) assert allowed == "executed" + unselected = await middleware.awrap_tool_call(request("search"), handler) + assert isinstance(unselected, ToolMessage) + assert unselected.status == "error" + @pytest.mark.usefixtures("use_real_langgraph") async def test_real_langgraph_async_checkpointer(tmp_path, make_payload, monkeypatch): @@ -931,17 +937,17 @@ async def test_blocked_tools_reject_unenforceable_subagents( [ ( {"name": "researcher"}, - "harness.settings.deepagents.subagents must be a list when tools.blocked is configured.", + "harness.settings.deepagents.subagents must be a list when a tools policy is configured.", ), ( [{"name": "researcher"}, "invalid"], - "Deep Agents subagents must be mappings when tools.blocked is configured.", + "Deep Agents subagents must be mappings when a tools policy is configured.", ), ], ) def test_gated_subagents_reject_invalid_configuration(subagents, message): with pytest.raises(adapter.AdapterConfigError) as error: - adapter._gated_subagents(subagents, {"write_file"}) + adapter._gated_subagents(subagents, None, {"write_file"}) assert str(error.value) == message @@ -1102,6 +1108,19 @@ async def test_openai_compatible_provider_requires_api_key_env(tmp_path, make_pa await adapter.DeepAgentsRuntime().start(lifecycle_start_payload(payload)) +async def test_openai_compatible_provider_requires_base_url(tmp_path, make_payload): + os.environ["CUSTOM_API_KEY"] = "sk-test" + payload = make_payload(tmp_path) + payload["config"]["models"]["default"] = { + "provider": "openai-compatible", + "model": "some/model", + "api_key_env": "CUSTOM_API_KEY", + } + + with pytest.raises(adapter.AdapterConfigError, match="base_url"): + await adapter.DeepAgentsRuntime().start(lifecycle_start_payload(payload)) + + def test_main_serves_persistent_runtime(monkeypatch): serve = MagicMock() monkeypatch.setattr(adapter.lifecycle, "serve", serve) diff --git a/tests/adapters/test_hermes_adapter.py b/tests/adapters/test_hermes_adapter.py index 5c1186aac..9284e2719 100644 --- a/tests/adapters/test_hermes_adapter.py +++ b/tests/adapters/test_hermes_adapter.py @@ -181,22 +181,20 @@ def test_build_hermes_config_maps_fabric_config_to_hermes_config(): "config": { "harness": { "settings": { - "model": "review", - "max_iterations": 4, - "disabled_toolsets": ["browser"], - "terminal_backend": "local", "terminal_timeout": 90, - "enabled_toolsets": "git", - "toolset_platform": "cli", "plugins_enabled": ["custom/plugin"], } }, - "tools": {"blocked": ["shell", "browser"]}, + "runtime": {"max_turns": 4}, + "tools": { + "enabled": ["git"], + "blocked": ["browser"], + }, "models": { "review": { "provider": "nvidia", "model": "nvidia/review-model", - "settings": {"base_url": "https://model.example/v1"}, + "base_url": "https://model.example/v1", } }, }, @@ -212,7 +210,7 @@ def test_build_hermes_config_maps_fabric_config_to_hermes_config(): }, "agent": { "max_turns": 4, - "disabled_toolsets": ["shell", "browser"], + "disabled_toolsets": ["browser"], }, "terminal": { "backend": "local", @@ -249,8 +247,8 @@ def test_default_max_iterations_matches_hermes_library_default(): assert adapter.DEFAULT_MAX_ITERATIONS == hermes_default -def test_build_hermes_config_omits_max_turns_when_max_iterations_unset(): - # When max_iterations is unset the config layer must leave agent.max_turns +def test_build_hermes_config_omits_max_turns_when_fabric_limit_unset(): + # When max_turns is unset the config layer must leave agent.max_turns # absent so Hermes applies its own default rather than a starving override. payload = { "config": { @@ -264,12 +262,13 @@ def test_build_hermes_config_omits_max_turns_when_max_iterations_unset(): assert "max_turns" not in config["agent"] -def test_build_hermes_config_omits_max_turns_when_max_iterations_null(): - # An explicit null max_iterations is treated like unset: agent.max_turns is +def test_build_hermes_config_omits_max_turns_when_fabric_limit_null(): + # An explicit null max_turns is treated like unset: agent.max_turns is # omitted so Hermes applies its own default instead of a starving override. payload = { "config": { - "harness": {"settings": {"max_iterations": None}}, + "harness": {"settings": {}}, + "runtime": {"max_turns": None}, "models": {"default": {"provider": "nvidia", "model": "nvidia/test-model"}}, } } @@ -335,14 +334,8 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( "agent_name": "matrix-agent", "base_dir": str(tmp_path), "config": { - "harness": { - "settings": { - "model": "review", - "enabled_toolsets": ["git", "shell"], - "toolset_platform": "cli", - "terminal_backend": "local", - } - }, + "harness": {"settings": {}}, + "tools": {"enabled": ["git", "shell"]}, "models": { "review": { "provider": "nvidia", @@ -359,7 +352,6 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( assert config["model"] == { "provider": "nvidia", "default": "nvidia/review-model", - "base_url": "https://integrate.api.nvidia.com/v1", } assert config["terminal"]["cwd"] == str(tmp_path / "workspace") assert config["skills"]["external_dirs"] == [str(tmp_path / "skills" / "review")] @@ -472,6 +464,43 @@ async def test_runtime_start_rejects_native_telemetry(): await adapter.HermesRuntime().start(payload) +async def test_runtime_start_overrides_inherited_terminal_environment( + monkeypatch, + tmp_path: Path, +): + os.environ["TERMINAL_ENV"] = "docker" + + def stop_after_environment_setup(*_args, **_kwargs): + assert os.environ["TERMINAL_ENV"] == "local" + raise RuntimeError("stop after environment setup") + + monkeypatch.setattr(adapter, "write_hermes_config", stop_after_environment_setup) + payload = { + "base_dir": str(tmp_path), + "config": { + "harness": {"settings": {}}, + "models": {"default": {"provider": "nvidia", "model": "test-model"}}, + }, + "runtime_context": { + "runtime_id": "runtime-terminal-env", + "environment": {"workspace": str(tmp_path)}, + "artifacts": {"root": str(tmp_path / "artifacts")}, + }, + } + + with pytest.raises(RuntimeError, match="stop after environment setup"): + await adapter.HermesRuntime().start(payload) + + +def test_artifact_root_resolves_relative_to_base_dir(tmp_path: Path): + payload = { + "base_dir": str(tmp_path), + "runtime_context": {"artifacts": {"root": "run-artifacts"}}, + } + + assert adapter._artifact_root(payload) == (tmp_path / "run-artifacts").resolve() + + async def test_persistent_runtime_reuses_hermes_agent_session_and_history( monkeypatch, tmp_path: Path, @@ -537,20 +566,18 @@ async def test_persistent_runtime_reuses_hermes_agent_session_and_history( "agent_name": "demo", "base_dir": str(tmp_path), "config": { - "harness": { - "settings": { - "hermes_home": "./hermes-home", - "enabled_toolsets": [], - "system_prompt": "system", - # Explicit null must resolve to DEFAULT_MAX_ITERATIONS (not int(None)). - "max_iterations": None, - } + "harness": {"settings": {}}, + "instructions": { + "system": {"content": "system", "mode": "replace"} }, + "runtime": {"max_turns": None}, + "tools": {"enabled": []}, "models": { "default": { "provider": "test-provider", "model": "test-model", "api_key_env": "TEST_API_KEY", + "temperature": 0.2, } }, }, @@ -599,6 +626,7 @@ async def test_persistent_runtime_reuses_hermes_agent_session_and_history( skip_memory=True, save_trajectories=False, max_tokens=512, + request_overrides={"temperature": 0.2}, reasoning_config={"effort": "none"}, platform="fabric", session_id="runtime-fabric-123", @@ -626,7 +654,12 @@ async def test_persistent_runtime_reuses_hermes_agent_session_and_history( assert second["response"] == "second response" assert "session_id" not in second assert Path(second["hermes_home"]) == ( - tmp_path / "hermes-home" / "runtimes" / "runtime-fabric-123" + tmp_path + / "artifacts" + / ".fabric" + / "hermes" + / "runtimes" + / "runtime-fabric-123" ) diff --git a/tests/adapters/test_hermes_config_builder.py b/tests/adapters/test_hermes_config_builder.py index ece011e46..7c8bb9a5f 100644 --- a/tests/adapters/test_hermes_config_builder.py +++ b/tests/adapters/test_hermes_config_builder.py @@ -34,6 +34,5 @@ def test_build_hermes_config_omits_unset_values_without_hermes_agent(): assert config["model"] == { "provider": "nvidia", "default": "nvidia/test-model", - "base_url": "https://integrate.api.nvidia.com/v1", } assert config["agent"] == {} diff --git a/tests/e2e/test_claude.py b/tests/e2e/test_claude.py index 669848f27..45706f91c 100644 --- a/tests/e2e/test_claude.py +++ b/tests/e2e/test_claude.py @@ -76,19 +76,18 @@ def fabric_config( "setting_sources": [], "permission_mode": "dontAsk", } - if cli_path is not None: - settings.update( - { - "cli_path": str(cli_path), - "env": { - "CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK": "1", - "MOCK_CLAUDE_CLI_LOG": str(tmp_path / "claude-args.jsonl"), - "MOCK_CLAUDE_CLI_ENV_LOG": str(tmp_path / "claude-env.jsonl"), - }, - } - ) + environment_env = ( + { + "FABRIC_TEST_CLAUDE_CLI_PATH": str(cli_path), + "CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK": "1", + "MOCK_CLAUDE_CLI_LOG": str(tmp_path / "claude-args.jsonl"), + "MOCK_CLAUDE_CLI_ENV_LOG": str(tmp_path / "claude-env.jsonl"), + } + if cli_path is not None + else {} + ) if nemo_relay_command is not None: - settings["nemo_relay_command"] = str(nemo_relay_command) + environment_env["FABRIC_TEST_NEMO_RELAY_COMMAND"] = str(nemo_relay_command) config = FabricConfig( metadata=MetadataConfig(name="claude-runtime-test"), harness=HarnessConfig( @@ -110,6 +109,7 @@ def fabric_config( provider="local", workspace=tmp_path, artifacts=tmp_path / "artifacts", + env=environment_env, ), ) if cli_path is not None: diff --git a/tests/e2e/test_codex.py b/tests/e2e/test_codex.py index c59c462cb..9b8102f4c 100644 --- a/tests/e2e/test_codex.py +++ b/tests/e2e/test_codex.py @@ -18,13 +18,6 @@ from _utils.utils import assert_semantic_relay_artifacts -def _select_codex_runtime(config): - codex_bin = os.environ.get("FABRIC_TEST_CODEX_BIN") - if codex_bin: - config.harness.settings["codex_bin"] = codex_bin - return config - - async def test_codex_sdk(): if os.environ.get("RUN_FABRIC_CODEX_INTEGRATION") != "1": pytest.skip("set RUN_FABRIC_CODEX_INTEGRATION=1 to run") @@ -47,14 +40,14 @@ async def test_codex_sdk_with_relay(): ) if relay_command is None: pytest.fail("the nemo-relay CLI is required") - await _run_relay(relay_command) + await _run_relay(str(relay_command)) async def _run() -> None: from examples.code_review_agent import BASE_DIR, codex_config from nemo_fabric import Fabric - config = _select_codex_runtime(codex_config()) + config = codex_config() nonce = f"fabric-{uuid.uuid4().hex[:8]}" client = Fabric() single = await client.run( @@ -92,8 +85,9 @@ async def _run_relay(relay_command: str) -> None: from examples.code_review_agent import BASE_DIR, codex_config, with_relay from nemo_fabric import Fabric - config = _select_codex_runtime(with_relay(codex_config())) - config.harness.settings["nemo_relay_command"] = relay_command + config = with_relay(codex_config()) + assert config.environment is not None + config.environment.env["FABRIC_TEST_NEMO_RELAY_COMMAND"] = relay_command client = Fabric() result = await client.run( config, diff --git a/tests/e2e/test_deepagents.py b/tests/e2e/test_deepagents.py index f7b957bce..52c93e818 100644 --- a/tests/e2e/test_deepagents.py +++ b/tests/e2e/test_deepagents.py @@ -24,8 +24,7 @@ async def test_deepagents_persistent_host_with_mock_model(api_server, tmp_path): from nemo_fabric import EnvironmentConfig, Fabric, RuntimeConfig config = deepagents_config() - config.harness.settings["base_url"] = f"{api_server}/v1" - config.harness.settings["workspace"] = str(tmp_path) + config.models["default"].base_url = f"{api_server}/v1" config.environment = EnvironmentConfig( provider="local", workspace=tmp_path, @@ -60,8 +59,7 @@ async def test_deepagents_persistent_host_with_relay_and_mock_model( from nemo_fabric import EnvironmentConfig, Fabric, RuntimeConfig config = with_relay(deepagents_config()) - config.harness.settings["base_url"] = f"{api_server}/v1" - config.harness.settings["workspace"] = str(tmp_path) + config.models["default"].base_url = f"{api_server}/v1" config.environment = EnvironmentConfig( provider="local", workspace=tmp_path, diff --git a/tests/e2e/test_hermes_config_mapping.py b/tests/e2e/test_hermes_config_mapping.py index 9920260c2..7f6c0d70b 100644 --- a/tests/e2e/test_hermes_config_mapping.py +++ b/tests/e2e/test_hermes_config_mapping.py @@ -52,19 +52,21 @@ def payload(tmpdir: str) -> dict: return { "agent_name": "code-review-agent", "base_dir": tmpdir, - "environment": { - "workspace": f"{tmpdir}/workspace", - }, - "models": { - "default": { - "provider": "nvidia", - "model": "nvidia/nemotron-3-nano-30b-a3b", - "api_key_env": "NVIDIA_API_KEY", - } + "config": { + "environment": { + "workspace": f"{tmpdir}/workspace", + }, + "models": { + "default": { + "provider": "nvidia", + "model": "nvidia/nemotron-3-nano-30b-a3b", + "api_key_env": "NVIDIA_API_KEY", + "base_url": "https://integrate.api.nvidia.com/v1", + } + }, + "tools": {"enabled": []}, }, "settings": { - "enabled_toolsets": [], - "terminal_backend": "local", "terminal_timeout": 30, }, "capabilities": { diff --git a/tests/e2e/test_hermes_e2e.py b/tests/e2e/test_hermes_e2e.py index 4b798a59b..80d82f6f4 100644 --- a/tests/e2e/test_hermes_e2e.py +++ b/tests/e2e/test_hermes_e2e.py @@ -29,7 +29,7 @@ async def test_hermes_persistent_host_reuses_native_session( ): os.environ["ADAPTER_PYTHON"] = sys.executable config = with_relay(hermes_config()) - config.harness.settings["base_url"] = f"{api_server}/v1" + config.models["default"].base_url = f"{api_server}/v1" with warnings.catch_warnings(): warnings.simplefilter("error", RuntimeWarning) @@ -61,7 +61,7 @@ async def test_hermes_persistent_host_with_relay( ): os.environ["ADAPTER_PYTHON"] = sys.executable config = with_relay(hermes_config()) - config.harness.settings["base_url"] = f"{api_server}/v1" + config.models["default"].base_url = f"{api_server}/v1" async with await Fabric().start_runtime( config, base_dir=code_review_agent_dir @@ -115,7 +115,7 @@ async def run_hermes_with_relay( self.code_review_agent_dir = code_review_agent_dir self.api_server = api_server config = self.config_builder() - config.harness.settings["base_url"] = f"{api_server}/v1" + config.models["default"].base_url = f"{api_server}/v1" config = with_relay(config) self.result = await Fabric().run( diff --git a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/fabric-adapter.json b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/fabric-adapter.json index 3df5e70c0..e485a2de8 100644 --- a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/fabric-adapter.json +++ b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/fabric-adapter.json @@ -17,10 +17,10 @@ }, "config": { "accepts": [ - "tools", + "models", + "models.temperature", "mcp", - "skills", - "telemetry" + "skills" ] }, "telemetry": { diff --git a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py index d89373e1c..642d98ce9 100644 --- a/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py +++ b/tests/fixtures/hermes-shim-agent/adapters/hermes-shim/src/nemo_fabric_test_adapters/hermes_shim/adapter.py @@ -54,7 +54,10 @@ def request_payload(payload: dict[str, Any]) -> dict[str, Any]: def environment_payload(payload: dict[str, Any]) -> dict[str, Any]: return ( - runtime_context(payload).get("environment") or payload.get("environment") or {} + runtime_context(payload).get("environment") + or fabric_config(payload).get("environment") + or payload.get("environment") + or {} ) @@ -87,7 +90,7 @@ def run_shim(payload: dict[str, Any]) -> dict[str, Any]: "mode": "shim", "received": request.get("input"), "runtime_id": context.get("runtime_id"), - "workspace": environment.get("workspace") or settings.get("workspace"), + "workspace": environment.get("workspace"), "native_skill_paths": (capabilities.get("native") or {}).get("skill_paths", []), "native_mcp_servers": sorted( (capabilities.get("native") or {}).get("mcp_servers", {}).keys() @@ -108,7 +111,10 @@ def run_swebench_shim(payload: dict[str, Any]) -> dict[str, Any]: request = request_payload(payload) context = request.get("context", {}) environment = environment_payload(payload) - workspace = Path(environment.get("workspace") or settings.get("workspace") or ".") + workspace_value = environment.get("workspace") + if not isinstance(workspace_value, str) or not workspace_value: + raise ValueError("runtime_context.environment.workspace is required") + workspace = Path(workspace_value) target_file = workspace / settings.get("target_file", "calculator.py") before = settings.get("expected_before") after = settings.get("replacement") diff --git a/tests/integrations/test_harbor_runner.py b/tests/integrations/test_harbor_runner.py index 02577be77..e91ef9264 100644 --- a/tests/integrations/test_harbor_runner.py +++ b/tests/integrations/test_harbor_runner.py @@ -242,13 +242,15 @@ def test_claude_calculator_run_uses_current_adapter_contract(): adapter_id="nvidia.fabric.claude", workspace="/app", model_name="anthropic/claude-sonnet-4-5", - harness_settings={"max_turns": 20, "timeout_seconds": 600}, + max_turns=20, + timeout_seconds=600, ) settings = config.harness.settings assert config.harness.adapter_id == "nvidia.fabric.claude" assert settings["permission_mode"] == "bypassPermissions" - assert settings["max_turns"] == 20 + assert config.runtime.max_turns == 20 + assert config.runtime.timeout_seconds == 600 assert config.models["default"].provider == "anthropic" dockerfile = CALCULATOR_DOCKERFILE.read_text(encoding="utf-8") assert "-e /opt/nemo-fabric/adapters/claude" in dockerfile @@ -311,6 +313,7 @@ def test_harbor_calculator_documents_explicit_cli_commands(): in swebench ) assert "PIP_FIND_LINKS" not in swebench + assert 'PATH=/tmp/nemo-fabric-config/.relay/bin:$PATH' in swebench assert "--dataset swe-bench/swe-bench-verified" in swebench for flag in ( "--path", @@ -424,17 +427,22 @@ def test_swebench_matrix_translates_harbor_inputs_to_typed_config(tmp_path: Path ), ) tools = build_harbor_config( + adapter_id="nvidia.fabric.deepagents", + workspace="/testbed", + blocked_tools=["browser"], + telemetry="relay", + ) + selected_tools = build_harbor_config( adapter_id="nvidia.fabric.hermes", workspace="/testbed", + enabled_tools=[], blocked_tools=["browser"], telemetry="relay", ) claude = build_harbor_config( adapter_id="nvidia.fabric.claude", workspace="/testbed", - harness_settings={ - "nemo_relay_command": "/tmp/nemo-fabric-config/.relay/bin/nemo-relay" - }, + harness_settings={"allowed_tools": ["Read"]}, ) assert base.environment is not None @@ -454,17 +462,19 @@ def test_swebench_matrix_translates_harbor_inputs_to_typed_config(tmp_path: Path ] assert tools.tools is not None assert tools.tools.blocked == ["browser"] - assert "enabled_toolsets" not in tools.harness.settings + assert tools.tools.enabled is None + assert selected_tools.tools is not None + assert selected_tools.tools.enabled == [] + assert selected_tools.tools.blocked == ["browser"] assert relay.telemetry is not None assert "relay" in relay.telemetry.providers assert relay.relay is not None assert relay.relay.observability.atif.enabled is True assert relay.relay.observability.atof.enabled is True assert claude.harness.settings["permission_mode"] == "bypassPermissions" - assert claude.harness.settings["env"] == {"IS_SANDBOX": "1"} - assert claude.harness.settings["nemo_relay_command"] == ( - "/tmp/nemo-fabric-config/.relay/bin/nemo-relay" - ) + assert claude.environment is not None + assert claude.environment.env == {"IS_SANDBOX": "1"} + assert claude.harness.settings["allowed_tools"] == ["Read"] assert not list(SWEBENCH_ROOT.rglob("*.yaml")) assert not (SWEBENCH_ROOT / "harbor_swebench_config.py").exists() diff --git a/tests/python/test_native_sdk.py b/tests/python/test_native_sdk.py index 7ca05cb92..8b4339917 100644 --- a/tests/python/test_native_sdk.py +++ b/tests/python/test_native_sdk.py @@ -65,6 +65,19 @@ def test_native_run_rejects_multiple_request_sources(hermes_shim_agent_dir: Path ) +def test_plan_rejects_adapter_incompatible_normalized_tool_policy(): + config = base_config() + config.harness.adapter_id = "nvidia.fabric.codex" + config.models["default"].temperature = None + config.block_tools("Bash") + + with pytest.raises( + FabricConfigError, + match=r"nvidia\.fabric\.codex.*tools\.blocked", + ): + Fabric().plan(config, base_dir=BASE_DIR) + + async def smoke(client: Fabric, fixture_agent: Path) -> None: example_config = base_config() @@ -96,10 +109,7 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: "harness": { "adapter_id": "test.fabric.hermes_shim", "resolution": "preinstalled", - "settings": { - "workspace": "./repos/my-service", - "timeout_seconds": 30, - }, + "settings": {}, }, "models": { "default": { @@ -112,6 +122,7 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: "input_schema": "chat", "output_schema": "message", "artifacts": "./artifacts", + "timeout_seconds": 30, }, "environment": { "provider": "local", @@ -145,8 +156,8 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: assert typed_plan["telemetry_plan"]["relay_enabled"] is True resolved_config = typed_plan.config.to_mapping() assert resolved_config["harness"]["adapter_id"] == "test.fabric.hermes_shim" - assert resolved_config["harness"]["settings"]["workspace"] == "./repos/my-service" - assert resolved_config["harness"]["settings"]["timeout_seconds"] == 30 + assert "settings" not in resolved_config["harness"] + assert resolved_config["runtime"]["timeout_seconds"] == 30 assert resolved_config["consumer_extension"] == { "base": True, "custom": True, diff --git a/tests/python/test_notebook_examples.py b/tests/python/test_notebook_examples.py index 1b66238d3..c8d484a44 100644 --- a/tests/python/test_notebook_examples.py +++ b/tests/python/test_notebook_examples.py @@ -16,7 +16,13 @@ import pytest from examples.code_review_agent import BASE_DIR, base_config -from nemo_fabric import Fabric, HarnessConfig, ModelConfig +from nemo_fabric import ( + Fabric, + HarnessConfig, + InstructionConfig, + InstructionsConfig, + ModelConfig, +) ROOT = Path(__file__).resolve().parents[2] @@ -34,6 +40,8 @@ def _variation_harness_definitions(base_dir=BASE_DIR): "base_config": base_config, "BASE_DIR": base_dir, "HarnessConfig": HarnessConfig, + "InstructionConfig": InstructionConfig, + "InstructionsConfig": InstructionsConfig, "ModelConfig": ModelConfig, "os": os, "Path": Path, diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index a8f689f03..6dca975f6 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -26,6 +26,8 @@ from nemo_fabric import FabricRuntimeError from nemo_fabric import FabricStateError from nemo_fabric import HarnessConfig +from nemo_fabric import InstructionConfig +from nemo_fabric import InstructionsConfig from nemo_fabric import McpConfig from nemo_fabric import MetadataConfig from nemo_fabric import RelayAtifConfig @@ -145,13 +147,18 @@ def test_typed_config_authoring_helpers_emit_schema_shape(): output_dir="./artifacts/relay", ) config.block_tools("browser", "shell", "browser") + assert config.tools is not None + config.tools.enabled = ["terminal"] assert isinstance(config.mcp, McpConfig) assert isinstance(config.skills, SkillConfig) assert isinstance(config.telemetry, TelemetryConfig) assert isinstance(config.tools, ToolsConfig) - assert config.to_mapping()["tools"] == {"blocked": ["browser", "shell"]} + assert config.to_mapping()["tools"] == { + "enabled": ["terminal"], + "blocked": ["browser", "shell"], + } assert config.to_mapping()["skills"] == {"paths": ["./skills/review"]} assert config.to_mapping()["mcp"] == { "servers": { @@ -201,6 +208,76 @@ def test_typed_tools_config_serializes_blocked_policy(): assert config.to_mapping()["tools"] == {"blocked": ["browser", "shell"]} +def test_typed_config_serializes_normalized_execution_fields(): + config = FabricConfig( + metadata=MetadataConfig(name="demo"), + harness=HarnessConfig(adapter_id="test.fabric.shim"), + instructions=InstructionsConfig( + system=InstructionConfig(content="Be concise.", mode="replace") + ), + runtime=RuntimeConfig(timeout_seconds=12.5, max_turns=7), + environment=EnvironmentConfig(env={"VISIBLE": "yes"}), + models={ + "default": { + "provider": "nvidia", + "model": "nvidia/test", + "base_url": "https://models.example/v1", + } + }, + tools=ToolsConfig(enabled=[], blocked=["browser"]), + ) + + mapping = config.to_mapping() + assert mapping["instructions"]["system"] == { + "content": "Be concise.", + "mode": "replace", + } + assert mapping["runtime"]["max_turns"] == 7 + assert mapping["runtime"]["timeout_seconds"] == 12.5 + assert mapping["environment"]["env"] == {"VISIBLE": "yes"} + assert mapping["models"]["default"]["base_url"] == ( + "https://models.example/v1" + ) + assert mapping["tools"] == {"enabled": [], "blocked": ["browser"]} + + with pytest.raises(ValidationError, match="greater than 0"): + RuntimeConfig(timeout_seconds=0) + with pytest.raises(ValidationError, match="finite number"): + RuntimeConfig(timeout_seconds=float("inf")) + with pytest.raises(ValidationError, match="greater than 0"): + RuntimeConfig(max_turns=0) + with pytest.raises(ValidationError, match="both enabled and blocked"): + ToolsConfig(enabled=["browser"], blocked=["browser"]) + + +@pytest.mark.parametrize("content", ["", " "]) +def test_instruction_content_must_be_non_empty(content: str): + with pytest.raises(ValidationError): + InstructionConfig(content=content) + + raw = _plan()["config"] + raw["instructions"] = { + "system": {"content": content, "mode": "replace"} + } + with pytest.raises(FabricConfigError, match="non-empty string"): + _FabricConfigSnapshot.from_mapping(raw) + + +def test_max_turns_matches_rust_u32_range(): + maximum = (1 << 32) - 1 + + assert RuntimeConfig(max_turns=maximum).max_turns == maximum + with pytest.raises(ValidationError): + RuntimeConfig(max_turns=maximum + 1) + + raw = _plan()["config"] + raw["runtime"] = {"max_turns": maximum} + assert _FabricConfigSnapshot.from_mapping(raw).runtime.max_turns == maximum + raw["runtime"]["max_turns"] = maximum + 1 + with pytest.raises(FabricConfigError, match="between 1 and 4294967295"): + _FabricConfigSnapshot.from_mapping(raw) + + def test_run_plan_config_block_tools_emits_canonical_shape(): config = _FabricConfigSnapshot.from_mapping(_plan()["config"]) @@ -209,6 +286,31 @@ def test_run_plan_config_block_tools_emits_canonical_shape(): assert config.to_mapping()["tools"] == {"blocked": ["browser", "shell"]} +def test_run_plan_config_preserves_normalized_tools_and_execution_fields(): + raw = _plan()["config"] + raw.update( + { + "instructions": { + "system": {"content": "Be concise.", "mode": "replace"} + }, + "runtime": {"timeout_seconds": 9, "max_turns": 5}, + "environment": {"provider": "local", "env": {"VISIBLE": "yes"}}, + "tools": {"enabled": [], "blocked": ["browser"]}, + } + ) + + config = _FabricConfigSnapshot.from_mapping(raw) + + assert config.to_mapping()["instructions"]["system"]["content"] == "Be concise." + assert config.to_mapping()["runtime"]["max_turns"] == 5 + assert config.to_mapping()["runtime"]["timeout_seconds"] == 9 + assert config.to_mapping()["environment"]["env"] == {"VISIBLE": "yes"} + assert config.to_mapping()["tools"] == { + "enabled": [], + "blocked": ["browser"], + } + + def test_run_plan_tools_config_rejects_scalar_blocked_value(): with pytest.raises(FabricConfigError, match="tools blocked"): _ToolsConfig(blocked="browser") # type: ignore[arg-type] @@ -429,10 +531,12 @@ def test_environment_model_defines_extension_field_ownership(): properties = EnvironmentConfig.model_json_schema()["properties"] assert "environment provider" in properties["settings"]["description"] + assert "harness and its tools" in properties["env"]["description"] assert "without NeMo Fabric semantics" in properties["metadata"]["description"] assert "existing environment" in properties["connection"]["description"] assert "environment teardown" in properties["ownership"]["description"] assert "outside or inside" in properties["control_location"]["description"] + assert properties["env"]["propertyNames"]["pattern"] == r"\S" def test_inspection_models_are_typed_read_only_mappings(): @@ -1203,7 +1307,7 @@ def test_fabric_config_constructors_emit_schema_shaped_mappings(): harness=HarnessConfig( adapter_id="test.fabric.shim", resolution="preinstalled", - settings={"workspace": "./ws"}, + settings={"custom_option": "original"}, ), runtime=RuntimeConfig( input_schema="chat", @@ -1211,13 +1315,13 @@ def test_fabric_config_constructors_emit_schema_shaped_mappings(): ), ) copied = config.to_mapping() - copied["harness"]["settings"]["workspace"] = "mutated" + copied["harness"]["settings"]["custom_option"] = "mutated" assert config.schema_version == "fabric.agent/v1alpha1" assert config.metadata.to_mapping() == {"name": "demo"} assert config.harness.adapter_id == "test.fabric.shim" assert config.runtime.input_schema == "chat" - assert config.harness.settings["workspace"] == "./ws" + assert config.harness.settings["custom_option"] == "original" client = NativeClient(NativeRecorder()) client.plan(config) diff --git a/tests/python/test_typed_config.py b/tests/python/test_typed_config.py index 1804852ea..74c22e520 100644 --- a/tests/python/test_typed_config.py +++ b/tests/python/test_typed_config.py @@ -19,8 +19,10 @@ from pathlib import Path from shutil import copytree +import pytest from nemo_fabric import Fabric from nemo_fabric import FabricConfig +from nemo_fabric import FabricConfigError from nemo_fabric import RunRequest from nemo_fabric import RunResult @@ -72,7 +74,6 @@ def _shim_adapter_config() -> FabricConfig: config["harness"] = { "adapter_id": "test.fabric.hermes_shim", "resolution": "preinstalled", - "settings": {"workspace": "./ws"}, } config["models"] = { "default": {"provider": "test", "model": "test-model", "temperature": 0.0} @@ -131,7 +132,38 @@ async def runs_with_typed_config_and_adapter_directory(client: Fabric) -> None: assert result["output"]["received"] == "hello typed" +async def diagnoses_adapter_incompatibility_without_weakening_plan(client: Fabric) -> None: + """Doctor reports unsupported config that strict planning rejects.""" + + config = FabricConfig.from_mapping( + { + "metadata": {"name": "incompatible-agent"}, + "harness": {"adapter_id": "nvidia.fabric.codex"}, + "runtime": {"max_turns": 3}, + "tools": {"enabled": []}, + } + ) + + with pytest.raises(FabricConfigError, match=r"runtime\.max_turns"): + client.plan(config, base_dir=ROOT) + + report = await client.doctor(config, base_dir=ROOT) + + assert report.status == "fail" + assert any( + check.name == "config.unsupported" + and check.metadata.get("field") == "runtime.max_turns" + for check in report.checks + ) + assert any( + check.name == "capability.unsupported" + and "tools.enabled" in check.message + for check in report.checks + ) + + async def test_typed_config(): client = Fabric() await resolves_and_diagnoses_typed_config(client) await runs_with_typed_config_and_adapter_directory(client) + await diagnoses_adapter_incompatibility_without_weakening_plan(client)