From fb403113419c348267df2dbbe7e58af3fb49c2ba Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:09:04 -0700 Subject: [PATCH 1/3] Add typed Relay telemetry config Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- ATTRIBUTIONS-Python.md | 2 +- .../nemo_fabric_adapters/codex_cli/adapter.py | 77 +- .../src/nemo_fabric_adapters/common/hermes.py | 10 +- .../src/nemo_fabric_adapters/common/utils.py | 58 +- adapters/deepagents/README.md | 4 +- adapters/deepagents/pyproject.toml | 2 +- .../deepagents/adapter.py | 22 +- adapters/deepagents/uv.lock | 2 +- .../hermes_cli/adapter.py | 2 +- .../hermes_sdk/adapter.py | 9 +- crates/fabric-core/src/config.rs | 628 ++++++++++++++-- crates/fabric-core/src/lib.rs | 9 +- crates/fabric-core/src/runtime.rs | 52 +- .../api/python-library-reference/index.md | 11 + .../nemo_fabric.models.md | 682 +++++++++++++++++- .../config/enum-capabilitykind.mdx | 2 +- .../config/enum-capabilitytarget.mdx | 2 +- .../config/enum-relayatifstorageconfig.mdx | 162 +++++ .../enum-relayatofendpointfieldnamepolicy.mdx | 123 ++++ .../enum-relayatofendpointtransport.mdx | 130 ++++ .../fabric-core/config/enum-relayatofmode.mdx | 123 ++++ .../config/enum-relayotlptransport.mdx | 123 ++++ .../config/enum-relayunsupportedbehavior.mdx | 130 ++++ .../config/enum-telemetryprovider.mdx | 44 ++ .../config/fn-load-adapter-descriptor.mdx | 2 +- .../config/fn-load-fabric-document.mdx | 2 +- ...n-resolve-effective-config-from-config.mdx | 2 +- ...resolve-effective-config-with-profiles.mdx | 2 +- .../config/fn-resolve-effective-config.mdx | 2 +- .../fn-resolve-run-plan-from-config.mdx | 2 +- ...resolve-run-plan-from-effective-config.mdx | 2 +- .../fn-resolve-run-plan-with-profiles.mdx | 2 +- .../config/fn-resolve-run-plan.mdx | 2 +- .../config/fn-validate-agent-directory.mdx | 2 +- .../fabric-core/config/index.mdx | 17 +- .../config/struct-fabricconfig.mdx | 6 +- .../config/struct-relayatifconfig.mdx | 138 ++++ .../config/struct-relayatofconfig.mdx | 122 ++++ .../config/struct-relayatofendpointconfig.mdx | 114 +++ .../config/struct-relaycomponentconfig.mdx | 106 +++ .../fabric-core/config/struct-relayconfig.mdx | 122 ++++ .../config/struct-relayconfigpolicy.mdx | 110 +++ .../struct-relayobservabilityconfig.mdx | 126 ++++ .../config/struct-relayotlpconfig.mdx | 142 ++++ .../config/struct-telemetryconfig.mdx | 22 +- .../config/struct-telemetryplan.mdx | 10 +- .../config/struct-telemetryproviderconfig.mdx | 106 +++ .../fabric-core/doctor/enum-doctorstatus.mdx | 2 +- .../fabric-core/doctor/fn-doctor-plan.mdx | 2 +- .../fabric-core/doctor/index.mdx | 2 +- .../fabric-core/doctor/struct-doctorcheck.mdx | 2 +- .../doctor/struct-doctorreport.mdx | 2 +- .../fabric-core/error/enum-fabricerror.mdx | 2 +- .../fabric-core/error/index.mdx | 2 +- .../fabric-core/error/type-result.mdx | 2 +- .../fabric-core/fn-version.mdx | 2 +- .../fabric-core/index.mdx | 1 + .../fabric-core/runtime/index.mdx | 2 +- .../fabric-core/schema/index.mdx | 2 +- docs/sdk/python.mdx | 19 + examples/code_review_agent/config.py | 166 ++--- examples/harbor/README.md | 38 +- .../environment/fabric/configs/codex.yaml | 3 +- .../fabric/configs/hermes-relay.yaml | 42 +- .../environment/fabric/configs/hermes.yaml | 3 +- .../environment/fabric/configs/smoke.yaml | 3 +- pyproject.toml | 2 +- python/src/nemo_fabric/__init__.py | 99 +-- python/src/nemo_fabric/models.py | 193 ++++- python/src/nemo_fabric/types.py | 208 +++--- schemas/adapter-invocation.schema.json | 638 +++++++++++++++- schemas/agent.schema.json | 635 +++++++++++++++- schemas/effective-config.schema.json | 635 +++++++++++++++- schemas/profile.schema.json | 8 + schemas/run-plan.schema.json | 638 +++++++++++++++- tests/_utils/utils.py | 6 +- .../adapters/test_adapaters_common_hermes.py | 46 +- tests/adapters/test_codex_cli.py | 107 ++- tests/adapters/test_deepagents.py | 34 +- tests/adapters/test_hermes_cli.py | 13 +- tests/adapters/test_hermes_sdk_adapter.py | 14 +- tests/e2e/test_cli.py | 9 +- tests/fixtures/file-config-agent/agent.yaml | 3 +- .../file-config-agent/profiles/codex-cli.yaml | 3 +- .../file-config-agent/profiles/env-local.yaml | 3 +- .../profiles/env-opensandbox.yaml | 19 +- .../profiles/hermes-cli.yaml | 3 +- .../profiles/hermes-sdk.yaml | 3 +- .../profiles/mcp-github.yaml | 31 +- .../profiles/native-otel.yaml | 28 +- .../profiles/relay-openinference.yaml | 44 +- .../profiles/relay-otel.yaml | 36 +- .../file-config-agent/profiles/relay.yaml | 36 +- tests/fixtures/hermes-cli-agent/agent.yaml | 3 +- .../hermes-cli-agent/profiles/env-local.yaml | 3 +- tests/fixtures/hermes-shim-agent/agent.yaml | 3 +- .../hermes-shim-agent/profiles/env-local.yaml | 3 +- .../harbor-swebench-django-13741.yaml | 3 +- .../profiles/mcp-github.yaml | 5 +- .../profiles/swebench-shim.yaml | 3 +- tests/integrations/test_harbor_runner.py | 62 +- tests/python/test_code_review_example.py | 47 +- tests/python/test_native_sdk.py | 13 +- tests/python/test_sdk_contract.py | 210 ++++-- tests/python/test_typed_config.py | 15 +- uv.lock | 16 +- 106 files changed, 6696 insertions(+), 1051 deletions(-) create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx diff --git a/ATTRIBUTIONS-Python.md b/ATTRIBUTIONS-Python.md index fdb91e1f6..5c75ab2d5 100644 --- a/ATTRIBUTIONS-Python.md +++ b/ATTRIBUTIONS-Python.md @@ -5725,7 +5725,7 @@ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ``` -## nemo-relay (0.4.0) +## nemo-relay (0.5.0) ### Licenses License: `Apache-2.0` diff --git a/adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py b/adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py index a2eaa8b0f..270a0655d 100755 --- a/adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py +++ b/adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py @@ -18,7 +18,8 @@ import urllib.request from collections.abc import Mapping from pathlib import Path -from typing import Any, NamedTuple +from typing import Any +from typing import NamedTuple import nemo_fabric_adapters.common.utils as common_utils import tomli_w @@ -87,9 +88,7 @@ def load_thread_id(payload: dict[str, Any], runtime_id: str) -> str | None: value = json.loads(path.read_text(encoding="utf-8")) except json.JSONDecodeError as error: raise RuntimeError(f"invalid Codex runtime state in {path}") from error - if not isinstance(value, dict) or value.get("runtime_id") != runtime_id or not value.get( - "thread_id" - ): + if not isinstance(value, dict) or value.get("runtime_id") != runtime_id or not value.get("thread_id"): raise RuntimeError(f"invalid Codex runtime state in {path}") return str(value["thread_id"]) @@ -129,9 +128,7 @@ def build_command( command = resolve_command(payload, settings.get("codex_command") or "codex") sandbox = str(settings.get("sandbox") or "read-only") if sandbox not in SANDBOXES: - raise ValueError( - f"unsupported Codex sandbox {sandbox!r}; expected one of {sorted(SANDBOXES)}" - ) + raise ValueError(f"unsupported Codex sandbox {sandbox!r}; expected one of {sorted(SANDBOXES)}") args = [command, "exec", "--json"] @@ -172,11 +169,10 @@ def config_overrides(settings: dict[str, Any]) -> Mapping[str, Any]: def native_codex_telemetry_config(payload: dict[str, Any]) -> dict[str, Any]: - telemetry = common_utils.telemetry_payload(payload) - if not telemetry.get("enabled") or common_utils.telemetry_provider(payload) != "native": + if "native" not in common_utils.telemetry_providers(payload): return {} - telemetry_config = telemetry.get("config") or {} + telemetry_config = common_utils.native_telemetry_config(payload) components = telemetry_config.get("components") or [] for component in components: if ( @@ -202,13 +198,14 @@ def native_codex_telemetry_config(payload: dict[str, Any]) -> dict[str, Any]: if transport == "http_binary": exporter = "otlp-http" protocol = "binary" + elif transport == "grpc": + exporter = "otlp-grpc" + protocol = "grpc" elif transport == "http_json": exporter = "otlp-http" protocol = "json" else: - raise ValueError( - f"unsupported Codex native OpenTelemetry transport {transport!r}" - ) + raise ValueError(f"unsupported Codex native OpenTelemetry transport {transport!r}") otel["trace_exporter"] = { exporter: { "endpoint": endpoint, @@ -232,9 +229,7 @@ def apply_config_overrides( for part in parts[:-1]: existing = target.setdefault(part, {}) if not isinstance(existing, dict): - raise ValueError( - f"Codex config override {dotted_key!r} conflicts with {part!r}" - ) + raise ValueError(f"Codex config override {dotted_key!r} conflicts with {part!r}") target = existing target[parts[-1]] = value @@ -266,14 +261,12 @@ def load_codex_profile(settings: dict[str, Any]) -> dict[str, Any]: def write_config_files(payload: dict[str, Any]) -> CodexSettings: settings = common_utils.settings_payload(payload) - telemetry_provider = common_utils.telemetry_provider(payload) - relay_enabled = ( - telemetry_provider == "relay" - and os.environ.get("FABRIC_RELAY_ENABLED") == "true" - ) + telemetry_providers = common_utils.telemetry_providers(payload) + telemetry_provider = telemetry_providers[0] if telemetry_providers else "relay" + relay_enabled = common_utils.relay_enabled(payload) overrides = config_overrides(settings) config = load_codex_profile(settings) - if telemetry_provider == "native": + if "native" in telemetry_providers: merge_config(config, native_codex_telemetry_config(payload)) codex_profile_name = None @@ -298,9 +291,7 @@ def write_config_files(payload: dict[str, Any]) -> CodexSettings: plugin_config=relay_plugin_config, ) if relay_config_path is None: - raise RuntimeError( - "NeMo Relay configuration did not produce a gateway config" - ) + raise RuntimeError("NeMo Relay configuration did not produce a gateway config") relay_command = resolve_command( payload, @@ -337,7 +328,7 @@ def write_config_files(payload: dict[str, Any]) -> CodexSettings: }, "features": {"hooks": True}, "hooks": hooks, - } + }, ) apply_config_overrides(config, overrides) @@ -363,9 +354,7 @@ def write_config_files(payload: dict[str, Any]) -> CodexSettings: def get_codex_profile_path(payload: dict[str, Any]) -> tuple[str, Path]: runtime_id = common_utils.runtime_context(payload).get("runtime_id") if not runtime_id: - raise RuntimeError( - "runtime_context.runtime_id is required for generated Codex profiles" - ) + raise RuntimeError("runtime_context.runtime_id is required for generated Codex profiles") name = f"fabric-{runtime_id}" return name, codex_home() / f"{name}.config.toml" @@ -392,9 +381,7 @@ def toml_value(value: Any) -> str: try: document = tomli_w.dumps({"value": value}) except TypeError as error: - raise ValueError( - "Codex config override values must be a TOML scalar or array" - ) from error + raise ValueError("Codex config override values must be a TOML scalar or array") from error prefix = "value = " if not document.startswith(prefix): raise ValueError("Codex config override values must be a TOML scalar or array") @@ -491,9 +478,7 @@ def wait_for_relay_gateway( while time.monotonic() < deadline: returncode = process.poll() if returncode is not None: - raise RuntimeError( - f"NeMo Relay gateway exited with status {returncode} before becoming ready" - ) + raise RuntimeError(f"NeMo Relay gateway exited with status {returncode} before becoming ready") try: with urllib.request.urlopen(health_url, timeout=1) as response: if 200 <= response.status < 300: @@ -549,15 +534,8 @@ def start_relay_gateway( def process_timeout(payload: dict[str, Any]) -> float: - value = common_utils.settings_payload(payload).get( - "timeout_seconds", DEFAULT_TIMEOUT_SECONDS - ) - if ( - isinstance(value, bool) - or not isinstance(value, (int, float)) - or not math.isfinite(value) - or value <= 0 - ): + value = common_utils.settings_payload(payload).get("timeout_seconds", DEFAULT_TIMEOUT_SECONDS) + if isinstance(value, bool) or not isinstance(value, (int, float)) or not math.isfinite(value) or value <= 0: raise ValueError("timeout_seconds must be a positive finite number") return float(value) @@ -636,9 +614,7 @@ def run_codex(payload: dict[str, Any]) -> dict[str, Any]: if not thread_id: error = error or "Codex runtime invocation did not return a thread identity" if prior_thread_id and thread_id != prior_thread_id: - error = error or ( - f"Codex resumed thread {thread_id}, expected persisted thread {prior_thread_id}" - ) + error = error or (f"Codex resumed thread {thread_id}, expected persisted thread {prior_thread_id}") if thread_id and not error: save_thread_id(payload, runtime_id, thread_id) @@ -659,9 +635,7 @@ def run_codex(payload: dict[str, Any]) -> dict[str, Any]: } if codex_settings.relay_plugin_config is not None: - relay_artifacts = common_utils.collect_relay_artifacts( - codex_settings.relay_plugin_config - ) + relay_artifacts = common_utils.collect_relay_artifacts(codex_settings.relay_plugin_config) output["relay_runtime"] = { "enabled": True, "config_path": os.environ.get("FABRIC_RELAY_CONFIG_PATH"), @@ -676,8 +650,7 @@ def redact_command(command: list[str]) -> list[str]: redacted = list(command) for index, value in enumerate(redacted[:-1]): if value == "--config" and any( - marker in redacted[index + 1].lower() - for marker in ("key", "token", "secret", "password") + marker in redacted[index + 1].lower() for marker in ("key", "token", "secret", "password") ): redacted[index + 1] = "" return redacted diff --git a/adapters/common/src/nemo_fabric_adapters/common/hermes.py b/adapters/common/src/nemo_fabric_adapters/common/hermes.py index a31b9c54c..fb792329d 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/hermes.py +++ b/adapters/common/src/nemo_fabric_adapters/common/hermes.py @@ -40,7 +40,8 @@ def selected_model_config(payload: dict[str, Any]) -> dict[str, Any]: def validate_hermes_telemetry_provider(payload: dict[str, Any]) -> None: - if common_utils.telemetry_provider(payload) != "relay": + providers = common_utils.telemetry_providers(payload) + if any(provider != "relay" for provider in providers): raise ValueError("only relay telemetry is supported for Hermes") @@ -83,10 +84,7 @@ def build_hermes_config(payload: dict[str, Any], *, relay_enabled: bool = False) mcp_servers = native.get("mcp_servers") or {} if mcp_servers: - config["mcp_servers"] = { - name: hermes_mcp_server_config(server) - for name, server in sorted(mcp_servers.items()) - } + config["mcp_servers"] = {name: hermes_mcp_server_config(server) for name, server in sorted(mcp_servers.items())} if "enabled_toolsets" in settings: config["platform_toolsets"] = { @@ -150,7 +148,7 @@ def summarize_hermes_config(config: dict[str, Any]) -> dict[str, Any]: def configure_hermes_relay(payload: dict[str, Any]) -> dict[str, Any] | None: - if os.environ.get("FABRIC_RELAY_ENABLED") != "true": + if not common_utils.relay_enabled(payload): return None relay_plugin_config = common_utils.load_relay_plugin_config(payload) diff --git a/adapters/common/src/nemo_fabric_adapters/common/utils.py b/adapters/common/src/nemo_fabric_adapters/common/utils.py index 644b7fda1..fc590d370 100644 --- a/adapters/common/src/nemo_fabric_adapters/common/utils.py +++ b/adapters/common/src/nemo_fabric_adapters/common/utils.py @@ -9,17 +9,16 @@ import os import sys from pathlib import Path -from typing import Any, TYPE_CHECKING +from typing import TYPE_CHECKING +from typing import Any if TYPE_CHECKING: from nemo_relay import plugin - from nemo_relay.observability import ( - AtifConfig, - AtofConfig, - HttpStorageConfig, - OtlpConfig, - S3StorageConfig, - ) + from nemo_relay.observability import AtifConfig + from nemo_relay.observability import AtofConfig + from nemo_relay.observability import HttpStorageConfig + from nemo_relay.observability import OtlpConfig + from nemo_relay.observability import S3StorageConfig def current_virtualenv() -> Path | None: @@ -40,7 +39,7 @@ def virtualenv_subprocess_env() -> dict[str, str]: env = os.environ.copy() virtualenv = current_virtualenv() if virtualenv is None: - return env + return env scripts = virtualenv / ("Scripts" if os.name == "nt" else "bin") path = env.get("PATH") @@ -109,13 +108,25 @@ def models_payload(payload: dict[str, Any]) -> dict[str, Any]: return fabric_config(payload).get("models") or payload.get("models") or {} -def telemetry_payload(payload: dict[str, Any]) -> dict[str, Any]: - telemetry = fabric_config(payload).get("telemetry") or payload.get("telemetry") or {} - return telemetry if isinstance(telemetry, dict) else {} +def telemetry_plan(payload: dict[str, Any]) -> dict[str, Any]: + plan = payload.get("telemetry_plan") or {} + return plan if isinstance(plan, dict) else {} + + +def telemetry_providers(payload: dict[str, Any]) -> list[str]: + providers = telemetry_plan(payload).get("providers") + if isinstance(providers, list): + return [str(provider) for provider in providers if str(provider)] + return [] -def telemetry_provider(payload: dict[str, Any]) -> str: - return str(telemetry_payload(payload).get("provider") or "relay") +def relay_enabled(payload: dict[str, Any]) -> bool: + return telemetry_plan(payload).get("relay_enabled") is True + + +def native_telemetry_config(payload: dict[str, Any]) -> dict[str, Any]: + config = telemetry_plan(payload).get("native_config") or {} + return config if isinstance(config, dict) else {} def capability_plan(payload: dict[str, Any]) -> dict[str, Any]: @@ -135,6 +146,7 @@ def normalize_list(value: Any) -> list[str]: def dump_yaml(value: dict[str, Any]) -> str: try: import yaml + return yaml.safe_dump(value, sort_keys=False) except ImportError: return json.dumps(value, indent=2, sort_keys=False) + "\n" @@ -201,11 +213,9 @@ def normalize_relay_output_dirs(plugin_config: dict[str, Any], payload: dict[str def relay_api_plugin_config(plugin_config: dict[str, Any]) -> plugin.PluginConfig: from nemo_relay import plugin - from nemo_relay.observability import ( - ComponentSpec, - ConfigPolicy, - ObservabilityConfig, - ) + from nemo_relay.observability import ComponentSpec + from nemo_relay.observability import ConfigPolicy + from nemo_relay.observability import ObservabilityConfig components: list[Any] = [] for component in plugin_config.get("components", []): @@ -264,7 +274,8 @@ def relay_api_plugin_config(plugin_config: dict[str, Any]) -> plugin.PluginConfi def _relay_api_atof_config(value: Any) -> AtofConfig | None: if not isinstance(value, dict): return None - from nemo_relay.observability import AtofConfig, AtofEndpointConfig + from nemo_relay.observability import AtofConfig + from nemo_relay.observability import AtofEndpointConfig endpoint_configs = value.get("endpoints") endpoints = None @@ -296,11 +307,7 @@ def _relay_api_atif_config(value: Any) -> AtifConfig | None: storage_configs = value.get("storage") storage = None if isinstance(storage_configs, list): - storage = [ - _relay_api_storage_config(item) - for item in storage_configs - if isinstance(item, dict) - ] + storage = [_relay_api_storage_config(item) for item in storage_configs if isinstance(item, dict)] return AtifConfig( enabled=bool(value.get("enabled", False)), agent_name=value.get("agent_name", "NeMo Relay"), @@ -409,7 +416,6 @@ 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) diff --git a/adapters/deepagents/README.md b/adapters/deepagents/README.md index 353b5bf7b..c05ad54ea 100644 --- a/adapters/deepagents/README.md +++ b/adapters/deepagents/README.md @@ -134,11 +134,11 @@ async with await client.start_runtime(config, base_dir=BASE_DIR) as runtime: ## Telemetry -- **Relay** (`telemetry.provider: relay`): the agent is wrapped with +- **Relay** (`telemetry.providers.relay`): the agent is wrapped with `nemo_relay.integrations.deepagents.add_nemo_relay_integration`, emitting ATOF/ATIF artifacts referenced in the `ArtifactManifest`. OTel/OpenInference export is available through the relay plugin config (see the `relay-otel` and `relay-openinference` profiles). -- **Native** (`telemetry.provider: native`): the `telemetry.config` +- **Native** (`telemetry.providers.native.config`): the provider config OpenTelemetry/OpenInference exporter is applied and spans export directly to the configured collector, without writing ATOF/ATIF relay artifacts. diff --git a/adapters/deepagents/pyproject.toml b/adapters/deepagents/pyproject.toml index ec99cb8f4..911df9629 100644 --- a/adapters/deepagents/pyproject.toml +++ b/adapters/deepagents/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ # Requires 3.x: the 2.x line is incompatible with the langgraph core deepagents # pulls (AsyncSqliteSaver fails with 'Connection has no attribute is_alive'). "langgraph-checkpoint-sqlite>=3.0,<4.0", - "nemo-relay~=0.4", + "nemo-relay~=0.5.0", ] [tool.setuptools.packages.find] diff --git a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py index b48dd4012..bdc191977 100644 --- a/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py +++ b/adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py @@ -19,7 +19,8 @@ import shlex import uuid from pathlib import Path -from typing import Any, NamedTuple +from typing import Any +from typing import NamedTuple import nemo_fabric_adapters.common.utils as common_utils @@ -451,8 +452,9 @@ def _gate_subagent(subagent: Any, allowed: set[str]) -> Any: async def run_deepagents(payload: dict[str, Any]) -> dict[str, Any]: settings = common_utils.settings_payload(payload) request = payload.get("request") or {} - telemetry_provider = common_utils.telemetry_provider(payload) - relay_enabled = os.environ.get("FABRIC_RELAY_ENABLED", "").strip().lower() == "true" + telemetry_providers = common_utils.telemetry_providers(payload) + relay_enabled = common_utils.relay_enabled(payload) + telemetry_provider = "relay" if relay_enabled else ("native" if "native" in telemetry_providers else "") user_message = request.get("input") or "" if not isinstance(user_message, str): @@ -594,9 +596,10 @@ def resolve_observability( """Resolve the nemo_relay observability plugin config for relay or native telemetry. Relay telemetry loads its plugin config from ``FABRIC_RELAY_CONFIG_PATH`` and - collects ATOF/ATIF artifacts. Native telemetry reads ``telemetry.config`` from - the payload (e.g. an OpenTelemetry/OpenInference exporter) and exports spans - directly to the configured collector without writing relay artifacts. + collects ATOF/ATIF artifacts. Native telemetry reads + ``telemetry_plan.native_config`` from the payload (e.g. an + OpenTelemetry/OpenInference exporter) and exports spans directly to the + configured collector without writing relay artifacts. """ if relay_enabled and telemetry_provider == "relay": @@ -605,10 +608,9 @@ def resolve_observability( "deepagents.observability/nemo_relay", True, ) - telemetry = common_utils.telemetry_payload(payload) - if telemetry.get("enabled") and telemetry_provider == "native": - native_config = telemetry.get("config") or {} - if isinstance(native_config, dict) and native_config.get("components"): + if telemetry_provider == "native": + native_config = common_utils.native_telemetry_config(payload) + if native_config.get("components"): return Observability(native_config, "deepagents.observability/native", False) return None diff --git a/adapters/deepagents/uv.lock b/adapters/deepagents/uv.lock index 71c6fb71f..551390347 100644 --- a/adapters/deepagents/uv.lock +++ b/adapters/deepagents/uv.lock @@ -861,7 +861,7 @@ requires-dist = [ { name = "langgraph", specifier = ">=1.2,<2.0" }, { name = "langgraph-checkpoint-sqlite", specifier = ">=3.0,<4.0" }, { name = "nemo-fabric-adapters-common", editable = "../common" }, - { name = "nemo-relay", specifier = "~=0.4" }, + { name = "nemo-relay", specifier = "~=0.5.0" }, ] [[package]] diff --git a/adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py b/adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py index ee447f864..b3fabd951 100755 --- a/adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py +++ b/adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py @@ -63,7 +63,7 @@ def run_hermes_cli(payload: dict[str, Any]) -> dict[str, Any]: hermes_config_path, hermes_config = hermes_common.write_hermes_config( payload, hermes_home, - relay_enabled=relay_plugin_config is not None, + relay_enabled=common_utils.relay_enabled(payload), ) if settings.get("prepare_runtime_state", True): diff --git a/adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py b/adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py index 72f291231..81ceb5aa5 100755 --- a/adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py +++ b/adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py @@ -81,7 +81,7 @@ async def run_hermes_sdk(payload: dict[str, Any]) -> dict[str, Any]: os.environ["HERMES_SESSION_SOURCE"] = "fabric" os.environ.setdefault("TERMINAL_ENV", settings.get("terminal_backend", "local")) os.environ.setdefault("TERMINAL_TIMEOUT", str(settings.get("terminal_timeout", 60))) - relay_enabled = os.environ.get("FABRIC_RELAY_ENABLED", "").strip().lower() == "true" + relay_enabled = common_utils.relay_enabled(payload) relay_plugin_config = None if relay_enabled: @@ -118,9 +118,7 @@ async def run_hermes_sdk(payload: dict[str, Any]) -> dict[str, Any]: from nemo_relay import plugin async with plugin.plugin(relay_api_config): - (result, enabled_toolsets, relay_artifacts, adapter_stdout) = _invoke_hermes( - **hermes_kwargs - ) + (result, enabled_toolsets, relay_artifacts, adapter_stdout) = _invoke_hermes(**hermes_kwargs) else: (result, enabled_toolsets, relay_artifacts, adapter_stdout) = _invoke_hermes(**hermes_kwargs) @@ -169,7 +167,8 @@ def _invoke_hermes( relay_plugin_config: dict[str, Any] | None, ) -> tuple[dict[str, Any], list[str] | None, list[dict[str, str]], str]: from hermes_cli.config import load_config - from hermes_cli.plugins import discover_plugins, invoke_hook + from hermes_cli.plugins import discover_plugins + from hermes_cli.plugins import invoke_hook from hermes_state import SessionDB from run_agent import AIAgent diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 1c9efb9ee..6b41f825e 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -62,6 +62,9 @@ pub struct FabricConfig { /// Telemetry configuration. #[serde(default, skip_serializing_if = "Option::is_none")] pub telemetry: Option, + /// First-class NeMo Relay integration configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub relay: Option, /// Optional profile discovery config. #[serde(default, skip_serializing_if = "ProfileRegistryConfig::is_empty")] pub profiles: ProfileRegistryConfig, @@ -408,6 +411,8 @@ struct ProfileConfigSchema { mcp: Option>, /// Partial telemetry overlay. telemetry: Option>, + /// Partial Relay integration overlay. + relay: Option>, /// Additive config overlays. #[serde(flatten)] extensions: BTreeMap, @@ -604,29 +609,399 @@ pub enum McpExposure { /// Telemetry configuration. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct TelemetryConfig { - /// Whether telemetry is enabled for this run. + /// Telemetry providers enabled for this run. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub providers: BTreeMap, + /// Additive telemetry fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// Provider-specific telemetry configuration. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct TelemetryProviderConfig { + /// Provider-specific pass-through config. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub config: Option, + /// Additive provider fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// NeMo Relay integration configuration. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayConfig { + /// Optional project name for Relay backends. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub project: Option, + /// Optional Relay output directory. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output_dir: Option, + /// Relay observability component configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub observability: Option, + /// Additional Relay plugin components. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub components: Vec, + /// Relay plugin validation policy. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub policy: Option, + /// Additive Relay fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// Generic NeMo Relay plugin component configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayComponentConfig { + /// Registered Relay plugin kind. + pub kind: String, + /// Whether this Relay component should be activated. + #[serde(default = "default_enabled")] + pub enabled: bool, + /// Component-local Relay plugin config. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub config: BTreeMap, + /// Additive component fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// NeMo Relay observability component configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayObservabilityConfig { + /// Relay observability config version. + #[serde(default = "default_relay_config_version")] + pub version: u32, + /// ATOF export configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub atof: Option, + /// ATIF export configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub atif: Option, + /// OpenTelemetry export configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub opentelemetry: Option, + /// OpenInference export configuration. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub openinference: Option, + /// Relay config validation policy. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub policy: Option, + /// Additive observability fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +impl Default for RelayObservabilityConfig { + fn default() -> Self { + Self { + version: default_relay_config_version(), + atof: None, + atif: None, + opentelemetry: None, + openinference: None, + policy: None, + extensions: BTreeMap::new(), + } + } +} + +/// Relay ATOF export configuration. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayAtofConfig { + /// Whether ATOF export is enabled. #[serde(default)] pub enabled: bool, - /// Telemetry provider responsible for runtime integration. + /// Directory used for ATOF files. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output_directory: Option, + /// ATOF file name. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub filename: Option, + /// File write mode. #[serde(default)] - pub provider: TelemetryProvider, - /// Optional project name for telemetry backends. + pub mode: RelayAtofMode, + /// Optional remote ATOF endpoints. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub endpoints: Vec, + /// Additive ATOF fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// Relay ATOF endpoint configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayAtofEndpointConfig { + /// Endpoint URL. + pub url: String, + /// Endpoint transport. + #[serde(default)] + pub transport: RelayAtofEndpointTransport, + /// Endpoint headers. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub headers: BTreeMap, + /// Request timeout in milliseconds. + #[serde(default = "default_relay_timeout_millis")] + pub timeout_millis: u64, + /// Field-name handling policy. + #[serde(default)] + pub field_name_policy: RelayAtofEndpointFieldNamePolicy, + /// Additive endpoint fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +/// Relay ATIF export configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayAtifConfig { + /// Whether ATIF export is enabled. + #[serde(default)] + pub enabled: bool, + /// Agent name written into ATIF. + #[serde(default = "default_relay_atif_agent_name")] + pub agent_name: String, + /// Agent version written into ATIF. #[serde(default, skip_serializing_if = "Option::is_none")] - pub project: Option, - /// Optional telemetry output directory. + pub agent_version: Option, + /// Model name written into ATIF. + #[serde(default = "default_relay_atif_model_name")] + pub model_name: String, + /// Tool definitions written into ATIF. #[serde(default, skip_serializing_if = "Option::is_none")] - pub output_dir: Option, - /// Pass-through telemetry backend config. + pub tool_definitions: Option>, + /// Extra ATIF metadata. #[serde(default, skip_serializing_if = "Option::is_none")] - pub config: Option, - /// Additive telemetry fields. + pub extra: Option, + /// Directory used for ATIF files. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub output_directory: Option, + /// ATIF file name template. + #[serde(default = "default_relay_atif_filename_template")] + pub filename_template: String, + /// Optional ATIF remote storage. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub storage: Option>, + /// Additive ATIF fields. #[serde(default, flatten)] pub extensions: BTreeMap, } -/// Telemetry runtime provider. +impl Default for RelayAtifConfig { + fn default() -> Self { + Self { + enabled: false, + agent_name: default_relay_atif_agent_name(), + agent_version: None, + model_name: default_relay_atif_model_name(), + tool_definitions: None, + extra: None, + output_directory: None, + filename_template: default_relay_atif_filename_template(), + storage: None, + extensions: BTreeMap::new(), + } + } +} + +/// Relay ATIF remote storage configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum RelayAtifStorageConfig { + /// Upload ATIF artifacts to HTTP storage. + Http { + /// HTTP storage endpoint. + #[serde(default)] + endpoint: String, + /// Static HTTP headers. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + headers: BTreeMap, + /// Environment-variable-backed HTTP headers. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + header_env: BTreeMap, + /// Request timeout in milliseconds. + #[serde(default = "default_relay_timeout_millis")] + timeout_millis: u64, + /// Additive HTTP storage fields. + #[serde(default, flatten)] + extensions: BTreeMap, + }, + /// Upload ATIF artifacts to S3-compatible storage. + S3 { + /// S3 bucket name. + #[serde(default)] + bucket: String, + /// Optional S3 object key prefix. + #[serde(default, skip_serializing_if = "Option::is_none")] + key_prefix: Option, + /// AWS access key id. + #[serde(default, skip_serializing_if = "Option::is_none")] + access_key_id: Option, + /// Environment variable containing the AWS secret access key. + #[serde(default, skip_serializing_if = "Option::is_none")] + secret_access_key_var: Option, + /// Environment variable containing the AWS session token. + #[serde(default, skip_serializing_if = "Option::is_none")] + session_token_var: Option, + /// AWS region. + #[serde(default, skip_serializing_if = "Option::is_none")] + region: Option, + /// S3-compatible endpoint URL. + #[serde(default, skip_serializing_if = "Option::is_none")] + endpoint_url: Option, + /// Allow HTTP endpoints for S3-compatible storage. + #[serde(default, skip_serializing_if = "Option::is_none")] + allow_http: Option, + /// Additive S3 storage fields. + #[serde(default, flatten)] + extensions: BTreeMap, + }, +} + +/// Relay OpenTelemetry/OpenInference export configuration. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayOtlpConfig { + /// Whether OTLP export is enabled. + #[serde(default)] + pub enabled: bool, + /// OTLP transport. + #[serde(default)] + pub transport: RelayOtlpTransport, + /// OTLP endpoint. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub endpoint: Option, + /// OTLP headers. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub headers: BTreeMap, + /// OTLP resource attributes. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub resource_attributes: BTreeMap, + /// OTLP service name. + #[serde(default = "default_relay_service_name")] + pub service_name: String, + /// OTLP service namespace. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub service_namespace: Option, + /// OTLP service version. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub service_version: Option, + /// OTLP instrumentation scope. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub instrumentation_scope: Option, + /// Request timeout in milliseconds. + #[serde(default = "default_relay_timeout_millis")] + pub timeout_millis: u64, + /// Additive OTLP fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + +impl Default for RelayOtlpConfig { + fn default() -> Self { + Self { + enabled: false, + transport: RelayOtlpTransport::default(), + endpoint: None, + headers: BTreeMap::new(), + resource_attributes: BTreeMap::new(), + service_name: default_relay_service_name(), + service_namespace: None, + service_version: None, + instrumentation_scope: None, + timeout_millis: default_relay_timeout_millis(), + extensions: BTreeMap::new(), + } + } +} + +/// Relay validation policy. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct RelayConfigPolicy { + /// Policy for unknown components. + #[serde(default)] + pub unknown_component: RelayUnsupportedBehavior, + /// Policy for unknown fields. + #[serde(default)] + pub unknown_field: RelayUnsupportedBehavior, + /// Policy for unsupported values. + #[serde(default = "default_relay_unsupported_value_behavior")] + pub unsupported_value: RelayUnsupportedBehavior, +} + +impl Default for RelayConfigPolicy { + fn default() -> Self { + Self { + unknown_component: RelayUnsupportedBehavior::default(), + unknown_field: RelayUnsupportedBehavior::default(), + unsupported_value: default_relay_unsupported_value_behavior(), + } + } +} + +/// Relay unsupported/unknown config handling. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RelayUnsupportedBehavior { + /// Ignore the unsupported or unknown value. + Ignore, + /// Warn on the unsupported or unknown value. + #[default] + Warn, + /// Error on the unsupported or unknown value. + Error, +} + +/// Relay ATOF file mode. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RelayAtofMode { + /// Append to an existing ATOF file. + #[default] + Append, + /// Overwrite an existing ATOF file. + Overwrite, +} + +/// Relay ATOF endpoint transport. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RelayAtofEndpointTransport { + /// HTTP POST transport. + #[default] + HttpPost, + /// WebSocket transport. + Websocket, + /// NDJSON transport. + Ndjson, +} + +/// Relay ATOF endpoint field-name policy. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] +pub enum RelayAtofEndpointFieldNamePolicy { + /// Preserve field names. + #[default] + Preserve, + /// Replace dots in field names. + ReplaceDots, +} + +/// Relay OTLP transport. +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] +pub enum RelayOtlpTransport { + /// OTLP HTTP binary transport. + #[default] + HttpBinary, + /// OTLP gRPC transport. + Grpc, +} + +/// Telemetry runtime provider. +#[derive( + Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema, +)] +#[serde(rename_all = "snake_case")] pub enum TelemetryProvider { /// Use NeMo Relay for telemetry integration. #[default] @@ -645,6 +1020,38 @@ impl TelemetryProvider { } } +fn default_relay_config_version() -> u32 { + 1 +} + +fn default_enabled() -> bool { + true +} + +fn default_relay_atif_agent_name() -> String { + "NeMo Relay".to_string() +} + +fn default_relay_atif_model_name() -> String { + "unknown".to_string() +} + +fn default_relay_atif_filename_template() -> String { + "nemo-relay-atif-{session_id}.json".to_string() +} + +fn default_relay_timeout_millis() -> u64 { + 3000 +} + +fn default_relay_service_name() -> String { + "nemo-relay".to_string() +} + +fn default_relay_unsupported_value_behavior() -> RelayUnsupportedBehavior { + RelayUnsupportedBehavior::Error +} + /// Load a Fabric document from an agent directory or single agent config. pub fn load_fabric_document(path: impl AsRef) -> Result { let path = path.as_ref(); @@ -1271,18 +1678,71 @@ fn resolve_telemetry_plan( adapter_descriptor: Option<&AdapterDescriptor>, ) -> Option { let telemetry = config.telemetry.as_ref()?; + if telemetry.providers.is_empty() { + return None; + } + let relay_provider = telemetry.providers.get(&TelemetryProvider::Relay); + let native_provider = telemetry.providers.get(&TelemetryProvider::Native); + let relay = config.relay.as_ref(); + let relay_enabled = relay_provider.is_some(); + let providers = [TelemetryProvider::Relay, TelemetryProvider::Native] + .into_iter() + .filter(|provider| telemetry.providers.contains_key(provider)) + .collect(); Some(TelemetryPlan { - provider: telemetry.provider, - relay_enabled: telemetry.enabled && telemetry.provider == TelemetryProvider::Relay, - relay_project: telemetry.project.clone(), - relay_output_dir: telemetry.output_dir.clone(), - relay_config: telemetry.config.clone(), + providers, + relay_enabled, + relay_project: relay_enabled + .then(|| relay.and_then(|relay| relay.project.clone())) + .flatten(), + relay_output_dir: relay_enabled + .then(|| relay.and_then(|relay| relay.output_dir.clone())) + .flatten(), + relay_config: relay_enabled + .then(|| resolve_relay_plugin_config(relay)) + .flatten(), + native_config: native_provider.and_then(|provider| provider.config.clone()), adapter_outputs: adapter_descriptor .map(|descriptor| descriptor.telemetry.supports.clone()) .unwrap_or_default(), }) } +fn resolve_relay_plugin_config(relay: Option<&RelayConfig>) -> Option { + let relay = relay?; + let mut components = Vec::new(); + + if let Some(observability) = relay.observability.as_ref() { + components.push(serde_json::json!({ + "kind": "observability", + "enabled": true, + "config": serde_json::to_value(observability).unwrap_or(Value::Null), + })); + } + + for component in &relay.components { + components.push(serde_json::to_value(component).unwrap_or(Value::Null)); + } + + if !components.is_empty() || relay.policy.is_some() { + let mut plugin_config = serde_json::json!({ + "version": 1, + "components": components, + }); + if let Some(policy) = relay.policy.as_ref() + && let Some(object) = plugin_config.as_object_mut() + { + object.insert( + "policy".to_string(), + serde_json::to_value(policy).unwrap_or(Value::Null), + ); + } + return Some(plugin_config); + } + + None +} + fn resolve_path(root: &Path, path: &Path) -> PathBuf { let resolved = if path.is_absolute() { path.to_path_buf() @@ -1488,8 +1948,8 @@ pub struct McpServerPlan { /// Resolved telemetry plan. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct TelemetryPlan { - /// Telemetry provider selected for this run. - pub provider: TelemetryProvider, + /// Telemetry providers selected for this run. + pub providers: Vec, /// Whether Relay is enabled. pub relay_enabled: bool, /// Relay project, when configured. @@ -1501,6 +1961,9 @@ pub struct TelemetryPlan { /// Relay pass-through config. #[serde(default, skip_serializing_if = "Option::is_none")] pub relay_config: Option, + /// Native telemetry pass-through config. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub native_config: Option, /// Telemetry outputs declared by the selected adapter descriptor. #[serde(default, skip_serializing_if = "Vec::is_empty")] pub adapter_outputs: Vec, @@ -1602,7 +2065,7 @@ runtime: } #[test] - fn telemetry_provider_defaults_to_relay() { + fn relay_telemetry_provider_is_enabled_by_presence() { let config: FabricConfig = serde_yaml::from_str( r#" schema_version: fabric.agent/v1alpha1 @@ -1612,27 +2075,23 @@ harness: adapter_id: nvidia.fabric.hermes.sdk runtime: telemetry: - enabled: true - config: - exporter: test + providers: + relay: {} "#, ) - .expect("config with default telemetry provider"); + .expect("config with relay telemetry provider"); let telemetry = config.telemetry.as_ref().expect("telemetry config"); let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); - assert_eq!(telemetry.provider, TelemetryProvider::Relay); - assert_eq!(plan.provider, TelemetryProvider::Relay); + assert!(telemetry.providers.contains_key(&TelemetryProvider::Relay)); + assert_eq!(plan.providers, vec![TelemetryProvider::Relay]); assert!(plan.relay_enabled); - assert_eq!( - plan.relay_config, - Some(serde_json::json!({"exporter": "test"})) - ); + assert_eq!(plan.relay_config, None); } #[test] - fn native_telemetry_provider_skips_relay_and_preserves_config() { + fn native_telemetry_provider_skips_relay_config() { let config: FabricConfig = serde_yaml::from_str( r#" schema_version: fabric.agent/v1alpha1 @@ -1642,30 +2101,113 @@ harness: adapter_id: nvidia.fabric.hermes.sdk runtime: telemetry: - enabled: true - provider: native - config: - exporter: test + providers: + native: + config: + exporter: test +relay: + project: relay-project + output_dir: ./relay-output "#, ) .expect("config with native telemetry provider"); let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); - assert_eq!(plan.provider, TelemetryProvider::Native); + assert_eq!(plan.providers, vec![TelemetryProvider::Native]); assert!(!plan.relay_enabled); + assert_eq!(plan.relay_project, None); + assert_eq!(plan.relay_output_dir, None); + assert_eq!(plan.relay_config, None); assert_eq!( - plan.relay_config, + plan.native_config, Some(serde_json::json!({"exporter": "test"})) ); } #[test] - fn telemetry_provider_rejects_unknown_values() { + fn relay_telemetry_config_generates_relay_plugin_config() { + let config: FabricConfig = serde_yaml::from_str( + r#" +schema_version: fabric.agent/v1alpha1 +metadata: + name: demo +harness: + adapter_id: nvidia.fabric.hermes.sdk +runtime: +telemetry: + providers: + relay: {} +relay: + project: typed-project + output_dir: ./typed-relay + observability: + atof: + enabled: true + output_directory: ./typed-relay + filename: events.atof.jsonl + mode: overwrite + atif: + enabled: true + output_directory: ./typed-relay + filename_template: trajectory-{session_id}.atif.json + agent_name: code-review-agent + opentelemetry: + enabled: true + endpoint: http://localhost:4318/v1/traces + components: + - kind: switchyard + enabled: true + config: + route: canary + policy: + unknown_component: error +"#, + ) + .expect("config with typed relay telemetry"); + + let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); + let relay_config = plan.relay_config.expect("relay plugin config"); + + assert_eq!(plan.relay_project.as_deref(), Some("typed-project")); + assert_eq!(plan.relay_output_dir, Some(PathBuf::from("./typed-relay"))); + assert_eq!(relay_config["version"], serde_json::json!(1)); + assert_eq!( + relay_config["components"][0]["kind"], + serde_json::json!("observability") + ); + assert_eq!( + relay_config["components"][0]["config"]["atof"]["mode"], + serde_json::json!("overwrite") + ); + assert_eq!( + relay_config["components"][0]["config"]["atif"]["agent_name"], + serde_json::json!("code-review-agent") + ); + assert_eq!( + relay_config["components"][0]["config"]["opentelemetry"]["endpoint"], + serde_json::json!("http://localhost:4318/v1/traces") + ); + assert_eq!( + relay_config["components"][1], + serde_json::json!({ + "kind": "switchyard", + "enabled": true, + "config": {"route": "canary"} + }) + ); + assert_eq!( + relay_config["policy"]["unknown_component"], + serde_json::json!("error") + ); + } + + #[test] + fn telemetry_provider_rejects_unknown_provider_keys() { let result = serde_yaml::from_str::( r#" -enabled: true -provider: unsupported +providers: + unsupported: {} "#, ); @@ -1758,8 +2300,8 @@ provider: unsupported plan.config .telemetry .as_ref() - .map(|telemetry| telemetry.enabled), - Some(false) + .map(|telemetry| telemetry.providers.is_empty()), + None ); } @@ -2023,7 +2565,7 @@ mcp: plan.telemetry_plan .as_ref() .map(|telemetry| telemetry.relay_enabled), - Some(false) + None ); let profiles = vec!["env_local".to_string(), "env_opensandbox".to_string()]; @@ -2073,7 +2615,7 @@ mcp: plan.telemetry_plan .as_ref() .map(|telemetry| telemetry.relay_enabled), - Some(false) + None ); assert!( plan.capability_plan diff --git a/crates/fabric-core/src/lib.rs b/crates/fabric-core/src/lib.rs index d24ab5dcb..f7d2be22a 100644 --- a/crates/fabric-core/src/lib.rs +++ b/crates/fabric-core/src/lib.rs @@ -16,10 +16,11 @@ pub use config::{ FabricDocument, HarnessConfig, McpConfig, McpExposure, McpServerPlan, MetadataConfig, ModelConfig, ProfileConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, - TelemetryProvider, load_adapter_descriptor, load_fabric_document, resolve_effective_config, - resolve_effective_config_from_config, resolve_effective_config_with_profiles, resolve_run_plan, - resolve_run_plan_from_config, resolve_run_plan_from_effective_config, - resolve_run_plan_with_profiles, validate_agent_directory, + TelemetryProvider, TelemetryProviderConfig, load_adapter_descriptor, load_fabric_document, + resolve_effective_config, resolve_effective_config_from_config, + resolve_effective_config_with_profiles, resolve_run_plan, resolve_run_plan_from_config, + resolve_run_plan_from_effective_config, resolve_run_plan_with_profiles, + validate_agent_directory, }; 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 b51744879..c0d2cb2f2 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -1280,8 +1280,14 @@ fn runtime_telemetry_context( let telemetry = plan.telemetry_plan.as_ref()?; let mut metadata = BTreeMap::new(); metadata.insert( - "telemetry_provider".to_string(), - Value::String(telemetry.provider.as_str().to_string()), + "telemetry_providers".to_string(), + Value::Array( + telemetry + .providers + .iter() + .map(|provider| Value::String(provider.as_str().to_string())) + .collect(), + ), ); if let Some(project) = &telemetry.relay_project { metadata.insert("relay_project".to_string(), Value::String(project.clone())); @@ -1795,8 +1801,14 @@ fn telemetry_ref( let telemetry = plan.telemetry_plan.as_ref()?; let mut metadata = BTreeMap::new(); metadata.insert( - "telemetry_provider".to_string(), - Value::String(telemetry.provider.as_str().to_string()), + "telemetry_providers".to_string(), + Value::Array( + telemetry + .providers + .iter() + .map(|provider| Value::String(provider.as_str().to_string())) + .collect(), + ), ); if let Some(project) = &telemetry.relay_project { metadata.insert("relay_project".to_string(), Value::String(project.clone())); @@ -2080,10 +2092,13 @@ environment: config.push_str( r#" telemetry: - enabled: true - provider: native - config: - exporter: test + providers: + native: + config: + exporter: test +relay: + project: relay-project + output_dir: ./relay-output "#, ); fs::write(&config_path, config).expect("write native telemetry config"); @@ -2126,16 +2141,29 @@ telemetry: .iter() .any(|artifact| artifact.name == "relay_config") ); - assert_eq!(payload["telemetry_plan"]["provider"], "native"); + assert_eq!( + payload["telemetry_plan"]["providers"], + serde_json::json!(["native"]) + ); assert_eq!(payload["telemetry_plan"]["relay_enabled"], false); assert_eq!( - payload["effective_config"]["config"]["telemetry"]["config"], + payload["effective_config"]["config"]["telemetry"]["providers"]["native"]["config"], serde_json::json!({"exporter": "test"}) ); assert!(payload["runtime_context"]["telemetry"].get("env").is_none()); assert_eq!( - payload["runtime_context"]["telemetry"]["metadata"]["telemetry_provider"], - "native" + payload["runtime_context"]["telemetry"]["metadata"]["telemetry_providers"], + serde_json::json!(["native"]) + ); + assert!( + payload["runtime_context"]["telemetry"]["metadata"] + .get("relay_project") + .is_none() + ); + assert!( + payload["runtime_context"]["telemetry"]["metadata"] + .get("relay_output_dir") + .is_none() ); let _ = fs::remove_dir_all(root); diff --git a/docs/reference/api/python-library-reference/index.md b/docs/reference/api/python-library-reference/index.md index e0d90341d..c2b93b66c 100644 --- a/docs/reference/api/python-library-reference/index.md +++ b/docs/reference/api/python-library-reference/index.md @@ -31,10 +31,21 @@ SPDX-License-Identifier: Apache-2.0 */} - [`models.MetadataConfig`](./nemo_fabric.models.md#class-metadataconfig): Human-readable agent identity. - [`models.ModelConfig`](./nemo_fabric.models.md#class-modelconfig): Model alias configuration. - [`models.ProfileRegistryConfig`](./nemo_fabric.models.md#class-profileregistryconfig): Profile discovery config for portable file-backed agent packages. +- [`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.RelayAtofEndpointConfig`](./nemo_fabric.models.md#class-relayatofendpointconfig): NeMo Relay ATOF remote endpoint configuration. +- [`models.RelayComponentConfig`](./nemo_fabric.models.md#class-relaycomponentconfig): Generic NeMo Relay plugin component configuration. +- [`models.RelayConfig`](./nemo_fabric.models.md#class-relayconfig): First-class NeMo Relay integration configuration. +- [`models.RelayConfigPolicy`](./nemo_fabric.models.md#class-relayconfigpolicy): NeMo Relay config validation policy. +- [`models.RelayHttpStorageConfig`](./nemo_fabric.models.md#class-relayhttpstorageconfig): NeMo Relay ATIF HTTP storage configuration. +- [`models.RelayObservabilityConfig`](./nemo_fabric.models.md#class-relayobservabilityconfig): NeMo Relay observability component configuration. +- [`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 Fabric invocation request. - [`models.RuntimeConfig`](./nemo_fabric.models.md#class-runtimeconfig): Runtime input/output 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. - [`types.AdapterInfo`](./nemo_fabric.types.md#class-adapterinfo): Resolved adapter identity attached to a run plan. - [`types.ArtifactManifest`](./nemo_fabric.types.md#class-artifactmanifest): Normalized collection of artifacts produced by a run. - [`types.ArtifactRef`](./nemo_fabric.types.md#class-artifactref): Reference to one artifact produced by a run. 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 763c606f3..39ca5d2ab 100644 --- a/docs/reference/api/python-library-reference/nemo_fabric.models.md +++ b/docs/reference/api/python-library-reference/nemo_fabric.models.md @@ -608,8 +608,608 @@ Return a detached JSON-compatible mapping for Rust/core calls. --- -## class `TelemetryConfig` -Telemetry configuration. +## class `RelayConfigPolicy` +NeMo Relay config validation policy. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayAtofEndpointConfig` +NeMo Relay ATOF remote endpoint configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayAtofConfig` +NeMo Relay ATOF export configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayS3StorageConfig` +NeMo Relay ATIF S3 storage configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayHttpStorageConfig` +NeMo Relay ATIF HTTP storage configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayAtifConfig` +NeMo Relay ATIF export configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayOtlpConfig` +NeMo Relay OTLP export configuration for OpenTelemetry/OpenInference. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayObservabilityConfig` +NeMo Relay observability component configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayComponentConfig` +Generic NeMo Relay plugin component configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `RelayConfig` +First-class NeMo Relay integration configuration. + + +--- + +### 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 +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` + +```python +to_mapping() → dict[str, Any] +``` + +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `TelemetryProviderConfig` +Provider-specific telemetry configuration. --- @@ -645,13 +1245,62 @@ Returns the set of fields that have been explicitly set on this model instance. --- -### method `disable` +### classmethod `from_mapping` + +```python +from_mapping(value: 'Mapping[str, Any]') → Self +``` + +Validate a mapping using this Pydantic model. + +--- + + +### method `to_mapping` ```python -disable() → Self +to_mapping() → dict[str, Any] ``` -Disable telemetry. +Return a detached JSON-compatible mapping for Rust/core calls. + + +--- + + +## class `TelemetryConfig` +Telemetry configuration. + + +--- + +### 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. + + --- @@ -659,7 +1308,7 @@ Disable telemetry. ### method `enable_native` ```python -enable_native() → Self +enable_native(config: 'Mapping[str, Any] | None' = None) → Self ``` Let the selected adapter handle telemetry natively. @@ -670,11 +1319,7 @@ Let the selected adapter handle telemetry natively. ### method `enable_relay` ```python -enable_relay( - project: 'str | None' = None, - output_dir: 'str | Path | None' = None, - config: 'Mapping[str, Any] | None' = None -) → Self +enable_relay() → Self ``` Enable NeMo Relay telemetry for subsequently started runtimes. @@ -693,6 +1338,17 @@ Validate a mapping using this Pydantic model. --- +### method `remove_provider` + +```python +remove_provider(provider: "Literal['relay', 'native']") → Self +``` + +Remove a configured telemetry provider. + +--- + + ### method `to_mapping` ```python @@ -836,7 +1492,9 @@ Add a skill path and return this config. enable_relay( project: 'str | None' = None, output_dir: 'str | Path | None' = None, - config: 'Mapping[str, Any] | None' = None + observability: 'RelayObservabilityConfig | Mapping[str, Any] | None' = None, + components: 'Sequence[RelayComponentConfig | Mapping[str, Any]] | None' = None, + policy: 'RelayConfigPolicy | Mapping[str, Any] | None' = None ) → Self ``` diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx index 0e1c60848..41eea0cce 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Kind" sidebar-title: "CapabilityKind" description: "Capability kind." -position: 30 +position: 39 --- {/* 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/fabric-core/config/enum-capabilitytarget.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx index dbbc0409c..76f2c1609 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Target" sidebar-title: "CapabilityTarget" description: "Capability routing target." -position: 31 +position: 40 --- {/* 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/fabric-core/config/enum-relayatifstorageconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx new file mode 100644 index 000000000..f8991a59c --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx @@ -0,0 +1,162 @@ +--- +title: "Enum Relay Atif Storage Config" +sidebar-title: "RelayAtifStorageConfig" +description: "Relay ATIF remote storage configuration." +position: 45 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p 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}"}} />
+ +Relay ATIF remote storage configuration. + +## Variants + +### `Http` + +
+ +Upload ATIF artifacts to HTTP storage. + +#### Fields + +### `endpoint: String` + +HTTP storage endpoint. + +### `headers: BTreeMap` + +Static HTTP headers. + +### `header_env: BTreeMap` + +Environment-variable-backed HTTP headers. + +### `timeout_millis: u64` + +Request timeout in milliseconds. + +### `extensions: BTreeMap` + +Additive HTTP storage fields. + +### `S3` + +
+ +Upload ATIF artifacts to S3-compatible storage. + +#### Fields + +### `bucket: String` + +S3 bucket name. + +### `key_prefix: Option` + +Optional S3 object key prefix. + +### `access_key_id: Option` + +AWS access key id. + +### `secret_access_key_var: Option` + +Environment variable containing the AWS secret access key. + +### `session_token_var: Option` + +Environment variable containing the AWS session token. + +### `region: Option` + +AWS region. + +### `endpoint_url: Option` + +S3-compatible endpoint URL. + +### `allow_http: Option` + +Allow HTTP endpoints for S3-compatible storage. + +### `extensions: BTreeMap` + +Additive S3 storage fields. + +## Trait Implementations + +### `impl Clone for RelayAtifStorageConfig` + +
Clone for RelayAtifStorageConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtifStorageConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtifStorageConfig` + +
Debug for RelayAtifStorageConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtifStorageConfig` + +
Deserialize<'de> for RelayAtifStorageConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtifStorageConfig` + +
RelayAtifStorageConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtifStorageConfig` + +
PartialEq for RelayAtifStorageConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtifStorageConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtifStorageConfig` + +
Serialize for RelayAtifStorageConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayAtifStorageConfig` + +
StructuralPartialEq for RelayAtifStorageConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx new file mode 100644 index 000000000..fbfd3c737 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx @@ -0,0 +1,123 @@ +--- +title: "Enum Relay Atof Endpoint Field Name Policy" +sidebar-title: "RelayAtofEndpointFieldNamePolicy" +description: "Relay ATOF endpoint field-name policy." +position: 46 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p fabric-core`. + +```rust +pub enum RelayAtofEndpointFieldNamePolicy { + Preserve, + ReplaceDots, +} +``` + +Relay ATOF endpoint field-name policy. + +## Variants + +### `Preserve` + +
+ +Preserve field names. + +### `ReplaceDots` + +
+ +Replace dots in field names. + +## Trait Implementations + +### `impl Clone for RelayAtofEndpointFieldNamePolicy` + +
Clone for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtofEndpointFieldNamePolicy` + +
Debug for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayAtofEndpointFieldNamePolicy` + +
Default for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `default` + +
default() -> RelayAtofEndpointFieldNamePolicy"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtofEndpointFieldNamePolicy` + +
Deserialize<'de> for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtofEndpointFieldNamePolicy` + +
RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtofEndpointFieldNamePolicy` + +
PartialEq for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtofEndpointFieldNamePolicy) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtofEndpointFieldNamePolicy` + +
Serialize for RelayAtofEndpointFieldNamePolicy"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for RelayAtofEndpointFieldNamePolicy` + +
Copy for RelayAtofEndpointFieldNamePolicy"}} />
+ +### `impl Eq for RelayAtofEndpointFieldNamePolicy` + +
Eq for RelayAtofEndpointFieldNamePolicy"}} />
+ +### `impl StructuralPartialEq for RelayAtofEndpointFieldNamePolicy` + +
StructuralPartialEq for RelayAtofEndpointFieldNamePolicy"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx new file mode 100644 index 000000000..12ca939ed --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx @@ -0,0 +1,130 @@ +--- +title: "Enum Relay Atof Endpoint Transport" +sidebar-title: "RelayAtofEndpointTransport" +description: "Relay ATOF endpoint transport." +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 fabric-core`. + +```rust +pub enum RelayAtofEndpointTransport { + HttpPost, + Websocket, + Ndjson, +} +``` + +Relay ATOF endpoint transport. + +## Variants + +### `HttpPost` + +
+ +HTTP POST transport. + +### `Websocket` + +
+ +WebSocket transport. + +### `Ndjson` + +
+ +NDJSON transport. + +## Trait Implementations + +### `impl Clone for RelayAtofEndpointTransport` + +
Clone for RelayAtofEndpointTransport"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtofEndpointTransport"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtofEndpointTransport` + +
Debug for RelayAtofEndpointTransport"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayAtofEndpointTransport` + +
Default for RelayAtofEndpointTransport"}} />
+ +#### `default` + +
default() -> RelayAtofEndpointTransport"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtofEndpointTransport` + +
Deserialize<'de> for RelayAtofEndpointTransport"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtofEndpointTransport` + +
RelayAtofEndpointTransport"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtofEndpointTransport` + +
PartialEq for RelayAtofEndpointTransport"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtofEndpointTransport) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtofEndpointTransport` + +
Serialize for RelayAtofEndpointTransport"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for RelayAtofEndpointTransport` + +
Copy for RelayAtofEndpointTransport"}} />
+ +### `impl Eq for RelayAtofEndpointTransport` + +
Eq for RelayAtofEndpointTransport"}} />
+ +### `impl StructuralPartialEq for RelayAtofEndpointTransport` + +
StructuralPartialEq for RelayAtofEndpointTransport"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx new file mode 100644 index 000000000..bb11f456b --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx @@ -0,0 +1,123 @@ +--- +title: "Enum Relay Atof Mode" +sidebar-title: "RelayAtofMode" +description: "Relay ATOF file mode." +position: 48 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p fabric-core`. + +```rust +pub enum RelayAtofMode { + Append, + Overwrite, +} +``` + +Relay ATOF file mode. + +## Variants + +### `Append` + +
+ +Append to an existing ATOF file. + +### `Overwrite` + +
+ +Overwrite an existing ATOF file. + +## Trait Implementations + +### `impl Clone for RelayAtofMode` + +
Clone for RelayAtofMode"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtofMode"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtofMode` + +
Debug for RelayAtofMode"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayAtofMode` + +
Default for RelayAtofMode"}} />
+ +#### `default` + +
default() -> RelayAtofMode"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtofMode` + +
Deserialize<'de> for RelayAtofMode"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtofMode` + +
RelayAtofMode"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtofMode` + +
PartialEq for RelayAtofMode"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtofMode) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtofMode` + +
Serialize for RelayAtofMode"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for RelayAtofMode` + +
Copy for RelayAtofMode"}} />
+ +### `impl Eq for RelayAtofMode` + +
Eq for RelayAtofMode"}} />
+ +### `impl StructuralPartialEq for RelayAtofMode` + +
StructuralPartialEq for RelayAtofMode"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx new file mode 100644 index 000000000..cf581a45f --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx @@ -0,0 +1,123 @@ +--- +title: "Enum Relay Otlp Transport" +sidebar-title: "RelayOtlpTransport" +description: "Relay OTLP transport." +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 fabric-core`. + +```rust +pub enum RelayOtlpTransport { + HttpBinary, + Grpc, +} +``` + +Relay OTLP transport. + +## Variants + +### `HttpBinary` + +
+ +OTLP HTTP binary transport. + +### `Grpc` + +
+ +OTLP gRPC transport. + +## Trait Implementations + +### `impl Clone for RelayOtlpTransport` + +
Clone for RelayOtlpTransport"}} />
+ +#### `clone` + +
clone(&self) -> RelayOtlpTransport"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayOtlpTransport` + +
Debug for RelayOtlpTransport"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayOtlpTransport` + +
Default for RelayOtlpTransport"}} />
+ +#### `default` + +
default() -> RelayOtlpTransport"}} />
+ +### `impl<'de> Deserialize<'de> for RelayOtlpTransport` + +
Deserialize<'de> for RelayOtlpTransport"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayOtlpTransport` + +
RelayOtlpTransport"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayOtlpTransport` + +
PartialEq for RelayOtlpTransport"}} />
+ +#### `eq` + +
eq(&self, other: &RelayOtlpTransport) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayOtlpTransport` + +
Serialize for RelayOtlpTransport"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for RelayOtlpTransport` + +
Copy for RelayOtlpTransport"}} />
+ +### `impl Eq for RelayOtlpTransport` + +
Eq for RelayOtlpTransport"}} />
+ +### `impl StructuralPartialEq for RelayOtlpTransport` + +
StructuralPartialEq for RelayOtlpTransport"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx new file mode 100644 index 000000000..6b3d3dc09 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx @@ -0,0 +1,130 @@ +--- +title: "Enum Relay Unsupported Behavior" +sidebar-title: "RelayUnsupportedBehavior" +description: "Relay unsupported/unknown config handling." +position: 50 +--- +{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +SPDX-License-Identifier: Apache-2.0 */} + +Generated from `cargo doc --no-deps -p fabric-core`. + +```rust +pub enum RelayUnsupportedBehavior { + Ignore, + Warn, + Error, +} +``` + +Relay unsupported/unknown config handling. + +## Variants + +### `Ignore` + +
+ +Ignore the unsupported or unknown value. + +### `Warn` + +
+ +Warn on the unsupported or unknown value. + +### `Error` + +
+ +Error on the unsupported or unknown value. + +## Trait Implementations + +### `impl Clone for RelayUnsupportedBehavior` + +
Clone for RelayUnsupportedBehavior"}} />
+ +#### `clone` + +
clone(&self) -> RelayUnsupportedBehavior"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayUnsupportedBehavior` + +
Debug for RelayUnsupportedBehavior"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayUnsupportedBehavior` + +
Default for RelayUnsupportedBehavior"}} />
+ +#### `default` + +
default() -> RelayUnsupportedBehavior"}} />
+ +### `impl<'de> Deserialize<'de> for RelayUnsupportedBehavior` + +
Deserialize<'de> for RelayUnsupportedBehavior"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayUnsupportedBehavior` + +
RelayUnsupportedBehavior"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayUnsupportedBehavior` + +
PartialEq for RelayUnsupportedBehavior"}} />
+ +#### `eq` + +
eq(&self, other: &RelayUnsupportedBehavior) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayUnsupportedBehavior` + +
Serialize for RelayUnsupportedBehavior"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl Copy for RelayUnsupportedBehavior` + +
Copy for RelayUnsupportedBehavior"}} />
+ +### `impl Eq for RelayUnsupportedBehavior` + +
Eq for RelayUnsupportedBehavior"}} />
+ +### `impl StructuralPartialEq for RelayUnsupportedBehavior` + +
StructuralPartialEq for RelayUnsupportedBehavior"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx index 030ece7d7..27483ad33 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx @@ -102,6 +102,26 @@ Return the stable configuration value for this provider.
bool"}} />
+### `impl Ord for TelemetryProvider` + +
Ord for TelemetryProvider"}} />
+ +#### `cmp` + +
cmp(&self, other: &TelemetryProvider) -> 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 TelemetryProvider`
PartialEq for TelemetryProvider"}} />
@@ -114,6 +134,30 @@ Return the stable configuration value for this provider.
ne(&self, other: &Rhs) -> bool"}} />
+### `impl PartialOrd for TelemetryProvider` + +
PartialOrd for TelemetryProvider"}} />
+ +#### `partial_cmp` + +
partial_cmp(&self, other: &TelemetryProvider) -> 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 TelemetryProvider`
Serialize for TelemetryProvider"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx index 382c5841a..2e49d35ff 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx @@ -2,7 +2,7 @@ title: "Function load_adapter_descriptor" sidebar-title: "load_adapter_descriptor" description: "Load an adapter descriptor from JSON package metadata." -position: 33 +position: 34 --- {/* 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/fabric-core/config/fn-load-fabric-document.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx index ad45abcd7..bd93f4f8d 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx @@ -2,7 +2,7 @@ title: "Function load_fabric_document" sidebar-title: "load_fabric_document" description: "Load a Fabric document from an agent directory or single agent config." -position: 34 +position: 35 --- {/* 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/fabric-core/config/fn-resolve-effective-config-from-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx index fb8c731ea..9457fe190 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config_from_config" sidebar-title: "resolve_effective_config_from_config" description: "Resolve typed config/profile overlays into merged effective config." -position: 36 +position: 37 --- {/* 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/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx index 2b2124314..1fed0c9e5 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config_with_profiles" sidebar-title: "resolve_effective_config_with_profiles" description: "Resolve an agent directory or single agent config with ordered profiles into merged effective config." -position: 37 +position: 38 --- {/* 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/fabric-core/config/fn-resolve-effective-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx index 558cba6c9..f6338a28e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config" sidebar-title: "resolve_effective_config" description: "Resolve an agent directory or single agent config into merged effective config." -position: 35 +position: 36 --- {/* 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/fabric-core/config/fn-resolve-run-plan-from-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx index eec4f2acf..72ccd8c13 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_config" sidebar-title: "resolve_run_plan_from_config" description: "Resolve a typed Fabric config and typed profile overlays into a runnable plan." -position: 39 +position: 40 --- {/* 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/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx index efaa9534c..124795331 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_effective_config" sidebar-title: "resolve_run_plan_from_effective_config" description: "Resolve execution planning metadata from merged effective config." -position: 40 +position: 41 --- {/* 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/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx index ab313bb1e..7f5cdb105 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_with_profiles" sidebar-title: "resolve_run_plan_with_profiles" description: "Resolve an agent directory or single agent config with ordered profile application." -position: 41 +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/fabric-core/config/fn-resolve-run-plan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx index 90951e465..af7b915b8 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan" sidebar-title: "resolve_run_plan" description: "Resolve an agent directory or single agent config into a runnable plan." -position: 38 +position: 39 --- {/* 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/fabric-core/config/fn-validate-agent-directory.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx index d4740ba0d..00347b51c 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx @@ -2,7 +2,7 @@ title: "Function validate_agent_directory" sidebar-title: "validate_agent_directory" description: "Validate an agent directory or config, including discoverable profile YAMLs." -position: 42 +position: 43 --- {/* 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/fabric-core/config/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx index b175aebcb..b87c9ddbd 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx @@ -2,7 +2,7 @@ title: "Module config" sidebar-title: "config" description: "Fabric config models and loading helpers." -position: 74 +position: 75 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -32,6 +32,14 @@ Fabric config models and loading helpers. - [ModelConfig](/reference/api/rust-library-reference/fabric-core/config/struct-modelconfig): Model configuration. - [ProfileConfig](/reference/api/rust-library-reference/fabric-core/config/struct-profileconfig): Profile config applied on top of a Fabric config. - [ProfileRegistryConfig](/reference/api/rust-library-reference/fabric-core/config/struct-profileregistryconfig): Profile discovery config for curated package profiles. +- [RelayAtifConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig): Relay ATIF export configuration. +- [RelayAtofConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig): Relay ATOF export configuration. +- [RelayAtofEndpointConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig): Relay ATOF endpoint configuration. +- [RelayComponentConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig): Generic NeMo Relay plugin component configuration. +- [RelayConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig): NeMo Relay integration configuration. +- [RelayConfigPolicy](/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy): Relay validation policy. +- [RelayObservabilityConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig): NeMo Relay observability component configuration. +- [RelayOtlpConfig](/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig): Relay OpenTelemetry/OpenInference export configuration. - [ResolveContext](/reference/api/rust-library-reference/fabric-core/config/struct-resolvecontext): Source context used when resolving an in-memory Fabric config. - [ResolvedAdapterDescriptor](/reference/api/rust-library-reference/fabric-core/config/struct-resolvedadapterdescriptor): Adapter descriptor selected for a run plan. - [RunPlan](/reference/api/rust-library-reference/fabric-core/config/struct-runplan): Resolved Fabric run plan. @@ -40,6 +48,7 @@ Fabric config models and loading helpers. - [SkillConfig](/reference/api/rust-library-reference/fabric-core/config/struct-skillconfig): Skill capability configuration. - [TelemetryConfig](/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig): Telemetry configuration. - [TelemetryPlan](/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan): Resolved telemetry plan. +- [TelemetryProviderConfig](/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig): Provider-specific telemetry configuration. ## Enums @@ -51,6 +60,12 @@ Fabric config models and loading helpers. - [EnvironmentOwnership](/reference/api/rust-library-reference/fabric-core/config/enum-environmentownership): Whether Fabric owns the underlying environment resource. - [FabricDocument](/reference/api/rust-library-reference/fabric-core/config/enum-fabricdocument): A loaded Fabric document with resolved source path and agent root. - [McpExposure](/reference/api/rust-library-reference/fabric-core/config/enum-mcpexposure): MCP exposure strategy. +- [RelayAtifStorageConfig](/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig): Relay ATIF remote storage configuration. +- [RelayAtofEndpointFieldNamePolicy](/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy): Relay ATOF endpoint field-name policy. +- [RelayAtofEndpointTransport](/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport): Relay ATOF endpoint transport. +- [RelayAtofMode](/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode): Relay ATOF file mode. +- [RelayOtlpTransport](/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport): Relay OTLP transport. +- [RelayUnsupportedBehavior](/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior): Relay unsupported/unknown config handling. - [ResolutionStrategy](/reference/api/rust-library-reference/fabric-core/config/enum-resolutionstrategy): Adapter install or availability strategy. - [TelemetryProvider](/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider): Telemetry runtime provider. diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx index 9a95ee871..5c0afd460 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx @@ -9,7 +9,7 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p 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<Value>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub profiles: ProfileRegistryConfig,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
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<Value>,\n    pub skills: Option<SkillConfig>,\n    pub mcp: Option<McpConfig>,\n    pub telemetry: Option<TelemetryConfig>,\n    pub relay: Option<RelayConfig>,\n    pub profiles: ProfileRegistryConfig,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Versioned Fabric agent config. @@ -55,6 +55,10 @@ MCP capability configuration. Telemetry configuration. +### `relay: Option` + +First-class NeMo Relay integration configuration. + ### `profiles: ProfileRegistryConfig` Optional profile discovery config. diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx new file mode 100644 index 000000000..c0d8e8dd4 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx @@ -0,0 +1,138 @@ +--- +title: "Struct Relay Atif Config" +sidebar-title: "RelayAtifConfig" +description: "Relay ATIF export configuration." +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 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}"}} />
+ +Relay ATIF export configuration. + +## Fields + +### `enabled: bool` + +Whether ATIF export is enabled. + +### `agent_name: String` + +Agent name written into ATIF. + +### `agent_version: Option` + +Agent version written into ATIF. + +### `model_name: String` + +Model name written into ATIF. + +### `tool_definitions: Option>` + +Tool definitions written into ATIF. + +### `extra: Option` + +Extra ATIF metadata. + +### `output_directory: Option` + +Directory used for ATIF files. + +### `filename_template: String` + +ATIF file name template. + +### `storage: Option>` + +Optional ATIF remote storage. + +### `extensions: BTreeMap` + +Additive ATIF fields. + +## Trait Implementations + +### `impl Clone for RelayAtifConfig` + +
Clone for RelayAtifConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtifConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtifConfig` + +
Debug for RelayAtifConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayAtifConfig` + +
Default for RelayAtifConfig"}} />
+ +#### `default` + +
default() -> Self"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtifConfig` + +
Deserialize<'de> for RelayAtifConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtifConfig` + +
RelayAtifConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtifConfig` + +
PartialEq for RelayAtifConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtifConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtifConfig` + +
Serialize for RelayAtifConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayAtifConfig` + +
StructuralPartialEq for RelayAtifConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx new file mode 100644 index 000000000..4486582dc --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx @@ -0,0 +1,122 @@ +--- +title: "Struct Relay Atof Config" +sidebar-title: "RelayAtofConfig" +description: "Relay ATOF export configuration." +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 fabric-core`. + +
bool,\n    pub output_directory: Option<PathBuf>,\n    pub filename: Option<String>,\n    pub mode: RelayAtofMode,\n    pub endpoints: Vec<RelayAtofEndpointConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Relay ATOF export configuration. + +## Fields + +### `enabled: bool` + +Whether ATOF export is enabled. + +### `output_directory: Option` + +Directory used for ATOF files. + +### `filename: Option` + +ATOF file name. + +### `mode: RelayAtofMode` + +File write mode. + +### `endpoints: Vec` + +Optional remote ATOF endpoints. + +### `extensions: BTreeMap` + +Additive ATOF fields. + +## Trait Implementations + +### `impl Clone for RelayAtofConfig` + +
Clone for RelayAtofConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtofConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtofConfig` + +
Debug for RelayAtofConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayAtofConfig` + +
Default for RelayAtofConfig"}} />
+ +#### `default` + +
default() -> Self"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtofConfig` + +
Deserialize<'de> for RelayAtofConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtofConfig` + +
RelayAtofConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtofConfig` + +
PartialEq for RelayAtofConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtofConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtofConfig` + +
Serialize for RelayAtofConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayAtofConfig` + +
StructuralPartialEq for RelayAtofConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx new file mode 100644 index 000000000..77471a8c7 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx @@ -0,0 +1,114 @@ +--- +title: "Struct Relay Atof Endpoint Config" +sidebar-title: "RelayAtofEndpointConfig" +description: "Relay ATOF endpoint configuration." +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 fabric-core`. + +
String,\n    pub transport: RelayAtofEndpointTransport,\n    pub headers: BTreeMap<String, String>,\n    pub timeout_millis: u64,\n    pub field_name_policy: RelayAtofEndpointFieldNamePolicy,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Relay ATOF endpoint configuration. + +## Fields + +### `url: String` + +Endpoint URL. + +### `transport: RelayAtofEndpointTransport` + +Endpoint transport. + +### `headers: BTreeMap` + +Endpoint headers. + +### `timeout_millis: u64` + +Request timeout in milliseconds. + +### `field_name_policy: RelayAtofEndpointFieldNamePolicy` + +Field-name handling policy. + +### `extensions: BTreeMap` + +Additive endpoint fields. + +## Trait Implementations + +### `impl Clone for RelayAtofEndpointConfig` + +
Clone for RelayAtofEndpointConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayAtofEndpointConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayAtofEndpointConfig` + +
Debug for RelayAtofEndpointConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl<'de> Deserialize<'de> for RelayAtofEndpointConfig` + +
Deserialize<'de> for RelayAtofEndpointConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayAtofEndpointConfig` + +
RelayAtofEndpointConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayAtofEndpointConfig` + +
PartialEq for RelayAtofEndpointConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayAtofEndpointConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayAtofEndpointConfig` + +
Serialize for RelayAtofEndpointConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayAtofEndpointConfig` + +
StructuralPartialEq for RelayAtofEndpointConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx new file mode 100644 index 000000000..d07cd4327 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx @@ -0,0 +1,106 @@ +--- +title: "Struct Relay Component Config" +sidebar-title: "RelayComponentConfig" +description: "Generic NeMo Relay plugin component configuration." +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 fabric-core`. + +
String,\n    pub enabled: bool,\n    pub config: BTreeMap<String, Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Generic NeMo Relay plugin component configuration. + +## Fields + +### `kind: String` + +Registered Relay plugin kind. + +### `enabled: bool` + +Whether this Relay component should be activated. + +### `config: BTreeMap` + +Component-local Relay plugin config. + +### `extensions: BTreeMap` + +Additive component fields. + +## Trait Implementations + +### `impl Clone for RelayComponentConfig` + +
Clone for RelayComponentConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayComponentConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayComponentConfig` + +
Debug for RelayComponentConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl<'de> Deserialize<'de> for RelayComponentConfig` + +
Deserialize<'de> for RelayComponentConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayComponentConfig` + +
RelayComponentConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayComponentConfig` + +
PartialEq for RelayComponentConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayComponentConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayComponentConfig` + +
Serialize for RelayComponentConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayComponentConfig` + +
StructuralPartialEq for RelayComponentConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx new file mode 100644 index 000000000..87cdeb362 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx @@ -0,0 +1,122 @@ +--- +title: "Struct Relay Config" +sidebar-title: "RelayConfig" +description: "NeMo Relay integration configuration." +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 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}"}} />
+ +NeMo Relay integration configuration. + +## Fields + +### `project: Option` + +Optional project name for Relay backends. + +### `output_dir: Option` + +Optional Relay output directory. + +### `observability: Option` + +Relay observability component configuration. + +### `components: Vec` + +Additional Relay plugin components. + +### `policy: Option` + +Relay plugin validation policy. + +### `extensions: BTreeMap` + +Additive Relay fields. + +## Trait Implementations + +### `impl Clone for RelayConfig` + +
Clone for RelayConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayConfig` + +
Debug for RelayConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayConfig` + +
Default for RelayConfig"}} />
+ +#### `default` + +
default() -> RelayConfig"}} />
+ +### `impl<'de> Deserialize<'de> for RelayConfig` + +
Deserialize<'de> for RelayConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayConfig` + +
RelayConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayConfig` + +
PartialEq for RelayConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayConfig` + +
Serialize for RelayConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayConfig` + +
StructuralPartialEq for RelayConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx new file mode 100644 index 000000000..a6e3f4e86 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx @@ -0,0 +1,110 @@ +--- +title: "Struct Relay Config Policy" +sidebar-title: "RelayConfigPolicy" +description: "Relay validation policy." +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 fabric-core`. + +
RelayUnsupportedBehavior,\n    pub unknown_field: RelayUnsupportedBehavior,\n    pub unsupported_value: RelayUnsupportedBehavior,\n}"}} />
+ +Relay validation policy. + +## Fields + +### `unknown_component: RelayUnsupportedBehavior` + +Policy for unknown components. + +### `unknown_field: RelayUnsupportedBehavior` + +Policy for unknown fields. + +### `unsupported_value: RelayUnsupportedBehavior` + +Policy for unsupported values. + +## Trait Implementations + +### `impl Clone for RelayConfigPolicy` + +
Clone for RelayConfigPolicy"}} />
+ +#### `clone` + +
clone(&self) -> RelayConfigPolicy"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayConfigPolicy` + +
Debug for RelayConfigPolicy"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayConfigPolicy` + +
Default for RelayConfigPolicy"}} />
+ +#### `default` + +
default() -> Self"}} />
+ +### `impl<'de> Deserialize<'de> for RelayConfigPolicy` + +
Deserialize<'de> for RelayConfigPolicy"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayConfigPolicy` + +
RelayConfigPolicy"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayConfigPolicy` + +
PartialEq for RelayConfigPolicy"}} />
+ +#### `eq` + +
eq(&self, other: &RelayConfigPolicy) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayConfigPolicy` + +
Serialize for RelayConfigPolicy"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayConfigPolicy` + +
StructuralPartialEq for RelayConfigPolicy"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx new file mode 100644 index 000000000..e929a6e76 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx @@ -0,0 +1,126 @@ +--- +title: "Struct Relay Observability Config" +sidebar-title: "RelayObservabilityConfig" +description: "NeMo Relay observability component configuration." +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 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}"}} />
+ +NeMo Relay observability component configuration. + +## Fields + +### `version: u32` + +Relay observability config version. + +### `atof: Option` + +ATOF export configuration. + +### `atif: Option` + +ATIF export configuration. + +### `opentelemetry: Option` + +OpenTelemetry export configuration. + +### `openinference: Option` + +OpenInference export configuration. + +### `policy: Option` + +Relay config validation policy. + +### `extensions: BTreeMap` + +Additive observability fields. + +## Trait Implementations + +### `impl Clone for RelayObservabilityConfig` + +
Clone for RelayObservabilityConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayObservabilityConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayObservabilityConfig` + +
Debug for RelayObservabilityConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayObservabilityConfig` + +
Default for RelayObservabilityConfig"}} />
+ +#### `default` + +
default() -> Self"}} />
+ +### `impl<'de> Deserialize<'de> for RelayObservabilityConfig` + +
Deserialize<'de> for RelayObservabilityConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayObservabilityConfig` + +
RelayObservabilityConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayObservabilityConfig` + +
PartialEq for RelayObservabilityConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayObservabilityConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayObservabilityConfig` + +
Serialize for RelayObservabilityConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayObservabilityConfig` + +
StructuralPartialEq for RelayObservabilityConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx new file mode 100644 index 000000000..71aaa9860 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx @@ -0,0 +1,142 @@ +--- +title: "Struct Relay Otlp Config" +sidebar-title: "RelayOtlpConfig" +description: "Relay OpenTelemetry/OpenInference export configuration." +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 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}"}} />
+ +Relay OpenTelemetry/OpenInference export configuration. + +## Fields + +### `enabled: bool` + +Whether OTLP export is enabled. + +### `transport: RelayOtlpTransport` + +OTLP transport. + +### `endpoint: Option` + +OTLP endpoint. + +### `headers: BTreeMap` + +OTLP headers. + +### `resource_attributes: BTreeMap` + +OTLP resource attributes. + +### `service_name: String` + +OTLP service name. + +### `service_namespace: Option` + +OTLP service namespace. + +### `service_version: Option` + +OTLP service version. + +### `instrumentation_scope: Option` + +OTLP instrumentation scope. + +### `timeout_millis: u64` + +Request timeout in milliseconds. + +### `extensions: BTreeMap` + +Additive OTLP fields. + +## Trait Implementations + +### `impl Clone for RelayOtlpConfig` + +
Clone for RelayOtlpConfig"}} />
+ +#### `clone` + +
clone(&self) -> RelayOtlpConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for RelayOtlpConfig` + +
Debug for RelayOtlpConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for RelayOtlpConfig` + +
Default for RelayOtlpConfig"}} />
+ +#### `default` + +
default() -> Self"}} />
+ +### `impl<'de> Deserialize<'de> for RelayOtlpConfig` + +
Deserialize<'de> for RelayOtlpConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for RelayOtlpConfig` + +
RelayOtlpConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for RelayOtlpConfig` + +
PartialEq for RelayOtlpConfig"}} />
+ +#### `eq` + +
eq(&self, other: &RelayOtlpConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for RelayOtlpConfig` + +
Serialize for RelayOtlpConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for RelayOtlpConfig` + +
StructuralPartialEq for RelayOtlpConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx index 0857073e1..7d201c34e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx @@ -9,31 +9,15 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p fabric-core`. -
bool,\n    pub provider: TelemetryProvider,\n    pub project: Option<String>,\n    pub output_dir: Option<PathBuf>,\n    pub config: Option<Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
BTreeMap<TelemetryProvider, TelemetryProviderConfig>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Telemetry configuration. ## Fields -### `enabled: bool` +### `providers: BTreeMap` -Whether telemetry is enabled for this run. - -### `provider: TelemetryProvider` - -Telemetry provider responsible for runtime integration. - -### `project: Option` - -Optional project name for telemetry backends. - -### `output_dir: Option` - -Optional telemetry output directory. - -### `config: Option` - -Pass-through telemetry backend config. +Telemetry providers enabled for this run. ### `extensions: BTreeMap` diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx index ed5349d70..97ca74a41 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx @@ -9,15 +9,15 @@ SPDX-License-Identifier: Apache-2.0 */} Generated from `cargo doc --no-deps -p fabric-core`. -
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 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. ## Fields -### `provider: TelemetryProvider` +### `providers: Vec` -Telemetry provider selected for this run. +Telemetry providers selected for this run. ### `relay_enabled: bool` @@ -35,6 +35,10 @@ Relay output directory, when configured. Relay pass-through config. +### `native_config: Option` + +Native telemetry pass-through config. + ### `adapter_outputs: Vec` Telemetry outputs declared by the selected adapter descriptor. diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx new file mode 100644 index 000000000..6081568d0 --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx @@ -0,0 +1,106 @@ +--- +title: "Struct Telemetry Provider Config" +sidebar-title: "TelemetryProviderConfig" +description: "Provider-specific telemetry configuration." +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 fabric-core`. + +
Option<Value>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Provider-specific telemetry configuration. + +## Fields + +### `config: Option` + +Provider-specific pass-through config. + +### `extensions: BTreeMap` + +Additive provider fields. + +## Trait Implementations + +### `impl Clone for TelemetryProviderConfig` + +
Clone for TelemetryProviderConfig"}} />
+ +#### `clone` + +
clone(&self) -> TelemetryProviderConfig"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for TelemetryProviderConfig` + +
Debug for TelemetryProviderConfig"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for TelemetryProviderConfig` + +
Default for TelemetryProviderConfig"}} />
+ +#### `default` + +
default() -> TelemetryProviderConfig"}} />
+ +### `impl<'de> Deserialize<'de> for TelemetryProviderConfig` + +
Deserialize<'de> for TelemetryProviderConfig"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for TelemetryProviderConfig` + +
TelemetryProviderConfig"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for TelemetryProviderConfig` + +
PartialEq for TelemetryProviderConfig"}} />
+ +#### `eq` + +
eq(&self, other: &TelemetryProviderConfig) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for TelemetryProviderConfig` + +
Serialize for TelemetryProviderConfig"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for TelemetryProviderConfig` + +
StructuralPartialEq for TelemetryProviderConfig"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx index bd2ac54c7..5ef34bb27 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx @@ -2,7 +2,7 @@ title: "Enum Doctor Status" sidebar-title: "DoctorStatus" description: "Diagnostic status." -position: 45 +position: 46 --- {/* 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/fabric-core/doctor/fn-doctor-plan.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/fn-doctor-plan.mdx index 1e00456ba..733398689 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/fn-doctor-plan.mdx +++ b/docs/reference/api/rust-library-reference/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: 46 +position: 47 --- {/* 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/fabric-core/doctor/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx index 522bac0f8..e79ae26a8 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx @@ -2,7 +2,7 @@ title: "Module doctor" sidebar-title: "doctor" description: "Plan diagnostics for Fabric." -position: 75 +position: 76 --- {/* 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/fabric-core/doctor/struct-doctorcheck.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx index 5e97be328..8e94ca38d 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Check" sidebar-title: "DoctorCheck" description: "Diagnostic check result." -position: 43 +position: 44 --- {/* 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/fabric-core/doctor/struct-doctorreport.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx index 372c5faa0..4d6c153d2 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Report" sidebar-title: "DoctorReport" description: "Diagnostic report for a resolved run plan." -position: 44 +position: 45 --- {/* 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/fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx index f9a7c83ac..d53f38e54 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx @@ -2,7 +2,7 @@ title: "Enum Fabric Error" sidebar-title: "FabricError" description: "Errors raised by Fabric config loading and validation." -position: 47 +position: 48 --- {/* 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/fabric-core/error/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx index 151328eb8..dfb44afde 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx @@ -2,7 +2,7 @@ title: "Module error" sidebar-title: "error" description: "Error types for Fabric core." -position: 76 +position: 77 --- {/* 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/fabric-core/error/type-result.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx index 508833955..6c600f286 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx @@ -2,7 +2,7 @@ title: "Type Alias Result" sidebar-title: "Result" description: "Core Fabric result type." -position: 48 +position: 49 --- {/* 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/fabric-core/fn-version.mdx b/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx index e98d68d11..dd81b4a7c 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx @@ -2,7 +2,7 @@ title: "Function version" sidebar-title: "version" description: "Returns the crate version compiled into this build." -position: 79 +position: 80 --- {/* 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/fabric-core/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/index.mdx index cf59388d5..6acfef10b 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/index.mdx @@ -45,6 +45,7 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::TelemetryConfig;` - `pub use config::TelemetryPlan;` - `pub use config::TelemetryProvider;` +- `pub use config::TelemetryProviderConfig;` - `pub use config::load_adapter_descriptor;` - `pub use config::load_fabric_document;` - `pub use config::resolve_effective_config;` diff --git a/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx index a35d051a6..ab02904ca 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx @@ -2,7 +2,7 @@ title: "Module runtime" sidebar-title: "runtime" description: "Runtime invocation helpers." -position: 77 +position: 78 --- {/* 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/fabric-core/schema/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx index 38faab82d..6a09ead74 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx @@ -2,7 +2,7 @@ title: "Module schema" sidebar-title: "schema" description: "JSON Schema generation for the public Fabric contract." -position: 78 +position: 79 --- {/* 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 ad9fe4395..80f0cc8af 100644 --- a/docs/sdk/python.mdx +++ b/docs/sdk/python.mdx @@ -149,6 +149,8 @@ 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. +Telemetry is enabled by adding entries to `telemetry.providers`; Relay-specific +settings live in the top-level `relay` block. For evaluation or deployment variations, use ordinary Python functions and copies of the typed config. Supply the final config to Fabric when the variation @@ -171,6 +173,23 @@ def review_agent_config(base, *, github_mcp: bool, relay: bool): variant = review_agent_config(config, github_mcp=True, relay=True) ``` +Relay observability is represented directly in the SDK config's top-level +`relay` block, and additional Relay plugin components can be supplied +generically when their component package is available in the runtime +environment: + +```python +from nemo_fabric import RelayComponentConfig + +relay_config = config.model_copy(deep=True) +relay_config.enable_relay( + output_dir="./artifacts/relay", + components=[ + RelayComponentConfig(kind="switchyard", config={"route": "canary"}), + ], +) +``` + The repository's [code-review example](https://github.com/NVIDIA/NeMo-Fabric/tree/main/examples/code_review_agent) uses this pattern for complete Hermes SDK, Hermes CLI, Codex CLI, Deep Agents, diff --git a/examples/code_review_agent/config.py b/examples/code_review_agent/config.py index dc03fdd7a..dfddf783c 100644 --- a/examples/code_review_agent/config.py +++ b/examples/code_review_agent/config.py @@ -7,15 +7,17 @@ from pathlib import Path -from nemo_fabric import ( - EnvironmentConfig, - FabricConfig, - HarnessConfig, - MetadataConfig, - ModelConfig, - RuntimeConfig, - TelemetryConfig, -) +from nemo_fabric import EnvironmentConfig +from nemo_fabric import FabricConfig +from nemo_fabric import HarnessConfig +from nemo_fabric import MetadataConfig +from nemo_fabric import ModelConfig +from nemo_fabric import RelayAtifConfig +from nemo_fabric import RelayAtofConfig +from nemo_fabric import RelayObservabilityConfig +from nemo_fabric import RelayOtlpConfig +from nemo_fabric import RuntimeConfig +from nemo_fabric import TelemetryConfig BASE_DIR = Path(__file__).resolve().parent WORKSPACE = "./repos/my-service" @@ -53,7 +55,7 @@ def base_config() -> FabricConfig: workspace=WORKSPACE, artifacts="./artifacts/local", ), - telemetry=TelemetryConfig(enabled=False), + telemetry=TelemetryConfig(), ) config.add_skill_path(SKILL_PATH) config.add_mcp_server( @@ -139,9 +141,7 @@ def codex_cli_config() -> FabricConfig: "config_overrides": {"model_reasoning_effort": "high"}, }, ) - config.models = { - "default": ModelConfig(provider="openai", model="openai/gpt-5.4") - } + config.models = {"default": ModelConfig(provider="openai", model="openai/gpt-5.4")} config.runtime = RuntimeConfig( input_schema="text", output_schema="message", @@ -220,31 +220,21 @@ def with_relay(base: FabricConfig) -> FabricConfig: config = base.model_copy(deep=True) config.enable_relay( output_dir="./artifacts/relay", - config={ - "version": 1, - "components": [ - { - "kind": "observability", - "enabled": True, - "config": { - "version": 1, - "atif": { - "enabled": True, - "output_directory": "./artifacts/relay", - "filename_template": "trajectory-{session_id}.atif.json", - "agent_name": "code-review-agent", - "agent_version": "fabric-sdk-example", - }, - "atof": { - "enabled": True, - "output_directory": "./artifacts/relay", - "filename": "events.atof.jsonl", - "mode": "overwrite", - }, - }, - } - ], - }, + observability=RelayObservabilityConfig( + atif=RelayAtifConfig( + enabled=True, + output_directory="./artifacts/relay", + filename_template="trajectory-{session_id}.atif.json", + agent_name="code-review-agent", + agent_version="fabric-sdk-example", + ), + atof=RelayAtofConfig( + enabled=True, + output_directory="./artifacts/relay", + filename="events.atof.jsonl", + mode="overwrite", + ), + ), ) return config @@ -255,29 +245,19 @@ def with_relay_otel(base: FabricConfig) -> FabricConfig: config = base.model_copy(deep=True) config.enable_relay( output_dir="./artifacts/relay-otel", - config={ - "version": 1, - "components": [ - { - "kind": "observability", - "enabled": True, - "config": { - "version": 1, - "opentelemetry": { - "enabled": True, - "transport": "http_binary", - "endpoint": "http://localhost:4318/v1/traces", - "service_name": "code-review-agent", - "service_namespace": "fabric", - "service_version": "fabric-sdk-example", - "instrumentation_scope": "nemo-relay-otel", - "timeout_millis": 3000, - "resource_attributes": {"deployment.environment": "dev"}, - }, - }, - } - ], - }, + observability=RelayObservabilityConfig( + opentelemetry=RelayOtlpConfig( + enabled=True, + transport="http_binary", + endpoint="http://localhost:4318/v1/traces", + service_name="code-review-agent", + service_namespace="fabric", + service_version="fabric-sdk-example", + instrumentation_scope="nemo-relay-otel", + timeout_millis=3000, + resource_attributes={"deployment.environment": "dev"}, + ), + ), ) return config @@ -286,16 +266,24 @@ def with_relay_openinference(base: FabricConfig) -> FabricConfig: """Return a copy with Relay OpenInference export enabled.""" config = with_relay(base) - assert config.telemetry is not None and config.telemetry.config is not None - component = config.telemetry.config["components"][0]["config"] - component["openinference"] = { - "enabled": True, - "transport": "http_binary", - "endpoint": "http://localhost:6006/v1/traces", - } - config.telemetry.output_dir = "./artifacts/relay-openinference" - component["atif"]["output_directory"] = "./artifacts/relay-openinference" - component["atof"]["output_directory"] = "./artifacts/relay-openinference" + assert config.telemetry is not None + assert config.relay is not None + relay = config.relay + assert not isinstance(relay, dict) + assert relay.observability is not None + observability = relay.observability + assert not isinstance(observability, dict) + + relay.output_dir = "./artifacts/relay-openinference" + observability.openinference = RelayOtlpConfig( + enabled=True, + transport="http_binary", + endpoint="http://localhost:6006/v1/traces", + ) + if isinstance(observability.atif, RelayAtifConfig): + observability.atif.output_directory = "./artifacts/relay-openinference" + if isinstance(observability.atof, RelayAtofConfig): + observability.atof.output_directory = "./artifacts/relay-openinference" return config @@ -304,25 +292,27 @@ def with_native_otel(base: FabricConfig) -> FabricConfig: config = base.model_copy(deep=True) config.telemetry = TelemetryConfig( - enabled=True, - provider="native", - config={ - "version": 1, - "components": [ - { - "kind": "observability", - "enabled": True, - "config": { - "version": 1, - "opentelemetry": { + providers={ + "native": { + "config": { + "version": 1, + "components": [ + { + "kind": "observability", "enabled": True, - "transport": "http_binary", - "endpoint": "http://localhost:4318/v1/traces", - "resource_attributes": {"deployment.environment": "dev"}, + "config": { + "version": 1, + "opentelemetry": { + "enabled": True, + "transport": "http_binary", + "endpoint": "http://localhost:4318/v1/traces", + "resource_attributes": {"deployment.environment": "dev"}, + }, + }, }, - }, - } - ], + ], + }, + } }, ) return config diff --git a/examples/harbor/README.md b/examples/harbor/README.md index 7a03cb264..a4f58440d 100644 --- a/examples/harbor/README.md +++ b/examples/harbor/README.md @@ -59,7 +59,10 @@ task image; the [multi-harness demo](demo/README.md) shows one complete setup. ## Prepare a Fabric config -Create one complete config for the execution path. For example: +Create one complete config for the execution path. Harbor runs one Fabric +harness at a time; to switch from Hermes CLI to Hermes SDK, Codex CLI, or a +Relay-enabled variant, pass a different complete config through +`fabric_config_path`. For example: ```yaml schema_version: fabric.agent/v1alpha1 @@ -89,14 +92,43 @@ environment: workspace: /app artifacts: /logs/agent/fabric-artifacts -telemetry: - enabled: false +# Omit telemetry when no telemetry provider is required. ``` Copy the config into the task image, for example at `/opt/fabric/configs/hermes.yaml`. `fabric_config_path` always refers to the path inside the task environment, not the host checkout. +Relay integration settings live in the first-class top-level `relay` block: + +```yaml +telemetry: + providers: + relay: {} + +relay: + output_dir: /logs/agent/fabric-artifacts/relay + observability: + atif: + enabled: true + output_directory: /logs/agent/fabric-artifacts/relay + atof: + enabled: true + output_directory: /logs/agent/fabric-artifacts/relay + filename: events.atof.jsonl + mode: overwrite +``` + +Use `relay.components` for additional Relay plugin components such as Switchyard: + +```yaml +relay: + components: + - kind: switchyard + config: + route: canary +``` + ## Run the task Pass one complete Fabric config path through Harbor's agent arguments: diff --git a/examples/harbor/demo/task/environment/fabric/configs/codex.yaml b/examples/harbor/demo/task/environment/fabric/configs/codex.yaml index 527f0746b..40d44b875 100644 --- a/examples/harbor/demo/task/environment/fabric/configs/codex.yaml +++ b/examples/harbor/demo/task/environment/fabric/configs/codex.yaml @@ -32,5 +32,4 @@ environment: workspace: /app artifacts: /logs/agent/fabric-artifacts/codex -telemetry: - enabled: false +telemetry: null diff --git a/examples/harbor/demo/task/environment/fabric/configs/hermes-relay.yaml b/examples/harbor/demo/task/environment/fabric/configs/hermes-relay.yaml index 0cef67d18..919df6f3a 100644 --- a/examples/harbor/demo/task/environment/fabric/configs/hermes-relay.yaml +++ b/examples/harbor/demo/task/environment/fabric/configs/hermes-relay.yaml @@ -34,27 +34,23 @@ environment: artifacts: /logs/agent/fabric-artifacts/hermes-relay telemetry: - enabled: true - provider: relay + providers: + relay: {} + +relay: output_dir: /logs/agent/fabric-artifacts/hermes-relay/relay - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - atif: - enabled: true - output_directory: /logs/agent/fabric-artifacts/hermes-relay/relay - filename_template: trajectory-{session_id}.atif.json - agent_name: harbor-calculator-demo - atof: - enabled: true - output_directory: /logs/agent/fabric-artifacts/hermes-relay/relay - filename: events.atof.jsonl - mode: overwrite - openinference: - enabled: true - transport: http_binary - endpoint: http://host.docker.internal:6006/v1/traces + observability: + atif: + enabled: true + output_directory: /logs/agent/fabric-artifacts/hermes-relay/relay + filename_template: trajectory-{session_id}.atif.json + agent_name: harbor-calculator-demo + atof: + enabled: true + output_directory: /logs/agent/fabric-artifacts/hermes-relay/relay + filename: events.atof.jsonl + mode: overwrite + openinference: + enabled: true + transport: http_binary + endpoint: http://host.docker.internal:6006/v1/traces diff --git a/examples/harbor/demo/task/environment/fabric/configs/hermes.yaml b/examples/harbor/demo/task/environment/fabric/configs/hermes.yaml index 2aec928a1..7984cd3c5 100644 --- a/examples/harbor/demo/task/environment/fabric/configs/hermes.yaml +++ b/examples/harbor/demo/task/environment/fabric/configs/hermes.yaml @@ -33,5 +33,4 @@ environment: workspace: /app artifacts: /logs/agent/fabric-artifacts/hermes -telemetry: - enabled: false +telemetry: null diff --git a/examples/harbor/demo/task/environment/fabric/configs/smoke.yaml b/examples/harbor/demo/task/environment/fabric/configs/smoke.yaml index aacdfc865..bf0aec56c 100644 --- a/examples/harbor/demo/task/environment/fabric/configs/smoke.yaml +++ b/examples/harbor/demo/task/environment/fabric/configs/smoke.yaml @@ -21,5 +21,4 @@ environment: workspace: /app artifacts: /logs/agent/fabric-artifacts/smoke -telemetry: - enabled: false +telemetry: null diff --git a/pyproject.toml b/pyproject.toml index 07dddf640..f5af1218a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ hermes = [ ] relay = [ - "nemo-relay~=0.4", + "nemo-relay~=0.5.0", "tomli-w~=1.2", # Needed by adapters to write relay config files ] diff --git a/python/src/nemo_fabric/__init__.py b/python/src/nemo_fabric/__init__.py index 2af20b50a..356cf6fb0 100644 --- a/python/src/nemo_fabric/__init__.py +++ b/python/src/nemo_fabric/__init__.py @@ -4,47 +4,53 @@ """Python SDK surface for NeMo Fabric.""" from nemo_fabric.client import Fabric -from nemo_fabric.errors import ( - FabricCapabilityError, - FabricConfigError, - FabricError, - FabricNativeUnavailableError, - FabricRuntimeError, - FabricStateError, -) -from nemo_fabric.runtime import Runtime, RuntimeStatus -from nemo_fabric.models import ( - EnvironmentConfig, - FabricBaseModel, - FabricConfig, - FabricProfileConfig, - HarnessConfig, - McpConfig, - McpServerConfig, - MetadataConfig, - ModelConfig, - ProfileRegistryConfig, - RunRequest, - RuntimeConfig, - SkillConfig, - TelemetryConfig, -) -from nemo_fabric.types import ( - AdapterInfo, - ArtifactManifest, - ArtifactRef, - DoctorCheck, - DoctorReport, - EffectiveConfig, - ErrorInfo, - FabricEvent, - RunOutput, - RunPlan, - RunResult, - RuntimeCapabilities, - RuntimeHandle, - TelemetryRef, -) +from nemo_fabric.errors import FabricCapabilityError +from nemo_fabric.errors import FabricConfigError +from nemo_fabric.errors import FabricError +from nemo_fabric.errors import FabricNativeUnavailableError +from nemo_fabric.errors import FabricRuntimeError +from nemo_fabric.errors import FabricStateError +from nemo_fabric.models import EnvironmentConfig +from nemo_fabric.models import FabricBaseModel +from nemo_fabric.models import FabricConfig +from nemo_fabric.models import FabricProfileConfig +from nemo_fabric.models import HarnessConfig +from nemo_fabric.models import McpConfig +from nemo_fabric.models import McpServerConfig +from nemo_fabric.models import MetadataConfig +from nemo_fabric.models import ModelConfig +from nemo_fabric.models import ProfileRegistryConfig +from nemo_fabric.models import RelayAtifConfig +from nemo_fabric.models import RelayAtofConfig +from nemo_fabric.models import RelayAtofEndpointConfig +from nemo_fabric.models import RelayComponentConfig +from nemo_fabric.models import RelayConfig +from nemo_fabric.models import RelayConfigPolicy +from nemo_fabric.models import RelayHttpStorageConfig +from nemo_fabric.models import RelayObservabilityConfig +from nemo_fabric.models import RelayOtlpConfig +from nemo_fabric.models import RelayS3StorageConfig +from nemo_fabric.models import RunRequest +from nemo_fabric.models import RuntimeConfig +from nemo_fabric.models import SkillConfig +from nemo_fabric.models import TelemetryConfig +from nemo_fabric.models import TelemetryProviderConfig +from nemo_fabric.runtime import Runtime +from nemo_fabric.runtime import RuntimeStatus +from nemo_fabric.types import AdapterInfo +from nemo_fabric.types import ArtifactManifest +from nemo_fabric.types import ArtifactRef +from nemo_fabric.types import DoctorCheck +from nemo_fabric.types import DoctorReport +from nemo_fabric.types import EffectiveConfig +from nemo_fabric.types import ErrorInfo +from nemo_fabric.types import FabricEvent +from nemo_fabric.types import RunOutput +from nemo_fabric.types import RunPlan +from nemo_fabric.types import RunResult +from nemo_fabric.types import RuntimeCapabilities +from nemo_fabric.types import RuntimeHandle +from nemo_fabric.types import TelemetryRef __all__ = [ "AdapterInfo", @@ -69,6 +75,16 @@ "MetadataConfig", "ModelConfig", "ProfileRegistryConfig", + "RelayAtifConfig", + "RelayAtofConfig", + "RelayAtofEndpointConfig", + "RelayComponentConfig", + "RelayConfigPolicy", + "RelayHttpStorageConfig", + "RelayObservabilityConfig", + "RelayOtlpConfig", + "RelayS3StorageConfig", + "RelayConfig", "FabricNativeUnavailableError", "FabricRuntimeError", "FabricStateError", @@ -83,5 +99,6 @@ "RuntimeStatus", "SkillConfig", "TelemetryConfig", + "TelemetryProviderConfig", "TelemetryRef", ] diff --git a/python/src/nemo_fabric/models.py b/python/src/nemo_fabric/models.py index cc3fc38b5..098851634 100644 --- a/python/src/nemo_fabric/models.py +++ b/python/src/nemo_fabric/models.py @@ -13,12 +13,19 @@ import math import uuid -from collections.abc import Mapping, Sequence +from collections.abc import Mapping +from collections.abc import Sequence from pathlib import Path -from typing import Any, Literal +from typing import Annotated +from typing import Any +from typing import Literal +from typing import Self -from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator -from typing_extensions import Self +from pydantic import BaseModel +from pydantic import ConfigDict +from pydantic import Field +from pydantic import field_validator +from pydantic import model_validator def _json_value(value: Any, name: str) -> Any: @@ -225,45 +232,159 @@ def remove_server(self, name: str) -> Self: return self -class TelemetryConfig(FabricBaseModel): - """Telemetry configuration.""" +class RelayConfigPolicy(FabricBaseModel): + """NeMo Relay config validation policy.""" + + unknown_component: Literal["ignore", "warn", "error"] = "warn" + unknown_field: Literal["ignore", "warn", "error"] = "warn" + unsupported_value: Literal["ignore", "warn", "error"] = "error" + + +class RelayAtofEndpointConfig(FabricBaseModel): + """NeMo Relay ATOF remote endpoint configuration.""" + + url: str + transport: Literal["http_post", "websocket", "ndjson"] = "http_post" + headers: dict[str, str] = Field(default_factory=dict) + timeout_millis: int = 3000 + field_name_policy: Literal["preserve", "replace_dots"] = "preserve" + + +class RelayAtofConfig(FabricBaseModel): + """NeMo Relay ATOF export configuration.""" + + enabled: bool = False + output_directory: str | Path | None = None + filename: str | None = None + mode: Literal["append", "overwrite"] = "append" + endpoints: list[RelayAtofEndpointConfig | dict[str, Any]] | None = None + + +class RelayS3StorageConfig(FabricBaseModel): + """NeMo Relay ATIF S3 storage configuration.""" + + type: Literal["s3"] = "s3" + bucket: str = "" + key_prefix: str | None = None + access_key_id: str | None = None + secret_access_key_var: str | None = None + session_token_var: str | None = None + region: str | None = None + endpoint_url: str | None = None + allow_http: bool | None = None + + +class RelayHttpStorageConfig(FabricBaseModel): + """NeMo Relay ATIF HTTP storage configuration.""" + + type: Literal["http"] = "http" + endpoint: str = "" + headers: dict[str, str] = Field(default_factory=dict) + header_env: dict[str, str] = Field(default_factory=dict) + timeout_millis: int = 3000 + + +class RelayAtifConfig(FabricBaseModel): + """NeMo Relay ATIF export configuration.""" + + enabled: bool = False + agent_name: str = "NeMo Relay" + agent_version: str | None = None + model_name: str = "unknown" + tool_definitions: list[dict[str, Any]] | None = None + extra: dict[str, Any] | None = None + output_directory: str | Path | None = None + filename_template: str = "nemo-relay-atif-{session_id}.json" + storage: ( + list[ + Annotated[ + RelayS3StorageConfig | RelayHttpStorageConfig, + Field(discriminator="type"), + ] + | dict[str, Any] + ] + | None + ) = None + + +class RelayOtlpConfig(FabricBaseModel): + """NeMo Relay OTLP export configuration for OpenTelemetry/OpenInference.""" enabled: bool = False - provider: Literal["relay", "native"] | None = None + transport: Literal["http_binary", "grpc"] = "http_binary" + endpoint: str | None = None + headers: dict[str, str] = Field(default_factory=dict) + resource_attributes: dict[str, str] = Field(default_factory=dict) + service_name: str = "nemo-relay" + service_namespace: str | None = None + service_version: str | None = None + instrumentation_scope: str | None = None + timeout_millis: int = 3000 + + +class RelayObservabilityConfig(FabricBaseModel): + """NeMo Relay observability component configuration.""" + + version: int = 1 + atof: RelayAtofConfig | dict[str, Any] | None = None + atif: RelayAtifConfig | dict[str, Any] | None = None + opentelemetry: RelayOtlpConfig | dict[str, Any] | None = None + openinference: RelayOtlpConfig | dict[str, Any] | None = None + policy: RelayConfigPolicy | dict[str, Any] | None = None + + +class RelayComponentConfig(FabricBaseModel): + """Generic NeMo Relay plugin component configuration.""" + + kind: str = Field(min_length=1) + enabled: bool = True + config: dict[str, Any] = Field(default_factory=dict) + + +class RelayConfig(FabricBaseModel): + """First-class NeMo Relay integration configuration.""" + 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) + policy: RelayConfigPolicy | dict[str, Any] | None = None + + +class TelemetryProviderConfig(FabricBaseModel): + """Provider-specific telemetry configuration.""" + config: dict[str, Any] | None = None + +class TelemetryConfig(FabricBaseModel): + """Telemetry configuration.""" + + providers: dict[Literal["relay", "native"], TelemetryProviderConfig | dict[str, Any]] = Field(default_factory=dict) + def enable_relay( self, - *, - project: str | None = None, - output_dir: str | Path | None = None, - config: Mapping[str, Any] | None = None, ) -> Self: """Enable NeMo Relay telemetry for subsequently started runtimes.""" - self.enabled = True - self.provider = "relay" - if project is not None: - self.project = project - if output_dir is not None: - self.output_dir = output_dir - if config is not None: - self.config = dict(config) + self.providers["relay"] = TelemetryProviderConfig() return self - def enable_native(self) -> Self: + def enable_native(self, *, config: Mapping[str, Any] | None = None) -> Self: """Let the selected adapter handle telemetry natively.""" - self.enabled = True - self.provider = "native" + provider_config = self.providers.get("native", TelemetryProviderConfig()) + if not isinstance(provider_config, TelemetryProviderConfig): + provider_config = TelemetryProviderConfig.model_validate(provider_config) + if config is not None: + provider_config.config = dict(config) + self.providers["native"] = provider_config return self - def disable(self) -> Self: - """Disable telemetry.""" + def remove_provider(self, provider: Literal["relay", "native"]) -> Self: + """Remove a configured telemetry provider.""" - self.enabled = False + self.providers.pop(provider, None) return self @@ -285,6 +406,7 @@ class FabricConfig(FabricBaseModel): mcp: McpConfig | None = None skills: SkillConfig | None = None telemetry: TelemetryConfig | None = None + relay: RelayConfig | dict[str, Any] | None = None profiles: ProfileRegistryConfig | dict[str, Any] | None = None tools: Any = None @@ -355,16 +477,30 @@ def enable_relay( *, project: str | None = None, output_dir: str | Path | None = None, - config: Mapping[str, Any] | None = None, + observability: RelayObservabilityConfig | Mapping[str, Any] | None = None, + components: Sequence[RelayComponentConfig | Mapping[str, Any]] | None = None, + policy: RelayConfigPolicy | Mapping[str, Any] | None = None, ) -> Self: """Enable NeMo Relay telemetry and return this config.""" if self.telemetry is None: self.telemetry = TelemetryConfig() - self.telemetry.enable_relay( + self.telemetry.enable_relay() + relay_observability = ( + observability + if observability is None or isinstance(observability, RelayObservabilityConfig) + else dict(observability) + ) + relay_components = [ + item if isinstance(item, RelayComponentConfig) else dict(item) for item in (components or []) + ] + relay_policy = policy if policy is None or isinstance(policy, RelayConfigPolicy) else dict(policy) + self.relay = RelayConfig( project=project, output_dir=output_dir, - config=config, + observability=relay_observability, + components=relay_components, + policy=relay_policy, ) return self @@ -382,6 +518,7 @@ class FabricProfileConfig(FabricBaseModel): mcp: McpConfig | dict[str, Any] | None = None skills: SkillConfig | dict[str, Any] | None = None telemetry: TelemetryConfig | dict[str, Any] | None = None + relay: RelayConfig | dict[str, Any] | None = None tools: Any = None diff --git a/python/src/nemo_fabric/types.py b/python/src/nemo_fabric/types.py index 393fad5dd..f75018d61 100644 --- a/python/src/nemo_fabric/types.py +++ b/python/src/nemo_fabric/types.py @@ -6,11 +6,14 @@ from __future__ import annotations import math -from collections.abc import Iterator, Mapping, Sequence +from collections.abc import Iterator +from collections.abc import Mapping +from collections.abc import Sequence from copy import deepcopy from pathlib import Path from types import MappingProxyType -from typing import Any, TypeVar +from typing import Any +from typing import TypeVar from pydantic import BaseModel @@ -101,12 +104,9 @@ def __init__( extras = _mapping({} if extra_fields is None else extra_fields, "extra_fields") overlap = self._fields.intersection(extras) if overlap: - raise FabricConfigError( - f"extra_fields duplicates known fields: {', '.join(sorted(overlap))}" - ) + raise FabricConfigError(f"extra_fields duplicates known fields: {', '.join(sorted(overlap))}") stored = { - key: deepcopy(item) if isinstance(item, _ConfigMapping) else _plain(item) - for key, item in values.items() + key: deepcopy(item) if isinstance(item, _ConfigMapping) else _plain(item) for key, item in values.items() } super().__init__({**stored, **extras}) @@ -130,11 +130,7 @@ def __setattr__(self, name: str, value: Any) -> None: def extra_fields(self) -> dict[str, Any]: """Return preserved schema-extension fields as a deep copy.""" - return { - key: _plain(value) - for key, value in self.items() - if key not in self._fields - } + return {key: _plain(value) for key, value in self.items() if key not in self._fields} def to_mapping(self) -> dict[str, Any]: """Return a detached, JSON-compatible mapping for serialization.""" @@ -265,11 +261,7 @@ 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"), - extra_fields={ - key: item - for key, item in data.items() - if key not in cls._fields - }, + extra_fields={key: item for key, item in data.items() if key not in cls._fields}, ) @@ -285,9 +277,7 @@ class _EnvironmentConfig(_ConfigMapping): extra_fields: Preserved extension fields not recognized by this SDK. """ - _fields = frozenset( - {"provider", "workspace", "artifacts", "settings", "metadata"} - ) + _fields = frozenset({"provider", "workspace", "artifacts", "settings", "metadata"}) _omit_if_empty = frozenset({"settings", "metadata"}) def __init__( @@ -348,9 +338,7 @@ def __init__( paths: Sequence[str | Path] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: - values: dict[str, Any] = { - "paths": [str(path) for path in ([] if paths is None else paths)] - } + values: dict[str, Any] = {"paths": [str(path) for path in ([] if paths is None else paths)]} super().__init__(values, extra_fields=extra_fields) @classmethod @@ -394,9 +382,7 @@ def __init__( servers: Mapping[str, Any] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: - values: dict[str, Any] = { - "servers": _mapping({} if servers is None else servers, "mcp servers") - } + values: dict[str, Any] = {"servers": _mapping({} if servers is None else servers, "mcp servers")} super().__init__(values, extra_fields=extra_fields) @classmethod @@ -451,28 +437,16 @@ def remove_server(self, name: str) -> "_McpConfig": class _TelemetryConfig(_ConfigMapping): """Telemetry configuration with authoring helpers.""" - _fields = frozenset({"enabled", "provider", "project", "output_dir", "config"}) + _fields = frozenset({"providers"}) _PROVIDERS = frozenset({"relay", "native"}) def __init__( self, *, - enabled: bool = False, - provider: str | None = None, - project: str | None = None, - output_dir: str | Path | None = None, - config: Mapping[str, Any] | None = None, + providers: Mapping[str, Any] | None = None, extra_fields: Mapping[str, Any] | None = None, ) -> None: - values: dict[str, Any] = {"enabled": _boolean(enabled, "telemetry enabled")} - if provider is not None: - values["provider"] = self._provider(provider) - if project is not None: - values["project"] = project - if output_dir is not None: - values["output_dir"] = output_dir - if config is not None: - values["config"] = config + values: dict[str, Any] = {"providers": self._providers(providers or {})} super().__init__(values, extra_fields=extra_fields) @classmethod @@ -481,54 +455,58 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_TelemetryConfig": data = _mapping(value, "telemetry") return cls( - enabled=data.get("enabled", False), - provider=data.get("provider"), - project=data.get("project"), - output_dir=data.get("output_dir"), - config=data.get("config"), + providers=data.get("providers", {}), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, ) @classmethod - def _provider(cls, provider: str) -> str: - value = _required_text(provider, "telemetry provider") - if value not in cls._PROVIDERS: - allowed = ", ".join(sorted(cls._PROVIDERS)) - raise FabricConfigError(f"telemetry provider must be one of: {allowed}") - return value + def _providers(cls, providers: Mapping[str, Any]) -> dict[str, Any]: + values: dict[str, Any] = {} + for key, value in _mapping(providers, "telemetry providers").items(): + provider = _required_text(key, "telemetry provider") + if provider not in cls._PROVIDERS: + allowed = ", ".join(sorted(cls._PROVIDERS)) + raise FabricConfigError(f"telemetry provider must be one of: {allowed}") + values[provider] = _mapping(value, f"{provider} telemetry provider") + return values def enable_relay( self, - *, - project: str | None = None, - output_dir: str | Path | None = None, - config: Mapping[str, Any] | None = None, ) -> "_TelemetryConfig": """Enable NeMo Relay telemetry for subsequently started runtimes.""" - self["enabled"] = True - self["provider"] = "relay" - if project is not None: - self["project"] = project - if output_dir is not None: - self["output_dir"] = str(output_dir) - if config is not None: - self["config"] = _mapping(config, "telemetry config") + providers = dict(self.get("providers", {})) + providers["relay"] = {} + self["providers"] = providers return self - def enable_native(self) -> "_TelemetryConfig": + def enable_native(self, *, config: Mapping[str, Any] | None = None) -> "_TelemetryConfig": """Let the selected harness adapter handle telemetry natively.""" - self["enabled"] = True - self["provider"] = "native" + providers = dict(self.get("providers", {})) + provider_config: dict[str, Any] = dict(providers.get("native", {})) + if config is not None: + provider_config["config"] = _mapping(config, "native telemetry config") + providers["native"] = provider_config + self["providers"] = providers return self - def disable(self) -> "_TelemetryConfig": - """Disable telemetry for subsequently started runtimes.""" + def remove_provider(self, provider: str) -> "_TelemetryConfig": + """Remove a configured telemetry provider.""" - self["enabled"] = False + providers = dict(self.get("providers", {})) + providers.pop(self._provider(provider), None) + self["providers"] = providers return self + @classmethod + def _provider(cls, provider: str) -> str: + value = _required_text(provider, "telemetry provider") + if value not in cls._PROVIDERS: + allowed = ", ".join(sorted(cls._PROVIDERS)) + raise FabricConfigError(f"telemetry provider must be one of: {allowed}") + return value + class _ResolvedFabricConfig(_ConfigMapping): """Mutable typed representation of a Fabric agent configuration. @@ -547,6 +525,7 @@ class _ResolvedFabricConfig(_ConfigMapping): mcp: Optional MCP configuration. skills: Optional skill configuration. telemetry: Optional telemetry configuration. + relay: Optional Relay integration configuration. profiles: Optional profile-discovery configuration. tools: Optional harness-neutral tool configuration. extra_fields: Preserved extension fields not recognized by this SDK. @@ -563,6 +542,7 @@ class _ResolvedFabricConfig(_ConfigMapping): "mcp", "skills", "telemetry", + "relay", "profiles", "tools", } @@ -581,6 +561,7 @@ def __init__( mcp: Mapping[str, Any] | None = None, skills: Mapping[str, Any] | None = None, telemetry: Mapping[str, Any] | None = None, + relay: Mapping[str, Any] | None = None, profiles: Mapping[str, Any] | None = None, tools: Any = None, extra_fields: Mapping[str, Any] | None = None, @@ -592,18 +573,11 @@ def __init__( _RuntimeConfig() if runtime is None else runtime, "runtime", ) - environment_value = ( - None - if environment is None - else _coerce(_EnvironmentConfig, environment, "environment") - ) + environment_value = None if environment is None else _coerce(_EnvironmentConfig, environment, "environment") 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") - ) + telemetry_value = None if telemetry is None else _coerce(_TelemetryConfig, telemetry, "telemetry") + relay_value = None if relay is None else _mapping(relay, "relay") values: dict[str, Any] = { "schema_version": _required_text(schema_version, "schema_version"), "metadata": metadata_value, @@ -616,6 +590,7 @@ def __init__( ("mcp", mcp_value), ("skills", skills_value), ("telemetry", telemetry_value), + ("relay", relay_value), ("profiles", profiles), ("tools", tools), ): @@ -642,6 +617,7 @@ def from_mapping(cls, value: Mapping[str, Any]) -> "_ResolvedFabricConfig": mcp=data.get("mcp"), skills=data.get("skills"), telemetry=data.get("telemetry"), + relay=data.get("relay"), profiles=data.get("profiles"), tools=data.get("tools"), extra_fields={key: item for key, item in data.items() if key not in cls._fields}, @@ -718,15 +694,25 @@ def enable_relay( *, project: str | None = None, output_dir: str | Path | None = None, - config: Mapping[str, Any] | None = None, + observability: Mapping[str, Any] | None = None, + components: Sequence[Mapping[str, Any]] | None = None, + policy: Mapping[str, Any] | None = None, ) -> "_ResolvedFabricConfig": """Enable NeMo Relay telemetry and return this config.""" - self.telemetry.enable_relay( - project=project, - output_dir=output_dir, - config=config, - ) + self.telemetry.enable_relay() + relay = dict(self.get("relay") or {}) + if project is not None: + relay["project"] = project + if output_dir is not None: + relay["output_dir"] = str(output_dir) + if observability is not None: + relay["observability"] = _mapping(observability, "relay observability") + if components is not None: + relay["components"] = [_mapping(component, "relay component") for component in components] + if policy is not None: + relay["policy"] = _mapping(policy, "relay policy") + self["relay"] = relay return self @@ -815,13 +801,7 @@ def __getattr__(self, name: str) -> Any: def extra_fields(self) -> Mapping[str, Any]: """Return an immutable view of preserved extension fields.""" - return MappingProxyType( - { - key: _thaw(value) - for key, value in self._data.items() - if key not in self._fields - } - ) + return MappingProxyType({key: _thaw(value) for key, value in self._data.items() if key not in self._fields}) def to_mapping(self) -> dict[str, Any]: """Return a detached, JSON-compatible mapping for serialization.""" @@ -922,18 +902,14 @@ class EffectiveConfig(FabricMapping): config_path: Path | None config_root: Path config: _ResolvedFabricConfig - _fields = frozenset( - {"agent_name", "profiles", "agent_root", "config_path", "config_root", "config"} - ) + _fields = frozenset({"agent_name", "profiles", "agent_root", "config_path", "config_root", "config"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: data["profiles"] = _required_profiles(data, "EffectiveConfig") data["agent_root"] = Path(data.get("agent_root", ".")) data["config_root"] = Path(data.get("config_root", ".")) - data["config_path"] = ( - None if data.get("config_path") is None else Path(data["config_path"]) - ) + data["config_path"] = None if data.get("config_path") is None else Path(data["config_path"]) data["config"] = _ResolvedFabricConfig.from_mapping(data.get("config", {})) return data @@ -954,9 +930,7 @@ class RunPlan(FabricMapping): profiles: Sequence[str] adapter: AdapterInfo capabilities: RuntimeCapabilities - _fields = frozenset( - {"effective_config", "agent_name", "profiles", "adapter", "capabilities"} - ) + _fields = frozenset({"effective_config", "agent_name", "profiles", "adapter", "capabilities"}) @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: @@ -1012,9 +986,7 @@ class DoctorReport(FabricMapping): @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: data["profiles"] = _required_profiles(data, "DoctorReport") - data["checks"] = tuple( - DoctorCheck.from_mapping(check) for check in data.get("checks", []) - ) + data["checks"] = tuple(DoctorCheck.from_mapping(check) for check in data.get("checks", [])) return data @@ -1084,9 +1056,7 @@ class ArtifactManifest(FabricMapping): @classmethod def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: data["root"] = None if data.get("root") is None else Path(data["root"]) - data["artifacts"] = tuple( - ArtifactRef.from_mapping(artifact) for artifact in data.get("artifacts", []) - ) + data["artifacts"] = tuple(ArtifactRef.from_mapping(artifact) for artifact in data.get("artifacts", [])) return data @@ -1113,9 +1083,13 @@ class TelemetryRef(FabricMapping): def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: metadata = _mapping(data.get("metadata", {}), "telemetry metadata") if "relay_enabled" in data: + providers = metadata.get("telemetry_providers") + provider = ( + providers[0] if isinstance(providers, list) and providers and isinstance(providers[0], str) else "relay" + ) metadata.setdefault("relay_enabled", data["relay_enabled"]) data = { - "provider": metadata.get("telemetry_provider", "relay"), + "provider": provider, "kind": "trace", "uri": metadata.get("relay_output_dir"), "trace_id": metadata.get("trace_id"), @@ -1298,24 +1272,16 @@ def _normalize(cls, data: dict[str, Any]) -> dict[str, Any]: "status", ): data[field] = _required_text(data.get(field), field.replace("_", " ")) - data["error"] = ( - None if data.get("error") is None else ErrorInfo.from_mapping(data["error"]) - ) - data["artifacts"] = ArtifactManifest.from_mapping( - data.get("artifacts", {"artifacts": []}) - ) + data["error"] = None if data.get("error") is None else ErrorInfo.from_mapping(data["error"]) + data["artifacts"] = ArtifactManifest.from_mapping(data.get("artifacts", {"artifacts": []})) telemetry = data.get("telemetry") if telemetry is None: data["telemetry"] = () elif isinstance(telemetry, Mapping): data["telemetry"] = (TelemetryRef.from_mapping(telemetry),) else: - data["telemetry"] = tuple( - TelemetryRef.from_mapping(item) for item in telemetry - ) - data["events"] = tuple( - FabricEvent.from_mapping(event) for event in data.get("events", []) - ) + data["telemetry"] = tuple(TelemetryRef.from_mapping(item) for item in telemetry) + data["events"] = tuple(FabricEvent.from_mapping(event) for event in data.get("events", [])) data["metadata"] = _mapping(data.get("metadata", {}), "result metadata") raw_output = data.get("output") if isinstance(raw_output, RunOutput): diff --git a/schemas/adapter-invocation.schema.json b/schemas/adapter-invocation.schema.json index eea68e66e..488e511f5 100644 --- a/schemas/adapter-invocation.schema.json +++ b/schemas/adapter-invocation.schema.json @@ -417,6 +417,17 @@ "$ref": "#/$defs/ProfileRegistryConfig", "description": "Optional profile discovery config." }, + "relay": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfig" + }, + { + "type": "null" + } + ], + "description": "First-class NeMo Relay integration configuration." + }, "runtime": { "$ref": "#/$defs/RuntimeConfig", "description": "Runtime input/output contract." @@ -639,6 +650,577 @@ }, "type": "object" }, + "RelayAtifConfig": { + "additionalProperties": true, + "description": "Relay ATIF export configuration.", + "properties": { + "agent_name": { + "default": "NeMo Relay", + "description": "Agent name written into ATIF.", + "type": "string" + }, + "agent_version": { + "description": "Agent version written into ATIF.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "default": false, + "description": "Whether ATIF export is enabled.", + "type": "boolean" + }, + "extra": { + "description": "Extra ATIF metadata." + }, + "filename_template": { + "default": "nemo-relay-atif-{session_id}.json", + "description": "ATIF file name template.", + "type": "string" + }, + "model_name": { + "default": "unknown", + "description": "Model name written into ATIF.", + "type": "string" + }, + "output_directory": { + "description": "Directory used for ATIF files.", + "type": [ + "string", + "null" + ] + }, + "storage": { + "description": "Optional ATIF remote storage.", + "items": { + "$ref": "#/$defs/RelayAtifStorageConfig" + }, + "type": [ + "array", + "null" + ] + }, + "tool_definitions": { + "description": "Tool definitions written into ATIF.", + "items": true, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtifStorageConfig": { + "description": "Relay ATIF remote storage configuration.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to HTTP storage.", + "properties": { + "endpoint": { + "default": "", + "description": "HTTP storage endpoint.", + "type": "string" + }, + "header_env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment-variable-backed HTTP headers.", + "type": "object" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Static HTTP headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "http", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to S3-compatible storage.", + "properties": { + "access_key_id": { + "description": "AWS access key id.", + "type": [ + "string", + "null" + ] + }, + "allow_http": { + "description": "Allow HTTP endpoints for S3-compatible storage.", + "type": [ + "boolean", + "null" + ] + }, + "bucket": { + "default": "", + "description": "S3 bucket name.", + "type": "string" + }, + "endpoint_url": { + "description": "S3-compatible endpoint URL.", + "type": [ + "string", + "null" + ] + }, + "key_prefix": { + "description": "Optional S3 object key prefix.", + "type": [ + "string", + "null" + ] + }, + "region": { + "description": "AWS region.", + "type": [ + "string", + "null" + ] + }, + "secret_access_key_var": { + "description": "Environment variable containing the AWS secret access key.", + "type": [ + "string", + "null" + ] + }, + "session_token_var": { + "description": "Environment variable containing the AWS session token.", + "type": [ + "string", + "null" + ] + }, + "type": { + "const": "s3", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } + ] + }, + "RelayAtofConfig": { + "additionalProperties": true, + "description": "Relay ATOF export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether ATOF export is enabled.", + "type": "boolean" + }, + "endpoints": { + "description": "Optional remote ATOF endpoints.", + "items": { + "$ref": "#/$defs/RelayAtofEndpointConfig" + }, + "type": "array" + }, + "filename": { + "description": "ATOF file name.", + "type": [ + "string", + "null" + ] + }, + "mode": { + "$ref": "#/$defs/RelayAtofMode", + "default": "append", + "description": "File write mode." + }, + "output_directory": { + "description": "Directory used for ATOF files.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtofEndpointConfig": { + "additionalProperties": true, + "description": "Relay ATOF endpoint configuration.", + "properties": { + "field_name_policy": { + "$ref": "#/$defs/RelayAtofEndpointFieldNamePolicy", + "default": "preserve", + "description": "Field-name handling policy." + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Endpoint headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayAtofEndpointTransport", + "default": "http_post", + "description": "Endpoint transport." + }, + "url": { + "description": "Endpoint URL.", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + }, + "RelayAtofEndpointFieldNamePolicy": { + "description": "Relay ATOF endpoint field-name policy.", + "oneOf": [ + { + "const": "preserve", + "description": "Preserve field names.", + "type": "string" + }, + { + "const": "replace_dots", + "description": "Replace dots in field names.", + "type": "string" + } + ] + }, + "RelayAtofEndpointTransport": { + "description": "Relay ATOF endpoint transport.", + "oneOf": [ + { + "const": "http_post", + "description": "HTTP POST transport.", + "type": "string" + }, + { + "const": "websocket", + "description": "WebSocket transport.", + "type": "string" + }, + { + "const": "ndjson", + "description": "NDJSON transport.", + "type": "string" + } + ] + }, + "RelayAtofMode": { + "description": "Relay ATOF file mode.", + "oneOf": [ + { + "const": "append", + "description": "Append to an existing ATOF file.", + "type": "string" + }, + { + "const": "overwrite", + "description": "Overwrite an existing ATOF file.", + "type": "string" + } + ] + }, + "RelayComponentConfig": { + "additionalProperties": true, + "description": "Generic NeMo Relay plugin component configuration.", + "properties": { + "config": { + "additionalProperties": true, + "description": "Component-local Relay plugin config.", + "type": "object" + }, + "enabled": { + "default": true, + "description": "Whether this Relay component should be activated.", + "type": "boolean" + }, + "kind": { + "description": "Registered Relay plugin kind.", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + "RelayConfig": { + "additionalProperties": true, + "description": "NeMo Relay integration configuration.", + "properties": { + "components": { + "description": "Additional Relay plugin components.", + "items": { + "$ref": "#/$defs/RelayComponentConfig" + }, + "type": "array" + }, + "observability": { + "anyOf": [ + { + "$ref": "#/$defs/RelayObservabilityConfig" + }, + { + "type": "null" + } + ], + "description": "Relay observability component configuration." + }, + "output_dir": { + "description": "Optional Relay output directory.", + "type": [ + "string", + "null" + ] + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay plugin validation policy." + }, + "project": { + "description": "Optional project name for Relay backends.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayConfigPolicy": { + "description": "Relay validation policy.", + "properties": { + "unknown_component": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown components." + }, + "unknown_field": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown fields." + }, + "unsupported_value": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "error", + "description": "Policy for unsupported values." + } + }, + "type": "object" + }, + "RelayObservabilityConfig": { + "additionalProperties": true, + "description": "NeMo Relay observability component configuration.", + "properties": { + "atif": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtifConfig" + }, + { + "type": "null" + } + ], + "description": "ATIF export configuration." + }, + "atof": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtofConfig" + }, + { + "type": "null" + } + ], + "description": "ATOF export configuration." + }, + "openinference": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenInference export configuration." + }, + "opentelemetry": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenTelemetry export configuration." + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay config validation policy." + }, + "version": { + "default": 1, + "description": "Relay observability config version.", + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "RelayOtlpConfig": { + "additionalProperties": true, + "description": "Relay OpenTelemetry/OpenInference export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether OTLP export is enabled.", + "type": "boolean" + }, + "endpoint": { + "description": "OTLP endpoint.", + "type": [ + "string", + "null" + ] + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP headers.", + "type": "object" + }, + "instrumentation_scope": { + "description": "OTLP instrumentation scope.", + "type": [ + "string", + "null" + ] + }, + "resource_attributes": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP resource attributes.", + "type": "object" + }, + "service_name": { + "default": "nemo-relay", + "description": "OTLP service name.", + "type": "string" + }, + "service_namespace": { + "description": "OTLP service namespace.", + "type": [ + "string", + "null" + ] + }, + "service_version": { + "description": "OTLP service version.", + "type": [ + "string", + "null" + ] + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayOtlpTransport", + "default": "http_binary", + "description": "OTLP transport." + } + }, + "type": "object" + }, + "RelayOtlpTransport": { + "description": "Relay OTLP transport.", + "oneOf": [ + { + "const": "http_binary", + "description": "OTLP HTTP binary transport.", + "type": "string" + }, + { + "const": "grpc", + "description": "OTLP gRPC transport.", + "type": "string" + } + ] + }, + "RelayUnsupportedBehavior": { + "description": "Relay unsupported/unknown config handling.", + "oneOf": [ + { + "const": "ignore", + "description": "Ignore the unsupported or unknown value.", + "type": "string" + }, + { + "const": "warn", + "description": "Warn on the unsupported or unknown value.", + "type": "string" + }, + { + "const": "error", + "description": "Error on the unsupported or unknown value.", + "type": "string" + } + ] + }, "ResolutionStrategy": { "description": "Adapter install or availability strategy.", "oneOf": [ @@ -822,32 +1404,12 @@ "additionalProperties": true, "description": "Telemetry configuration.", "properties": { - "config": { - "description": "Pass-through telemetry backend config." - }, - "enabled": { - "default": false, - "description": "Whether telemetry is enabled for this run.", - "type": "boolean" - }, - "output_dir": { - "description": "Optional telemetry output directory.", - "type": [ - "string", - "null" - ] - }, - "project": { - "description": "Optional project name for telemetry backends.", - "type": [ - "string", - "null" - ] - }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "default": "relay", - "description": "Telemetry provider responsible for runtime integration." + "providers": { + "additionalProperties": { + "$ref": "#/$defs/TelemetryProviderConfig" + }, + "description": "Telemetry providers enabled for this run.", + "type": "object" } }, "type": "object" @@ -862,9 +1424,15 @@ }, "type": "array" }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "description": "Telemetry provider selected for this run." + "native_config": { + "description": "Native telemetry pass-through config." + }, + "providers": { + "description": "Telemetry providers selected for this run.", + "items": { + "$ref": "#/$defs/TelemetryProvider" + }, + "type": "array" }, "relay_config": { "description": "Relay pass-through config." @@ -889,7 +1457,7 @@ } }, "required": [ - "provider", + "providers", "relay_enabled" ], "type": "object" @@ -908,6 +1476,16 @@ "type": "string" } ] + }, + "TelemetryProviderConfig": { + "additionalProperties": true, + "description": "Provider-specific telemetry configuration.", + "properties": { + "config": { + "description": "Provider-specific pass-through config." + } + }, + "type": "object" } }, "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/schemas/agent.schema.json b/schemas/agent.schema.json index bb2ddc5b5..a15c83d31 100644 --- a/schemas/agent.schema.json +++ b/schemas/agent.schema.json @@ -240,6 +240,577 @@ }, "type": "object" }, + "RelayAtifConfig": { + "additionalProperties": true, + "description": "Relay ATIF export configuration.", + "properties": { + "agent_name": { + "default": "NeMo Relay", + "description": "Agent name written into ATIF.", + "type": "string" + }, + "agent_version": { + "description": "Agent version written into ATIF.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "default": false, + "description": "Whether ATIF export is enabled.", + "type": "boolean" + }, + "extra": { + "description": "Extra ATIF metadata." + }, + "filename_template": { + "default": "nemo-relay-atif-{session_id}.json", + "description": "ATIF file name template.", + "type": "string" + }, + "model_name": { + "default": "unknown", + "description": "Model name written into ATIF.", + "type": "string" + }, + "output_directory": { + "description": "Directory used for ATIF files.", + "type": [ + "string", + "null" + ] + }, + "storage": { + "description": "Optional ATIF remote storage.", + "items": { + "$ref": "#/$defs/RelayAtifStorageConfig" + }, + "type": [ + "array", + "null" + ] + }, + "tool_definitions": { + "description": "Tool definitions written into ATIF.", + "items": true, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtifStorageConfig": { + "description": "Relay ATIF remote storage configuration.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to HTTP storage.", + "properties": { + "endpoint": { + "default": "", + "description": "HTTP storage endpoint.", + "type": "string" + }, + "header_env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment-variable-backed HTTP headers.", + "type": "object" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Static HTTP headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "http", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to S3-compatible storage.", + "properties": { + "access_key_id": { + "description": "AWS access key id.", + "type": [ + "string", + "null" + ] + }, + "allow_http": { + "description": "Allow HTTP endpoints for S3-compatible storage.", + "type": [ + "boolean", + "null" + ] + }, + "bucket": { + "default": "", + "description": "S3 bucket name.", + "type": "string" + }, + "endpoint_url": { + "description": "S3-compatible endpoint URL.", + "type": [ + "string", + "null" + ] + }, + "key_prefix": { + "description": "Optional S3 object key prefix.", + "type": [ + "string", + "null" + ] + }, + "region": { + "description": "AWS region.", + "type": [ + "string", + "null" + ] + }, + "secret_access_key_var": { + "description": "Environment variable containing the AWS secret access key.", + "type": [ + "string", + "null" + ] + }, + "session_token_var": { + "description": "Environment variable containing the AWS session token.", + "type": [ + "string", + "null" + ] + }, + "type": { + "const": "s3", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } + ] + }, + "RelayAtofConfig": { + "additionalProperties": true, + "description": "Relay ATOF export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether ATOF export is enabled.", + "type": "boolean" + }, + "endpoints": { + "description": "Optional remote ATOF endpoints.", + "items": { + "$ref": "#/$defs/RelayAtofEndpointConfig" + }, + "type": "array" + }, + "filename": { + "description": "ATOF file name.", + "type": [ + "string", + "null" + ] + }, + "mode": { + "$ref": "#/$defs/RelayAtofMode", + "default": "append", + "description": "File write mode." + }, + "output_directory": { + "description": "Directory used for ATOF files.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtofEndpointConfig": { + "additionalProperties": true, + "description": "Relay ATOF endpoint configuration.", + "properties": { + "field_name_policy": { + "$ref": "#/$defs/RelayAtofEndpointFieldNamePolicy", + "default": "preserve", + "description": "Field-name handling policy." + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Endpoint headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayAtofEndpointTransport", + "default": "http_post", + "description": "Endpoint transport." + }, + "url": { + "description": "Endpoint URL.", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + }, + "RelayAtofEndpointFieldNamePolicy": { + "description": "Relay ATOF endpoint field-name policy.", + "oneOf": [ + { + "const": "preserve", + "description": "Preserve field names.", + "type": "string" + }, + { + "const": "replace_dots", + "description": "Replace dots in field names.", + "type": "string" + } + ] + }, + "RelayAtofEndpointTransport": { + "description": "Relay ATOF endpoint transport.", + "oneOf": [ + { + "const": "http_post", + "description": "HTTP POST transport.", + "type": "string" + }, + { + "const": "websocket", + "description": "WebSocket transport.", + "type": "string" + }, + { + "const": "ndjson", + "description": "NDJSON transport.", + "type": "string" + } + ] + }, + "RelayAtofMode": { + "description": "Relay ATOF file mode.", + "oneOf": [ + { + "const": "append", + "description": "Append to an existing ATOF file.", + "type": "string" + }, + { + "const": "overwrite", + "description": "Overwrite an existing ATOF file.", + "type": "string" + } + ] + }, + "RelayComponentConfig": { + "additionalProperties": true, + "description": "Generic NeMo Relay plugin component configuration.", + "properties": { + "config": { + "additionalProperties": true, + "description": "Component-local Relay plugin config.", + "type": "object" + }, + "enabled": { + "default": true, + "description": "Whether this Relay component should be activated.", + "type": "boolean" + }, + "kind": { + "description": "Registered Relay plugin kind.", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + "RelayConfig": { + "additionalProperties": true, + "description": "NeMo Relay integration configuration.", + "properties": { + "components": { + "description": "Additional Relay plugin components.", + "items": { + "$ref": "#/$defs/RelayComponentConfig" + }, + "type": "array" + }, + "observability": { + "anyOf": [ + { + "$ref": "#/$defs/RelayObservabilityConfig" + }, + { + "type": "null" + } + ], + "description": "Relay observability component configuration." + }, + "output_dir": { + "description": "Optional Relay output directory.", + "type": [ + "string", + "null" + ] + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay plugin validation policy." + }, + "project": { + "description": "Optional project name for Relay backends.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayConfigPolicy": { + "description": "Relay validation policy.", + "properties": { + "unknown_component": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown components." + }, + "unknown_field": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown fields." + }, + "unsupported_value": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "error", + "description": "Policy for unsupported values." + } + }, + "type": "object" + }, + "RelayObservabilityConfig": { + "additionalProperties": true, + "description": "NeMo Relay observability component configuration.", + "properties": { + "atif": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtifConfig" + }, + { + "type": "null" + } + ], + "description": "ATIF export configuration." + }, + "atof": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtofConfig" + }, + { + "type": "null" + } + ], + "description": "ATOF export configuration." + }, + "openinference": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenInference export configuration." + }, + "opentelemetry": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenTelemetry export configuration." + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay config validation policy." + }, + "version": { + "default": 1, + "description": "Relay observability config version.", + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "RelayOtlpConfig": { + "additionalProperties": true, + "description": "Relay OpenTelemetry/OpenInference export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether OTLP export is enabled.", + "type": "boolean" + }, + "endpoint": { + "description": "OTLP endpoint.", + "type": [ + "string", + "null" + ] + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP headers.", + "type": "object" + }, + "instrumentation_scope": { + "description": "OTLP instrumentation scope.", + "type": [ + "string", + "null" + ] + }, + "resource_attributes": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP resource attributes.", + "type": "object" + }, + "service_name": { + "default": "nemo-relay", + "description": "OTLP service name.", + "type": "string" + }, + "service_namespace": { + "description": "OTLP service namespace.", + "type": [ + "string", + "null" + ] + }, + "service_version": { + "description": "OTLP service version.", + "type": [ + "string", + "null" + ] + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayOtlpTransport", + "default": "http_binary", + "description": "OTLP transport." + } + }, + "type": "object" + }, + "RelayOtlpTransport": { + "description": "Relay OTLP transport.", + "oneOf": [ + { + "const": "http_binary", + "description": "OTLP HTTP binary transport.", + "type": "string" + }, + { + "const": "grpc", + "description": "OTLP gRPC transport.", + "type": "string" + } + ] + }, + "RelayUnsupportedBehavior": { + "description": "Relay unsupported/unknown config handling.", + "oneOf": [ + { + "const": "ignore", + "description": "Ignore the unsupported or unknown value.", + "type": "string" + }, + { + "const": "warn", + "description": "Warn on the unsupported or unknown value.", + "type": "string" + }, + { + "const": "error", + "description": "Error on the unsupported or unknown value.", + "type": "string" + } + ] + }, "ResolutionStrategy": { "description": "Adapter install or availability strategy.", "oneOf": [ @@ -322,50 +893,25 @@ "additionalProperties": true, "description": "Telemetry configuration.", "properties": { - "config": { - "description": "Pass-through telemetry backend config." - }, - "enabled": { - "default": false, - "description": "Whether telemetry is enabled for this run.", - "type": "boolean" - }, - "output_dir": { - "description": "Optional telemetry output directory.", - "type": [ - "string", - "null" - ] - }, - "project": { - "description": "Optional project name for telemetry backends.", - "type": [ - "string", - "null" - ] - }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "default": "relay", - "description": "Telemetry provider responsible for runtime integration." + "providers": { + "additionalProperties": { + "$ref": "#/$defs/TelemetryProviderConfig" + }, + "description": "Telemetry providers enabled for this run.", + "type": "object" } }, "type": "object" }, - "TelemetryProvider": { - "description": "Telemetry runtime provider.", - "oneOf": [ - { - "const": "relay", - "description": "Use NeMo Relay for telemetry integration.", - "type": "string" - }, - { - "const": "native", - "description": "Let the selected adapter handle telemetry natively.", - "type": "string" + "TelemetryProviderConfig": { + "additionalProperties": true, + "description": "Provider-specific telemetry configuration.", + "properties": { + "config": { + "description": "Provider-specific pass-through config." } - ] + }, + "type": "object" } }, "$schema": "https://json-schema.org/draft/2020-12/schema", @@ -413,6 +959,17 @@ "$ref": "#/$defs/ProfileRegistryConfig", "description": "Optional profile discovery config." }, + "relay": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfig" + }, + { + "type": "null" + } + ], + "description": "First-class NeMo Relay integration configuration." + }, "runtime": { "$ref": "#/$defs/RuntimeConfig", "description": "Runtime input/output contract." diff --git a/schemas/effective-config.schema.json b/schemas/effective-config.schema.json index 78bdaba2d..60fb1062d 100644 --- a/schemas/effective-config.schema.json +++ b/schemas/effective-config.schema.json @@ -128,6 +128,17 @@ "$ref": "#/$defs/ProfileRegistryConfig", "description": "Optional profile discovery config." }, + "relay": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfig" + }, + { + "type": "null" + } + ], + "description": "First-class NeMo Relay integration configuration." + }, "runtime": { "$ref": "#/$defs/RuntimeConfig", "description": "Runtime input/output contract." @@ -327,6 +338,577 @@ }, "type": "object" }, + "RelayAtifConfig": { + "additionalProperties": true, + "description": "Relay ATIF export configuration.", + "properties": { + "agent_name": { + "default": "NeMo Relay", + "description": "Agent name written into ATIF.", + "type": "string" + }, + "agent_version": { + "description": "Agent version written into ATIF.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "default": false, + "description": "Whether ATIF export is enabled.", + "type": "boolean" + }, + "extra": { + "description": "Extra ATIF metadata." + }, + "filename_template": { + "default": "nemo-relay-atif-{session_id}.json", + "description": "ATIF file name template.", + "type": "string" + }, + "model_name": { + "default": "unknown", + "description": "Model name written into ATIF.", + "type": "string" + }, + "output_directory": { + "description": "Directory used for ATIF files.", + "type": [ + "string", + "null" + ] + }, + "storage": { + "description": "Optional ATIF remote storage.", + "items": { + "$ref": "#/$defs/RelayAtifStorageConfig" + }, + "type": [ + "array", + "null" + ] + }, + "tool_definitions": { + "description": "Tool definitions written into ATIF.", + "items": true, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtifStorageConfig": { + "description": "Relay ATIF remote storage configuration.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to HTTP storage.", + "properties": { + "endpoint": { + "default": "", + "description": "HTTP storage endpoint.", + "type": "string" + }, + "header_env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment-variable-backed HTTP headers.", + "type": "object" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Static HTTP headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "http", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to S3-compatible storage.", + "properties": { + "access_key_id": { + "description": "AWS access key id.", + "type": [ + "string", + "null" + ] + }, + "allow_http": { + "description": "Allow HTTP endpoints for S3-compatible storage.", + "type": [ + "boolean", + "null" + ] + }, + "bucket": { + "default": "", + "description": "S3 bucket name.", + "type": "string" + }, + "endpoint_url": { + "description": "S3-compatible endpoint URL.", + "type": [ + "string", + "null" + ] + }, + "key_prefix": { + "description": "Optional S3 object key prefix.", + "type": [ + "string", + "null" + ] + }, + "region": { + "description": "AWS region.", + "type": [ + "string", + "null" + ] + }, + "secret_access_key_var": { + "description": "Environment variable containing the AWS secret access key.", + "type": [ + "string", + "null" + ] + }, + "session_token_var": { + "description": "Environment variable containing the AWS session token.", + "type": [ + "string", + "null" + ] + }, + "type": { + "const": "s3", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } + ] + }, + "RelayAtofConfig": { + "additionalProperties": true, + "description": "Relay ATOF export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether ATOF export is enabled.", + "type": "boolean" + }, + "endpoints": { + "description": "Optional remote ATOF endpoints.", + "items": { + "$ref": "#/$defs/RelayAtofEndpointConfig" + }, + "type": "array" + }, + "filename": { + "description": "ATOF file name.", + "type": [ + "string", + "null" + ] + }, + "mode": { + "$ref": "#/$defs/RelayAtofMode", + "default": "append", + "description": "File write mode." + }, + "output_directory": { + "description": "Directory used for ATOF files.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtofEndpointConfig": { + "additionalProperties": true, + "description": "Relay ATOF endpoint configuration.", + "properties": { + "field_name_policy": { + "$ref": "#/$defs/RelayAtofEndpointFieldNamePolicy", + "default": "preserve", + "description": "Field-name handling policy." + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Endpoint headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayAtofEndpointTransport", + "default": "http_post", + "description": "Endpoint transport." + }, + "url": { + "description": "Endpoint URL.", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + }, + "RelayAtofEndpointFieldNamePolicy": { + "description": "Relay ATOF endpoint field-name policy.", + "oneOf": [ + { + "const": "preserve", + "description": "Preserve field names.", + "type": "string" + }, + { + "const": "replace_dots", + "description": "Replace dots in field names.", + "type": "string" + } + ] + }, + "RelayAtofEndpointTransport": { + "description": "Relay ATOF endpoint transport.", + "oneOf": [ + { + "const": "http_post", + "description": "HTTP POST transport.", + "type": "string" + }, + { + "const": "websocket", + "description": "WebSocket transport.", + "type": "string" + }, + { + "const": "ndjson", + "description": "NDJSON transport.", + "type": "string" + } + ] + }, + "RelayAtofMode": { + "description": "Relay ATOF file mode.", + "oneOf": [ + { + "const": "append", + "description": "Append to an existing ATOF file.", + "type": "string" + }, + { + "const": "overwrite", + "description": "Overwrite an existing ATOF file.", + "type": "string" + } + ] + }, + "RelayComponentConfig": { + "additionalProperties": true, + "description": "Generic NeMo Relay plugin component configuration.", + "properties": { + "config": { + "additionalProperties": true, + "description": "Component-local Relay plugin config.", + "type": "object" + }, + "enabled": { + "default": true, + "description": "Whether this Relay component should be activated.", + "type": "boolean" + }, + "kind": { + "description": "Registered Relay plugin kind.", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + "RelayConfig": { + "additionalProperties": true, + "description": "NeMo Relay integration configuration.", + "properties": { + "components": { + "description": "Additional Relay plugin components.", + "items": { + "$ref": "#/$defs/RelayComponentConfig" + }, + "type": "array" + }, + "observability": { + "anyOf": [ + { + "$ref": "#/$defs/RelayObservabilityConfig" + }, + { + "type": "null" + } + ], + "description": "Relay observability component configuration." + }, + "output_dir": { + "description": "Optional Relay output directory.", + "type": [ + "string", + "null" + ] + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay plugin validation policy." + }, + "project": { + "description": "Optional project name for Relay backends.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayConfigPolicy": { + "description": "Relay validation policy.", + "properties": { + "unknown_component": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown components." + }, + "unknown_field": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown fields." + }, + "unsupported_value": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "error", + "description": "Policy for unsupported values." + } + }, + "type": "object" + }, + "RelayObservabilityConfig": { + "additionalProperties": true, + "description": "NeMo Relay observability component configuration.", + "properties": { + "atif": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtifConfig" + }, + { + "type": "null" + } + ], + "description": "ATIF export configuration." + }, + "atof": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtofConfig" + }, + { + "type": "null" + } + ], + "description": "ATOF export configuration." + }, + "openinference": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenInference export configuration." + }, + "opentelemetry": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenTelemetry export configuration." + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay config validation policy." + }, + "version": { + "default": 1, + "description": "Relay observability config version.", + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "RelayOtlpConfig": { + "additionalProperties": true, + "description": "Relay OpenTelemetry/OpenInference export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether OTLP export is enabled.", + "type": "boolean" + }, + "endpoint": { + "description": "OTLP endpoint.", + "type": [ + "string", + "null" + ] + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP headers.", + "type": "object" + }, + "instrumentation_scope": { + "description": "OTLP instrumentation scope.", + "type": [ + "string", + "null" + ] + }, + "resource_attributes": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP resource attributes.", + "type": "object" + }, + "service_name": { + "default": "nemo-relay", + "description": "OTLP service name.", + "type": "string" + }, + "service_namespace": { + "description": "OTLP service namespace.", + "type": [ + "string", + "null" + ] + }, + "service_version": { + "description": "OTLP service version.", + "type": [ + "string", + "null" + ] + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayOtlpTransport", + "default": "http_binary", + "description": "OTLP transport." + } + }, + "type": "object" + }, + "RelayOtlpTransport": { + "description": "Relay OTLP transport.", + "oneOf": [ + { + "const": "http_binary", + "description": "OTLP HTTP binary transport.", + "type": "string" + }, + { + "const": "grpc", + "description": "OTLP gRPC transport.", + "type": "string" + } + ] + }, + "RelayUnsupportedBehavior": { + "description": "Relay unsupported/unknown config handling.", + "oneOf": [ + { + "const": "ignore", + "description": "Ignore the unsupported or unknown value.", + "type": "string" + }, + { + "const": "warn", + "description": "Warn on the unsupported or unknown value.", + "type": "string" + }, + { + "const": "error", + "description": "Error on the unsupported or unknown value.", + "type": "string" + } + ] + }, "ResolutionStrategy": { "description": "Adapter install or availability strategy.", "oneOf": [ @@ -409,50 +991,25 @@ "additionalProperties": true, "description": "Telemetry configuration.", "properties": { - "config": { - "description": "Pass-through telemetry backend config." - }, - "enabled": { - "default": false, - "description": "Whether telemetry is enabled for this run.", - "type": "boolean" - }, - "output_dir": { - "description": "Optional telemetry output directory.", - "type": [ - "string", - "null" - ] - }, - "project": { - "description": "Optional project name for telemetry backends.", - "type": [ - "string", - "null" - ] - }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "default": "relay", - "description": "Telemetry provider responsible for runtime integration." + "providers": { + "additionalProperties": { + "$ref": "#/$defs/TelemetryProviderConfig" + }, + "description": "Telemetry providers enabled for this run.", + "type": "object" } }, "type": "object" }, - "TelemetryProvider": { - "description": "Telemetry runtime provider.", - "oneOf": [ - { - "const": "relay", - "description": "Use NeMo Relay for telemetry integration.", - "type": "string" - }, - { - "const": "native", - "description": "Let the selected adapter handle telemetry natively.", - "type": "string" + "TelemetryProviderConfig": { + "additionalProperties": true, + "description": "Provider-specific telemetry configuration.", + "properties": { + "config": { + "description": "Provider-specific pass-through config." } - ] + }, + "type": "object" } }, "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/schemas/profile.schema.json b/schemas/profile.schema.json index 4e9629414..085fd50df 100644 --- a/schemas/profile.schema.json +++ b/schemas/profile.schema.json @@ -48,6 +48,14 @@ "null" ] }, + "relay": { + "additionalProperties": true, + "description": "Partial Relay integration overlay.", + "type": [ + "object", + "null" + ] + }, "runtime": { "additionalProperties": true, "description": "Partial runtime overlay.", diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index 419962680..1f14a4793 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -546,6 +546,17 @@ "$ref": "#/$defs/ProfileRegistryConfig", "description": "Optional profile discovery config." }, + "relay": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfig" + }, + { + "type": "null" + } + ], + "description": "First-class NeMo Relay integration configuration." + }, "runtime": { "$ref": "#/$defs/RuntimeConfig", "description": "Runtime input/output contract." @@ -768,6 +779,577 @@ }, "type": "object" }, + "RelayAtifConfig": { + "additionalProperties": true, + "description": "Relay ATIF export configuration.", + "properties": { + "agent_name": { + "default": "NeMo Relay", + "description": "Agent name written into ATIF.", + "type": "string" + }, + "agent_version": { + "description": "Agent version written into ATIF.", + "type": [ + "string", + "null" + ] + }, + "enabled": { + "default": false, + "description": "Whether ATIF export is enabled.", + "type": "boolean" + }, + "extra": { + "description": "Extra ATIF metadata." + }, + "filename_template": { + "default": "nemo-relay-atif-{session_id}.json", + "description": "ATIF file name template.", + "type": "string" + }, + "model_name": { + "default": "unknown", + "description": "Model name written into ATIF.", + "type": "string" + }, + "output_directory": { + "description": "Directory used for ATIF files.", + "type": [ + "string", + "null" + ] + }, + "storage": { + "description": "Optional ATIF remote storage.", + "items": { + "$ref": "#/$defs/RelayAtifStorageConfig" + }, + "type": [ + "array", + "null" + ] + }, + "tool_definitions": { + "description": "Tool definitions written into ATIF.", + "items": true, + "type": [ + "array", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtifStorageConfig": { + "description": "Relay ATIF remote storage configuration.", + "oneOf": [ + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to HTTP storage.", + "properties": { + "endpoint": { + "default": "", + "description": "HTTP storage endpoint.", + "type": "string" + }, + "header_env": { + "additionalProperties": { + "type": "string" + }, + "description": "Environment-variable-backed HTTP headers.", + "type": "object" + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Static HTTP headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "http", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "additionalProperties": true, + "description": "Upload ATIF artifacts to S3-compatible storage.", + "properties": { + "access_key_id": { + "description": "AWS access key id.", + "type": [ + "string", + "null" + ] + }, + "allow_http": { + "description": "Allow HTTP endpoints for S3-compatible storage.", + "type": [ + "boolean", + "null" + ] + }, + "bucket": { + "default": "", + "description": "S3 bucket name.", + "type": "string" + }, + "endpoint_url": { + "description": "S3-compatible endpoint URL.", + "type": [ + "string", + "null" + ] + }, + "key_prefix": { + "description": "Optional S3 object key prefix.", + "type": [ + "string", + "null" + ] + }, + "region": { + "description": "AWS region.", + "type": [ + "string", + "null" + ] + }, + "secret_access_key_var": { + "description": "Environment variable containing the AWS secret access key.", + "type": [ + "string", + "null" + ] + }, + "session_token_var": { + "description": "Environment variable containing the AWS session token.", + "type": [ + "string", + "null" + ] + }, + "type": { + "const": "s3", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object" + } + ] + }, + "RelayAtofConfig": { + "additionalProperties": true, + "description": "Relay ATOF export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether ATOF export is enabled.", + "type": "boolean" + }, + "endpoints": { + "description": "Optional remote ATOF endpoints.", + "items": { + "$ref": "#/$defs/RelayAtofEndpointConfig" + }, + "type": "array" + }, + "filename": { + "description": "ATOF file name.", + "type": [ + "string", + "null" + ] + }, + "mode": { + "$ref": "#/$defs/RelayAtofMode", + "default": "append", + "description": "File write mode." + }, + "output_directory": { + "description": "Directory used for ATOF files.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayAtofEndpointConfig": { + "additionalProperties": true, + "description": "Relay ATOF endpoint configuration.", + "properties": { + "field_name_policy": { + "$ref": "#/$defs/RelayAtofEndpointFieldNamePolicy", + "default": "preserve", + "description": "Field-name handling policy." + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "Endpoint headers.", + "type": "object" + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayAtofEndpointTransport", + "default": "http_post", + "description": "Endpoint transport." + }, + "url": { + "description": "Endpoint URL.", + "type": "string" + } + }, + "required": [ + "url" + ], + "type": "object" + }, + "RelayAtofEndpointFieldNamePolicy": { + "description": "Relay ATOF endpoint field-name policy.", + "oneOf": [ + { + "const": "preserve", + "description": "Preserve field names.", + "type": "string" + }, + { + "const": "replace_dots", + "description": "Replace dots in field names.", + "type": "string" + } + ] + }, + "RelayAtofEndpointTransport": { + "description": "Relay ATOF endpoint transport.", + "oneOf": [ + { + "const": "http_post", + "description": "HTTP POST transport.", + "type": "string" + }, + { + "const": "websocket", + "description": "WebSocket transport.", + "type": "string" + }, + { + "const": "ndjson", + "description": "NDJSON transport.", + "type": "string" + } + ] + }, + "RelayAtofMode": { + "description": "Relay ATOF file mode.", + "oneOf": [ + { + "const": "append", + "description": "Append to an existing ATOF file.", + "type": "string" + }, + { + "const": "overwrite", + "description": "Overwrite an existing ATOF file.", + "type": "string" + } + ] + }, + "RelayComponentConfig": { + "additionalProperties": true, + "description": "Generic NeMo Relay plugin component configuration.", + "properties": { + "config": { + "additionalProperties": true, + "description": "Component-local Relay plugin config.", + "type": "object" + }, + "enabled": { + "default": true, + "description": "Whether this Relay component should be activated.", + "type": "boolean" + }, + "kind": { + "description": "Registered Relay plugin kind.", + "type": "string" + } + }, + "required": [ + "kind" + ], + "type": "object" + }, + "RelayConfig": { + "additionalProperties": true, + "description": "NeMo Relay integration configuration.", + "properties": { + "components": { + "description": "Additional Relay plugin components.", + "items": { + "$ref": "#/$defs/RelayComponentConfig" + }, + "type": "array" + }, + "observability": { + "anyOf": [ + { + "$ref": "#/$defs/RelayObservabilityConfig" + }, + { + "type": "null" + } + ], + "description": "Relay observability component configuration." + }, + "output_dir": { + "description": "Optional Relay output directory.", + "type": [ + "string", + "null" + ] + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay plugin validation policy." + }, + "project": { + "description": "Optional project name for Relay backends.", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "RelayConfigPolicy": { + "description": "Relay validation policy.", + "properties": { + "unknown_component": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown components." + }, + "unknown_field": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "warn", + "description": "Policy for unknown fields." + }, + "unsupported_value": { + "$ref": "#/$defs/RelayUnsupportedBehavior", + "default": "error", + "description": "Policy for unsupported values." + } + }, + "type": "object" + }, + "RelayObservabilityConfig": { + "additionalProperties": true, + "description": "NeMo Relay observability component configuration.", + "properties": { + "atif": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtifConfig" + }, + { + "type": "null" + } + ], + "description": "ATIF export configuration." + }, + "atof": { + "anyOf": [ + { + "$ref": "#/$defs/RelayAtofConfig" + }, + { + "type": "null" + } + ], + "description": "ATOF export configuration." + }, + "openinference": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenInference export configuration." + }, + "opentelemetry": { + "anyOf": [ + { + "$ref": "#/$defs/RelayOtlpConfig" + }, + { + "type": "null" + } + ], + "description": "OpenTelemetry export configuration." + }, + "policy": { + "anyOf": [ + { + "$ref": "#/$defs/RelayConfigPolicy" + }, + { + "type": "null" + } + ], + "description": "Relay config validation policy." + }, + "version": { + "default": 1, + "description": "Relay observability config version.", + "format": "uint32", + "minimum": 0, + "type": "integer" + } + }, + "type": "object" + }, + "RelayOtlpConfig": { + "additionalProperties": true, + "description": "Relay OpenTelemetry/OpenInference export configuration.", + "properties": { + "enabled": { + "default": false, + "description": "Whether OTLP export is enabled.", + "type": "boolean" + }, + "endpoint": { + "description": "OTLP endpoint.", + "type": [ + "string", + "null" + ] + }, + "headers": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP headers.", + "type": "object" + }, + "instrumentation_scope": { + "description": "OTLP instrumentation scope.", + "type": [ + "string", + "null" + ] + }, + "resource_attributes": { + "additionalProperties": { + "type": "string" + }, + "description": "OTLP resource attributes.", + "type": "object" + }, + "service_name": { + "default": "nemo-relay", + "description": "OTLP service name.", + "type": "string" + }, + "service_namespace": { + "description": "OTLP service namespace.", + "type": [ + "string", + "null" + ] + }, + "service_version": { + "description": "OTLP service version.", + "type": [ + "string", + "null" + ] + }, + "timeout_millis": { + "default": 3000, + "description": "Request timeout in milliseconds.", + "format": "uint64", + "minimum": 0, + "type": "integer" + }, + "transport": { + "$ref": "#/$defs/RelayOtlpTransport", + "default": "http_binary", + "description": "OTLP transport." + } + }, + "type": "object" + }, + "RelayOtlpTransport": { + "description": "Relay OTLP transport.", + "oneOf": [ + { + "const": "http_binary", + "description": "OTLP HTTP binary transport.", + "type": "string" + }, + { + "const": "grpc", + "description": "OTLP gRPC transport.", + "type": "string" + } + ] + }, + "RelayUnsupportedBehavior": { + "description": "Relay unsupported/unknown config handling.", + "oneOf": [ + { + "const": "ignore", + "description": "Ignore the unsupported or unknown value.", + "type": "string" + }, + { + "const": "warn", + "description": "Warn on the unsupported or unknown value.", + "type": "string" + }, + { + "const": "error", + "description": "Error on the unsupported or unknown value.", + "type": "string" + } + ] + }, "ResolutionStrategy": { "description": "Adapter install or availability strategy.", "oneOf": [ @@ -909,32 +1491,12 @@ "additionalProperties": true, "description": "Telemetry configuration.", "properties": { - "config": { - "description": "Pass-through telemetry backend config." - }, - "enabled": { - "default": false, - "description": "Whether telemetry is enabled for this run.", - "type": "boolean" - }, - "output_dir": { - "description": "Optional telemetry output directory.", - "type": [ - "string", - "null" - ] - }, - "project": { - "description": "Optional project name for telemetry backends.", - "type": [ - "string", - "null" - ] - }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "default": "relay", - "description": "Telemetry provider responsible for runtime integration." + "providers": { + "additionalProperties": { + "$ref": "#/$defs/TelemetryProviderConfig" + }, + "description": "Telemetry providers enabled for this run.", + "type": "object" } }, "type": "object" @@ -949,9 +1511,15 @@ }, "type": "array" }, - "provider": { - "$ref": "#/$defs/TelemetryProvider", - "description": "Telemetry provider selected for this run." + "native_config": { + "description": "Native telemetry pass-through config." + }, + "providers": { + "description": "Telemetry providers selected for this run.", + "items": { + "$ref": "#/$defs/TelemetryProvider" + }, + "type": "array" }, "relay_config": { "description": "Relay pass-through config." @@ -976,7 +1544,7 @@ } }, "required": [ - "provider", + "providers", "relay_enabled" ], "type": "object" @@ -995,6 +1563,16 @@ "type": "string" } ] + }, + "TelemetryProviderConfig": { + "additionalProperties": true, + "description": "Provider-specific telemetry configuration.", + "properties": { + "config": { + "description": "Provider-specific pass-through config." + } + }, + "type": "object" } }, "$schema": "https://json-schema.org/draft/2020-12/schema", diff --git a/tests/_utils/utils.py b/tests/_utils/utils.py index 9be1dfd99..b5570b937 100644 --- a/tests/_utils/utils.py +++ b/tests/_utils/utils.py @@ -44,9 +44,9 @@ def assert_relay_disabled_native_observability(result: dict): event_kinds = {event["kind"] for event in result["events"]} assert {"runtime_start", "invocation_start", "invocation_end"} <= event_kinds - telemetry = result["telemetry"] - assert telemetry is not None - assert telemetry["relay_enabled"] is False + telemetry = result.get("telemetry") + if telemetry is not None: + assert telemetry["relay_enabled"] is False def assert_process_adapter_native_observability(result: dict): diff --git a/tests/adapters/test_adapaters_common_hermes.py b/tests/adapters/test_adapaters_common_hermes.py index 2c6d004fa..51066860a 100644 --- a/tests/adapters/test_adapaters_common_hermes.py +++ b/tests/adapters/test_adapaters_common_hermes.py @@ -91,24 +91,26 @@ def test_selected_model_config( assert hermes_common.selected_model_config(payload) == expected -@pytest.mark.parametrize("provider", [None, "relay"]) +@pytest.mark.parametrize("providers", [None, {"relay": {}}]) def test_validate_hermes_telemetry_provider_accepts_relay( - provider: str | None, + providers: dict[str, object] | None, ): - telemetry = {"enabled": True} - if provider is not None: - telemetry["provider"] = provider - payload = {"effective_config": {"config": {"telemetry": telemetry}}} + payload = {} + if providers is not None: + payload["telemetry_plan"] = {"providers": list(providers)} hermes_common.validate_hermes_telemetry_provider(payload) def test_validate_hermes_telemetry_provider_rejects_native(): - payload = { - "effective_config": { - "config": {"telemetry": {"enabled": True, "provider": "native"}} - } - } + payload = {"telemetry_plan": {"providers": ["native"], "relay_enabled": False}} + + with pytest.raises(ValueError, match="only relay telemetry is supported for Hermes"): + hermes_common.validate_hermes_telemetry_provider(payload) + + +def test_validate_hermes_telemetry_provider_rejects_mixed_native_and_relay(): + payload = {"telemetry_plan": {"providers": ["relay", "native"], "relay_enabled": True}} with pytest.raises(ValueError, match="only relay telemetry is supported for Hermes"): hermes_common.validate_hermes_telemetry_provider(payload) @@ -273,12 +275,8 @@ def test_hermes_config_variation_matrix_surfaces_supported_capabilities( } assert config["platform_toolsets"] == {"cli": ["git", "shell"]} assert config["plugins"]["enabled"] == ["observability/nemo_relay"] - assert observability["atof"]["output_directory"] == str( - tmp_path / "relay" / "atof" / "runtime-matrix" - ) - assert observability["atif"]["output_directory"] == str( - tmp_path / "relay" / "atif" / "runtime-matrix" - ) + assert observability["atof"]["output_directory"] == str(tmp_path / "relay" / "atof" / "runtime-matrix") + assert observability["atif"]["output_directory"] == str(tmp_path / "relay" / "atif" / "runtime-matrix") assert observability["atif"]["agent_name"] == "matrix-agent" assert observability["atif"]["model_name"] == "nvidia/review-model" @@ -415,9 +413,9 @@ def test_configure_hermes_relay_sets_hermes_plugin_environment( ), encoding="utf-8", ) - os.environ["FABRIC_RELAY_ENABLED"] = "true" os.environ["FABRIC_RELAY_CONFIG_PATH"] = str(config_path) payload = { + "telemetry_plan": {"providers": ["relay"], "relay_enabled": True}, "runtime_context": {"runtime_id": "runtime-relay"}, "effective_config": { "agent_name": "review-agent", @@ -426,22 +424,18 @@ def test_configure_hermes_relay_sets_hermes_plugin_environment( "harness": {"settings": {"model": "review"}}, "models": {"review": {"model": "nvidia/review-model"}}, }, - } + }, } plugin_config = hermes_common.configure_hermes_relay(payload) assert plugin_config is not None assert os.environ["HERMES_NEMO_RELAY_ATOF_ENABLED"] == "1" - assert os.environ["HERMES_NEMO_RELAY_ATOF_OUTPUT_DIRECTORY"] == str( - tmp_path / "atof" / "runtime-relay" - ) + assert os.environ["HERMES_NEMO_RELAY_ATOF_OUTPUT_DIRECTORY"] == str(tmp_path / "atof" / "runtime-relay") assert os.environ["HERMES_NEMO_RELAY_ATOF_FILENAME"] == "custom.atof.jsonl" assert os.environ["HERMES_NEMO_RELAY_ATOF_MODE"] == "append" assert os.environ["HERMES_NEMO_RELAY_ATIF_ENABLED"] == "1" - assert os.environ["HERMES_NEMO_RELAY_ATIF_OUTPUT_DIRECTORY"] == str( - tmp_path / "atif" / "runtime-relay" - ) + assert os.environ["HERMES_NEMO_RELAY_ATIF_OUTPUT_DIRECTORY"] == str(tmp_path / "atif" / "runtime-relay") assert os.environ["HERMES_NEMO_RELAY_ATIF_FILENAME_TEMPLATE"] == "trace-{session_id}.atif.json" assert os.environ["HERMES_NEMO_RELAY_ATIF_AGENT_NAME"] == "review-agent" assert os.environ["HERMES_NEMO_RELAY_ATIF_AGENT_VERSION"] == "1.2.3" @@ -449,6 +443,4 @@ def test_configure_hermes_relay_sets_hermes_plugin_environment( def test_configure_hermes_relay_returns_none_when_disabled(): - os.environ.pop("FABRIC_RELAY_ENABLED", None) - assert hermes_common.configure_hermes_relay({}) is None diff --git a/tests/adapters/test_codex_cli.py b/tests/adapters/test_codex_cli.py index b4665fd20..286acec83 100644 --- a/tests/adapters/test_codex_cli.py +++ b/tests/adapters/test_codex_cli.py @@ -7,22 +7,15 @@ import subprocess import tomllib from pathlib import Path -from unittest.mock import MagicMock, call +from unittest.mock import MagicMock +from unittest.mock import call import pytest -import yaml -from nemo_fabric import Fabric, FabricConfig +from nemo_fabric import Fabric +from nemo_fabric import FabricConfig ROOT = Path(__file__).resolve().parents[2] -ADAPTER_PATH = ( - ROOT - / "adapters" - / "codex-cli" - / "src" - / "nemo_fabric_adapters" - / "codex_cli" - / "adapter.py" -) +ADAPTER_PATH = ROOT / "adapters" / "codex-cli" / "src" / "nemo_fabric_adapters" / "codex_cli" / "adapter.py" def load_codex_adapter(): @@ -179,9 +172,7 @@ def test_oneshot_command_uses_fabric_overrides_and_codex_owned_auth( assert "--dangerously-bypass-hook-trust" not in command assert ["--model", "gpt-5.4"] == command[-3:-1] assert command[-1] == "-" - assert tomllib.loads( - codex_settings.codex_profile_path.read_text(encoding="utf-8") - ) == { + assert tomllib.loads(codex_settings.codex_profile_path.read_text(encoding="utf-8")) == { "features": {"web_search": False}, "model_reasoning_effort": "high", } @@ -205,9 +196,7 @@ def test_configured_codex_profile_is_base_for_generated_profile(codex_payload): codex_settings = adapter.write_config_files(codex_payload) assert codex_settings.codex_profile_name == "fabric-runtime-1" - assert tomllib.loads( - codex_settings.codex_profile_path.read_text(encoding="utf-8") - ) == { + assert tomllib.loads(codex_settings.codex_profile_path.read_text(encoding="utf-8")) == { "approval_policy": "never", "model_reasoning_effort": "high", "features": { @@ -244,9 +233,7 @@ def test_codex_home_uses_environment(tmp_path): adapter = load_codex_adapter() os.environ["CODEX_HOME"] = str(tmp_path / "custom-codex-home") - name, path = adapter.get_codex_profile_path( - {"runtime_context": {"runtime_id": "runtime-1"}} - ) + name, path = adapter.get_codex_profile_path({"runtime_context": {"runtime_id": "runtime-1"}}) assert name == "fabric-runtime-1" assert path == tmp_path / "custom-codex-home" / "fabric-runtime-1.config.toml" @@ -256,9 +243,7 @@ def test_codex_home_defaults_to_user_codex_directory(): adapter = load_codex_adapter() os.environ.pop("CODEX_HOME", None) - name, path = adapter.get_codex_profile_path( - {"runtime_context": {"runtime_id": "runtime-1"}} - ) + name, path = adapter.get_codex_profile_path({"runtime_context": {"runtime_id": "runtime-1"}}) assert name == "fabric-runtime-1" assert path == Path.home() / ".codex" / "fabric-runtime-1.config.toml" @@ -270,15 +255,16 @@ def test_relay_routes_codex_through_standalone_gateway( tmp_path, ): adapter = load_codex_adapter() - os.environ["FABRIC_RELAY_ENABLED"] = "true" + codex_payload["telemetry_plan"] = { + "providers": ["relay"], + "relay_enabled": True, + } mock_find_port = MagicMock(return_value=43210) monkeypatch.setattr(adapter, "find_available_tcp_port", mock_find_port) relay_plugin_config = {"version": 1, "components": []} relay_config_path = tmp_path / "relay-config" / "config.toml" mock_load_config = MagicMock(return_value=relay_plugin_config) - mock_write_config = MagicMock( - return_value=(relay_config_path, tmp_path / "plugins.toml") - ) + mock_write_config = MagicMock(return_value=(relay_config_path, tmp_path / "plugins.toml")) monkeypatch.setattr( adapter.common_utils, "load_relay_plugin_config", @@ -303,9 +289,7 @@ def test_relay_routes_codex_through_standalone_gateway( assert "--config" not in command assert command[command.index("--profile") + 1] == "fabric-runtime-1" - config = tomllib.loads( - codex_settings.codex_profile_path.read_text(encoding="utf-8") - ) + config = tomllib.loads(codex_settings.codex_profile_path.read_text(encoding="utf-8")) assert config["model_provider"] == "nemo-relay-openai" assert config["model_providers"]["nemo-relay-openai"] == { "name": "NeMo Relay OpenAI", @@ -329,14 +313,34 @@ def test_relay_routes_codex_through_standalone_gateway( ) -def test_native_otel_profile_writes_codex_telemetry_config(codex_payload, tmp_path): - from examples.code_review_agent import codex_cli_config, with_native_otel +@pytest.mark.parametrize( + ("transport", "expected_exporter", "expected_protocol"), + [ + ("http_binary", "otlp-http", "binary"), + ("grpc", "otlp-grpc", "grpc"), + ], +) +def test_native_otel_profile_writes_codex_telemetry_config( + codex_payload, + tmp_path, + transport, + expected_exporter, + expected_protocol, +): + from examples.code_review_agent import codex_cli_config + from examples.code_review_agent import with_native_otel adapter = load_codex_adapter() config = codex_payload["effective_config"]["config"] typed = with_native_otel(codex_cli_config()) assert typed.telemetry is not None - config["telemetry"] = typed.telemetry.to_mapping() + native_config = typed.telemetry.to_mapping()["providers"]["native"]["config"] + native_config["components"][0]["config"]["opentelemetry"]["transport"] = transport + codex_payload["telemetry_plan"] = { + "providers": ["native"], + "relay_enabled": False, + "native_config": native_config, + } config["harness"]["settings"]["config_overrides"] = {} codex_settings = adapter.write_config_files(codex_payload) @@ -347,15 +351,13 @@ def test_native_otel_profile_writes_codex_telemetry_config(codex_payload, tmp_pa assert command[command.index("--profile") + 1] == "fabric-runtime-1" assert "--dangerously-bypass-hook-trust" not in command - assert tomllib.loads( - codex_settings.codex_profile_path.read_text(encoding="utf-8") - ) == { + assert tomllib.loads(codex_settings.codex_profile_path.read_text(encoding="utf-8")) == { "otel": { "environment": "dev", "trace_exporter": { - "otlp-http": { + expected_exporter: { "endpoint": "http://localhost:4318/v1/traces", - "protocol": "binary", + "protocol": expected_protocol, } }, } @@ -435,7 +437,7 @@ def test_start_relay_gateway_waits_for_health_and_starts_process_group( adapter = load_codex_adapter() relay_config_path = tmp_path / "relay-config" / "config.toml" relay_config_path.parent.mkdir() - relay_config_path.write_text("[agents.codex]\ncommand = \"codex\"\n", encoding="utf-8") + relay_config_path.write_text('[agents.codex]\ncommand = "codex"\n', encoding="utf-8") mock_process = MagicMock() mock_popen = MagicMock(return_value=mock_process) mock_wait = MagicMock() @@ -555,9 +557,8 @@ def test_runtime_reuses_codex_thread_across_invocations(codex_payload, monkeypat monkeypatch.setattr(adapter.subprocess, "run", mock_run) os.environ.pop("OPENAI_API_KEY", None) os.environ["CODEX_HOME"] = str(tmp_path / "codex-home") - codex_payload["effective_config"]["config"]["harness"]["settings"]["env"] = { - "CODEX_EXPLICIT": "forward-me" - } + os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward" + codex_payload["effective_config"]["config"]["harness"]["settings"]["env"] = {"CODEX_EXPLICIT": "forward-me"} first = adapter.run_codex(codex_payload) codex_payload["runtime_context"]["invocation_id"] = "invocation-2" @@ -620,9 +621,7 @@ def test_runtime_persists_codex_thread_state(codex_payload, monkeypatch): def test_adapter_rejects_structured_input_until_chat_is_supported(codex_payload): adapter = load_codex_adapter() - codex_payload["request"]["input"] = { - "messages": [{"role": "user", "content": "Inspect the change."}] - } + codex_payload["request"]["input"] = {"messages": [{"role": "user", "content": "Inspect the change."}]} with pytest.raises(ValueError, match="requires text input"): adapter.request_to_prompt(codex_payload) @@ -649,9 +648,7 @@ def test_adapter_rejects_non_mapping_env(codex_payload, env): ), ], ) -def test_process_launch_failures_return_structured_results( - codex_payload, monkeypatch, error, message, returncode -): +def test_process_launch_failures_return_structured_results(codex_payload, monkeypatch, error, message, returncode): adapter = load_codex_adapter() monkeypatch.setattr(adapter.subprocess, "run", MagicMock(side_effect=error)) @@ -696,9 +693,7 @@ def test_adapter_rejects_invalid_timeout(codex_payload, timeout): adapter.run_codex(codex_payload) -def test_runtime_fails_if_codex_does_not_return_thread_identity( - codex_payload, monkeypatch -): +def test_runtime_fails_if_codex_does_not_return_thread_identity(codex_payload, monkeypatch): adapter = load_codex_adapter() mock_run = MagicMock( return_value=subprocess.CompletedProcess( @@ -779,17 +774,15 @@ async def test_fabric_oneshot_uses_cached_codex_auth(tmp_path): ) assert report.status == "pass" - assert any( - check.name == "requirement.binary" and "codex_command" in check.message - for check in report.checks - ) + assert any(check.name == "requirement.binary" and "codex_command" in check.message for check in report.checks) assert not any(check.name == "requirement.env" for check in report.checks) assert result.output["response"] == "thread-fake:inspect" assert "--ephemeral" not in result.output["command"] def test_codex_profile_resolves_runtime_adapter(): - from examples.code_review_agent import BASE_DIR, codex_cli_config + from examples.code_review_agent import BASE_DIR + from examples.code_review_agent import codex_cli_config plan = Fabric().plan( codex_cli_config(), diff --git a/tests/adapters/test_deepagents.py b/tests/adapters/test_deepagents.py index cf0ef2408..1aee5dd03 100644 --- a/tests/adapters/test_deepagents.py +++ b/tests/adapters/test_deepagents.py @@ -16,7 +16,8 @@ import types from pathlib import Path from typing import Any -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock +from unittest.mock import MagicMock import pytest @@ -132,7 +133,6 @@ def make(tmp_path: Path, *, runtime_id: str = "run-1") -> dict[str, Any]: "api_key_env": "NVIDIA_API_KEY", } }, - "telemetry": {"enabled": False}, }, }, "runtime_context": { @@ -278,9 +278,18 @@ async def test_relay_telemetry_wraps_agent_and_reports_artifacts( ) monkeypatch.setattr(adapter.common_utils, "relay_api_plugin_config", lambda _c: object()) monkeypatch.setattr(adapter.common_utils, "collect_relay_artifacts", lambda _c: artifacts) - monkeypatch.setenv("FABRIC_RELAY_ENABLED", "true") + payload = make_payload(tmp_path) + payload["telemetry_plan"] = { + "providers": ["relay"], + "relay_enabled": True, + "relay_project": None, + "relay_output_dir": None, + "relay_config": {}, + "native_config": None, + "adapter_outputs": [], + } - output = await adapter.run_deepagents(make_payload(tmp_path)) + output = await adapter.run_deepagents(payload) assert fake_relay["wrapped"] assert fake_relay["plugin_open"] @@ -300,10 +309,13 @@ async def test_native_telemetry_exports_without_artifacts( monkeypatch.setattr(adapter.common_utils, "relay_api_plugin_config", lambda _c: object()) payload = make_payload(tmp_path) - payload["effective_config"]["config"]["telemetry"] = { - "enabled": True, - "provider": "native", - "config": { + payload["telemetry_plan"] = { + "providers": ["native"], + "relay_enabled": False, + "relay_project": None, + "relay_output_dir": None, + "relay_config": None, + "native_config": { "version": 1, "components": [ { @@ -319,6 +331,7 @@ async def test_native_telemetry_exports_without_artifacts( } ], }, + "adapter_outputs": [], } output = await adapter.run_deepagents(payload) @@ -457,7 +470,10 @@ async def test_real_langgraph_async_checkpointer(tmp_path, make_payload, monkeyp import deepagents from langchain_core.messages import AIMessage - from langgraph.graph import END, START, MessagesState, StateGraph + from langgraph.graph import END + from langgraph.graph import START + from langgraph.graph import MessagesState + from langgraph.graph import StateGraph def respond(_state): return {"messages": [AIMessage(content="ok")]} diff --git a/tests/adapters/test_hermes_cli.py b/tests/adapters/test_hermes_cli.py index 30901ce66..1cd1d1f15 100644 --- a/tests/adapters/test_hermes_cli.py +++ b/tests/adapters/test_hermes_cli.py @@ -51,9 +51,8 @@ async def test_hermes_cli_rejects_native_telemetry( """schema_version: fabric.profile/v1alpha1 name: native_telemetry telemetry: - enabled: true - provider: native - config: {} + providers: + native: {} """, encoding="utf-8", ) @@ -93,7 +92,7 @@ async def test_hermes_cli_multi_turn( session_db = hermes_state.SessionDB(db_path=session_db_path) session = session_db.get_session_by_title(runtime_id) assert session is not None - assert session['id'] == runtime_id - assert session['model'] == 'test-model' - assert session['source'] == 'fabric' - assert session['title'] == runtime_id + assert session["id"] == runtime_id + assert session["model"] == "test-model" + assert session["source"] == "fabric" + assert session["title"] == runtime_id diff --git a/tests/adapters/test_hermes_sdk_adapter.py b/tests/adapters/test_hermes_sdk_adapter.py index ae1b62ebe..eca0b643d 100644 --- a/tests/adapters/test_hermes_sdk_adapter.py +++ b/tests/adapters/test_hermes_sdk_adapter.py @@ -23,9 +23,17 @@ async def test_hermes_sdk_rejects_native_telemetry(): payload = { - "effective_config": { - "config": {"telemetry": {"enabled": True, "provider": "native"}} - } + "effective_config": {"config": {}}, + "runtime_context": {"runtime_id": "runtime-1", "invocation_id": "invocation-1"}, + "telemetry_plan": { + "providers": ["native"], + "relay_enabled": False, + "relay_project": None, + "relay_output_dir": None, + "relay_config": None, + "native_config": {}, + "adapter_outputs": [], + }, } with pytest.raises(ValueError, match="only relay telemetry is supported for Hermes"): diff --git a/tests/e2e/test_cli.py b/tests/e2e/test_cli.py index 2a5e33d54..5526e4065 100644 --- a/tests/e2e/test_cli.py +++ b/tests/e2e/test_cli.py @@ -9,7 +9,8 @@ import subprocess from pathlib import Path -from _utils.utils import assert_relay_disabled_native_observability, run_fabric_cli +from _utils.utils import assert_relay_disabled_native_observability +from _utils.utils import run_fabric_cli def test_cli( @@ -76,12 +77,12 @@ def test_cli( assert "mode" not in profile_plan["config"]["runtime"] assert profile_plan["capability_plan"]["native"]["skill_paths"] assert "github" in profile_plan["capability_plan"]["native"]["mcp_servers"] - telemetry_plan = profile_plan["telemetry_plan"] - assert telemetry_plan["relay_enabled"] is relay_enabled if relay_enabled: + telemetry_plan = profile_plan["telemetry_plan"] + assert telemetry_plan["relay_enabled"] is True assert telemetry_plan["relay_output_dir"] else: - assert not telemetry_plan.get("relay_output_dir") + assert profile_plan.get("telemetry_plan") is None multi_plan = call_json( "plan", diff --git a/tests/fixtures/file-config-agent/agent.yaml b/tests/fixtures/file-config-agent/agent.yaml index 9e9f640e7..2124b3a61 100644 --- a/tests/fixtures/file-config-agent/agent.yaml +++ b/tests/fixtures/file-config-agent/agent.yaml @@ -41,8 +41,7 @@ mcp: url: ${GITHUB_MCP_URL} exposure: harness_native -telemetry: - enabled: false +telemetry: null profiles: directories: diff --git a/tests/fixtures/file-config-agent/profiles/codex-cli.yaml b/tests/fixtures/file-config-agent/profiles/codex-cli.yaml index 8bf2848ee..4b7582de4 100644 --- a/tests/fixtures/file-config-agent/profiles/codex-cli.yaml +++ b/tests/fixtures/file-config-agent/profiles/codex-cli.yaml @@ -34,5 +34,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/codex-cli -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/env-local.yaml b/tests/fixtures/file-config-agent/profiles/env-local.yaml index 809940f88..fe770a7b6 100644 --- a/tests/fixtures/file-config-agent/profiles/env-local.yaml +++ b/tests/fixtures/file-config-agent/profiles/env-local.yaml @@ -10,5 +10,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/local -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml b/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml index 51ee15188..d0603fd67 100644 --- a/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml +++ b/tests/fixtures/file-config-agent/profiles/env-opensandbox.yaml @@ -15,15 +15,12 @@ environment: image: nvcr.io/nvidia/nemo/fabric-hermes:latest telemetry: - enabled: true + providers: + relay: {} + +relay: output_dir: ./artifacts/opensandbox - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - atif: - enabled: true - output_directory: ./artifacts/opensandbox + observability: + atif: + enabled: true + output_directory: ./artifacts/opensandbox diff --git a/tests/fixtures/file-config-agent/profiles/hermes-cli.yaml b/tests/fixtures/file-config-agent/profiles/hermes-cli.yaml index b5ccbf8f5..91c20725d 100644 --- a/tests/fixtures/file-config-agent/profiles/hermes-cli.yaml +++ b/tests/fixtures/file-config-agent/profiles/hermes-cli.yaml @@ -26,5 +26,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/hermes-cli -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/hermes-sdk.yaml b/tests/fixtures/file-config-agent/profiles/hermes-sdk.yaml index 814cab828..571aaf587 100644 --- a/tests/fixtures/file-config-agent/profiles/hermes-sdk.yaml +++ b/tests/fixtures/file-config-agent/profiles/hermes-sdk.yaml @@ -30,5 +30,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/hermes-sdk -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/file-config-agent/profiles/mcp-github.yaml b/tests/fixtures/file-config-agent/profiles/mcp-github.yaml index 20d6f8245..b45bbb80b 100644 --- a/tests/fixtures/file-config-agent/profiles/mcp-github.yaml +++ b/tests/fixtures/file-config-agent/profiles/mcp-github.yaml @@ -13,21 +13,18 @@ mcp: exposure: fabric_managed telemetry: - enabled: true + providers: + relay: {} + +relay: output_dir: ./artifacts/mcp-github - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - atif: - enabled: true - output_directory: ./artifacts/mcp-github - filename_template: "trajectory-{session_id}.atif.json" - atof: - enabled: true - output_directory: ./artifacts/mcp-github - filename: events.atof.jsonl - mode: overwrite + observability: + atif: + enabled: true + output_directory: ./artifacts/mcp-github + filename_template: "trajectory-{session_id}.atif.json" + atof: + enabled: true + output_directory: ./artifacts/mcp-github + filename: events.atof.jsonl + mode: overwrite diff --git a/tests/fixtures/file-config-agent/profiles/native-otel.yaml b/tests/fixtures/file-config-agent/profiles/native-otel.yaml index f943b4d9b..e49a1ee3a 100644 --- a/tests/fixtures/file-config-agent/profiles/native-otel.yaml +++ b/tests/fixtures/file-config-agent/profiles/native-otel.yaml @@ -6,18 +6,18 @@ name: native_otel description: Test native telemetry file-profile resolution. telemetry: - enabled: true - provider: native - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - opentelemetry: + providers: + native: + config: + version: 1 + components: + - kind: observability enabled: true - transport: http_binary - endpoint: http://localhost:4318/v1/traces - resource_attributes: - deployment.environment: dev + config: + version: 1 + opentelemetry: + enabled: true + transport: http_binary + endpoint: http://localhost:4318/v1/traces + resource_attributes: + deployment.environment: dev diff --git a/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml b/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml index d52878669..0191fcb9a 100644 --- a/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml +++ b/tests/fixtures/file-config-agent/profiles/relay-openinference.yaml @@ -6,28 +6,24 @@ name: relay_openinference description: Test Relay OpenInference file-profile resolution. telemetry: - enabled: true - provider: relay + providers: + relay: {} + +relay: output_dir: ./artifacts/relay-openinference - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - atif: - enabled: true - output_directory: ./artifacts/relay-openinference - filename_template: "trajectory-{session_id}.atif.json" - agent_name: code-review-agent - agent_version: fabric-poc - atof: - enabled: true - output_directory: ./artifacts/relay-openinference - filename: events.atof.jsonl - mode: overwrite - openinference: - enabled: true - transport: http_binary - endpoint: http://localhost:6006/v1/traces + observability: + atif: + enabled: true + output_directory: ./artifacts/relay-openinference + filename_template: "trajectory-{session_id}.atif.json" + agent_name: code-review-agent + agent_version: fabric-poc + atof: + enabled: true + output_directory: ./artifacts/relay-openinference + filename: events.atof.jsonl + mode: overwrite + openinference: + enabled: true + transport: http_binary + endpoint: http://localhost:6006/v1/traces diff --git a/tests/fixtures/file-config-agent/profiles/relay-otel.yaml b/tests/fixtures/file-config-agent/profiles/relay-otel.yaml index 8125199f7..662f7d48e 100644 --- a/tests/fixtures/file-config-agent/profiles/relay-otel.yaml +++ b/tests/fixtures/file-config-agent/profiles/relay-otel.yaml @@ -6,24 +6,20 @@ name: relay_otel description: Test Relay OpenTelemetry file-profile resolution. telemetry: - enabled: true - provider: relay + providers: + relay: {} + +relay: output_dir: ./artifacts/relay-otel - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - opentelemetry: - enabled: true - transport: http_binary - endpoint: http://localhost:4318/v1/traces - service_name: code-review-agent - service_namespace: fabric - service_version: fabric-poc - instrumentation_scope: nemo-relay-otel - timeout_millis: 3000 - resource_attributes: - deployment.environment: dev + observability: + opentelemetry: + enabled: true + transport: http_binary + endpoint: http://localhost:4318/v1/traces + service_name: code-review-agent + service_namespace: fabric + service_version: fabric-poc + instrumentation_scope: nemo-relay-otel + timeout_millis: 3000 + resource_attributes: + deployment.environment: dev diff --git a/tests/fixtures/file-config-agent/profiles/relay.yaml b/tests/fixtures/file-config-agent/profiles/relay.yaml index c28f8a78d..9550bbd80 100644 --- a/tests/fixtures/file-config-agent/profiles/relay.yaml +++ b/tests/fixtures/file-config-agent/profiles/relay.yaml @@ -6,24 +6,20 @@ name: relay description: Test Relay ATOF/ATIF file-profile resolution. telemetry: - enabled: true - provider: relay + providers: + relay: {} + +relay: output_dir: ./artifacts/relay - config: - version: 1 - components: - - kind: observability - enabled: true - config: - version: 1 - atif: - enabled: true - output_directory: ./artifacts/relay - filename_template: "trajectory-{session_id}.atif.json" - agent_name: code-review-agent - agent_version: fabric-poc - atof: - enabled: true - output_directory: ./artifacts/relay - filename: events.atof.jsonl - mode: overwrite + observability: + atif: + enabled: true + output_directory: ./artifacts/relay + filename_template: "trajectory-{session_id}.atif.json" + agent_name: code-review-agent + agent_version: fabric-poc + atof: + enabled: true + output_directory: ./artifacts/relay + filename: events.atof.jsonl + mode: overwrite diff --git a/tests/fixtures/hermes-cli-agent/agent.yaml b/tests/fixtures/hermes-cli-agent/agent.yaml index a7fb5d9af..595b76046 100644 --- a/tests/fixtures/hermes-cli-agent/agent.yaml +++ b/tests/fixtures/hermes-cli-agent/agent.yaml @@ -44,8 +44,7 @@ mcp: url: ${GITHUB_MCP_URL} exposure: harness_native -telemetry: - enabled: false +telemetry: null profiles: directories: diff --git a/tests/fixtures/hermes-cli-agent/profiles/env-local.yaml b/tests/fixtures/hermes-cli-agent/profiles/env-local.yaml index 20c58f1b5..4b7c9eec3 100644 --- a/tests/fixtures/hermes-cli-agent/profiles/env-local.yaml +++ b/tests/fixtures/hermes-cli-agent/profiles/env-local.yaml @@ -10,5 +10,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/hermes-cli -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/hermes-shim-agent/agent.yaml b/tests/fixtures/hermes-shim-agent/agent.yaml index 85e735d18..92b17d861 100644 --- a/tests/fixtures/hermes-shim-agent/agent.yaml +++ b/tests/fixtures/hermes-shim-agent/agent.yaml @@ -40,8 +40,7 @@ mcp: url: ${GITHUB_MCP_URL} exposure: harness_native -telemetry: - enabled: false +telemetry: null profiles: directories: diff --git a/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml b/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml index 9bc6a0776..3ad46b047 100644 --- a/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml +++ b/tests/fixtures/hermes-shim-agent/profiles/env-local.yaml @@ -10,5 +10,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/local -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml b/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml index e09880b11..ea1d60aac 100644 --- a/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml +++ b/tests/fixtures/hermes-shim-agent/profiles/harbor-swebench-django-13741.yaml @@ -28,5 +28,4 @@ environment: source: harbor_swebench instance_id: django__django-13741 -telemetry: - enabled: false +telemetry: null diff --git a/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml b/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml index 4009fc365..a5e9896c7 100644 --- a/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml +++ b/tests/fixtures/hermes-shim-agent/profiles/mcp-github.yaml @@ -13,5 +13,8 @@ mcp: exposure: harness_native telemetry: - enabled: true + providers: + relay: {} + +relay: output_dir: ./artifacts/relay diff --git a/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml b/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml index bb6eb51d2..850a5661d 100644 --- a/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml +++ b/tests/fixtures/hermes-shim-agent/profiles/swebench-shim.yaml @@ -27,5 +27,4 @@ environment: workspace: ./repos/my-service artifacts: ./artifacts/swebench -telemetry: - enabled: false +telemetry: null diff --git a/tests/integrations/test_harbor_runner.py b/tests/integrations/test_harbor_runner.py index a31d10d50..793f2c678 100644 --- a/tests/integrations/test_harbor_runner.py +++ b/tests/integrations/test_harbor_runner.py @@ -19,32 +19,10 @@ DEMO_HOST_GATEWAY = DEMO_ROOT / "host-gateway.compose.yaml" DEMO_SOLUTION = DEMO_ROOT / "task" / "solution" / "solve.sh" DEMO_CONFIGS = DEMO_ROOT / "task" / "environment" / "fabric" / "configs" -CODEX_CONFIG = ( - DEMO_ROOT - / "task" - / "environment" - / "fabric" - / "configs" - / "codex.yaml" -) -RELAY_CONFIG = ( - DEMO_ROOT - / "task" - / "environment" - / "fabric" - / "configs" - / "hermes-relay.yaml" -) +CODEX_CONFIG = DEMO_ROOT / "task" / "environment" / "fabric" / "configs" / "codex.yaml" +RELAY_CONFIG = DEMO_ROOT / "task" / "environment" / "fabric" / "configs" / "hermes-relay.yaml" INTEGRATION_README = ROOT / "examples" / "harbor" / "README.md" -SDK_INTEGRATION_README = ( - ROOT - / "python" - / "src" - / "nemo_fabric" - / "integrations" - / "harbor" - / "README.md" -) +SDK_INTEGRATION_README = ROOT / "python" / "src" / "nemo_fabric" / "integrations" / "harbor" / "README.md" HARBOR_PACKAGE_INIT = SDK_INTEGRATION_README.parent / "__init__.py" @@ -59,8 +37,10 @@ def load_codex_adapter(): def test_runner_composes_harbor_values_on_an_independent_config(tmp_path): from nemo_fabric import RunRequest - from nemo_fabric.integrations.harbor.models import HarborMcpServer, HarborRunSpec - from nemo_fabric.integrations.harbor.runner import compose_config, load_config + from nemo_fabric.integrations.harbor.models import HarborMcpServer + from nemo_fabric.integrations.harbor.models import HarborRunSpec + from nemo_fabric.integrations.harbor.runner import compose_config + from nemo_fabric.integrations.harbor.runner import load_config config_path = tmp_path / "agent.yaml" config_path.write_text( @@ -122,13 +102,12 @@ def test_runner_composes_harbor_values_on_an_independent_config(tmp_path): assert config.mcp.servers["local"].extra_fields["args"] == ["--stdio"] assert config.skills is not None assert config.skills.paths == [str(tmp_path / "skills")] - assert json.loads(json.dumps(config.to_mapping()))["metadata"]["name"] == ( - "harbor-demo" - ) + assert json.loads(json.dumps(config.to_mapping()))["metadata"]["name"] == ("harbor-demo") def test_runner_preserves_config_capabilities_without_harbor_replacements(tmp_path): - from nemo_fabric import FabricConfig, RunRequest + from nemo_fabric import FabricConfig + from nemo_fabric import RunRequest from nemo_fabric.integrations.harbor.models import HarborRunSpec from nemo_fabric.integrations.harbor.runner import compose_config @@ -176,10 +155,10 @@ def test_runner_rejects_malformed_config(tmp_path): def test_harbor_transport_models_validate_mcp_targets(): - from pydantic import ValidationError - from nemo_fabric import RunRequest - from nemo_fabric.integrations.harbor.models import HarborMcpServer, HarborRunSpec + from nemo_fabric.integrations.harbor.models import HarborMcpServer + from nemo_fabric.integrations.harbor.models import HarborRunSpec + from pydantic import ValidationError spec = HarborRunSpec.model_validate_json( HarborRunSpec( @@ -328,9 +307,7 @@ def test_codex_adapter_maps_fabric_request_to_cli(tmp_path): "--skip-git-repo-check", "-", ] - assert tomllib.loads(profile_path.read_text(encoding="utf-8")) == { - "model_reasoning_effort": "high" - } + assert tomllib.loads(profile_path.read_text(encoding="utf-8")) == {"model_reasoning_effort": "high"} assert adapter.resolve_cwd(payload) == tmp_path @@ -344,7 +321,7 @@ def test_codex_demo_uses_current_adapter_contract(): assert settings["skip_git_repo_check"] is True assert settings["config_overrides"]["model_reasoning_effort"] == "high" dockerfile = DEMO_DOCKERFILE.read_text(encoding="utf-8") - assert 'nemo-fabric[codex,harbor,hermes,relay,runtime]' in dockerfile + assert "nemo-fabric[codex,harbor,hermes,relay,runtime]" in dockerfile assert "@openai/codex@0.142.4" in dockerfile @@ -366,9 +343,11 @@ def test_harbor_demo_uses_complete_configs_without_profiles(): def test_harbor_smoke_config_resolves_its_local_adapter(): - from nemo_fabric import Fabric, RunRequest + from nemo_fabric import Fabric + from nemo_fabric import RunRequest from nemo_fabric.integrations.harbor.models import HarborRunSpec - from nemo_fabric.integrations.harbor.runner import compose_config, load_config + from nemo_fabric.integrations.harbor.runner import compose_config + from nemo_fabric.integrations.harbor.runner import load_config config_path = DEMO_CONFIGS / "smoke.yaml" spec = HarborRunSpec(config_path=config_path, request=RunRequest(input="fix it")) @@ -416,7 +395,8 @@ def test_harbor_demo_setup_and_solution_fail_fast(): def test_harbor_telemetry_demo_exports_phoenix_atof_and_atif(): config = yaml.safe_load(RELAY_CONFIG.read_text(encoding="utf-8")) host_gateway = yaml.safe_load(DEMO_HOST_GATEWAY.read_text(encoding="utf-8")) - observability = config["telemetry"]["config"]["components"][0]["config"] + assert "relay" in config["telemetry"]["providers"] + observability = config["relay"]["observability"] demo = DEMO_README.read_text(encoding="utf-8") assert observability["openinference"] == { diff --git a/tests/python/test_code_review_example.py b/tests/python/test_code_review_example.py index ab3a911a7..063127d1a 100644 --- a/tests/python/test_code_review_example.py +++ b/tests/python/test_code_review_example.py @@ -6,23 +6,24 @@ import json import subprocess import sys -from unittest.mock import AsyncMock, MagicMock +from unittest.mock import AsyncMock +from unittest.mock import MagicMock +from examples.code_review_agent import BASE_DIR from examples.code_review_agent import __main__ as main_module -from examples.code_review_agent import ( - BASE_DIR, - base_config, - codex_cli_config, - hermes_cli_config, - hermes_sdk_config, - with_fabric_managed_github_mcp, - with_native_otel, - with_opensandbox, - with_relay, - with_relay_openinference, - with_relay_otel, -) -from nemo_fabric import Fabric, FabricConfig, RunOutput +from examples.code_review_agent import base_config +from examples.code_review_agent import codex_cli_config +from examples.code_review_agent import hermes_cli_config +from examples.code_review_agent import hermes_sdk_config +from examples.code_review_agent import with_fabric_managed_github_mcp +from examples.code_review_agent import with_native_otel +from examples.code_review_agent import with_opensandbox +from examples.code_review_agent import with_relay +from examples.code_review_agent import with_relay_openinference +from examples.code_review_agent import with_relay_otel +from nemo_fabric import Fabric +from nemo_fabric import FabricConfig +from nemo_fabric import RunOutput def test_variant_builders_return_independent_complete_configs(): @@ -59,7 +60,7 @@ def test_capability_and_telemetry_variants_do_not_mutate_their_input(): ) assert base.telemetry is not None - assert base.telemetry.enabled is False + assert base.telemetry.providers == {} assert base.environment is not None assert base.environment.provider == "local" assert base.mcp is not None @@ -68,11 +69,11 @@ def test_capability_and_telemetry_variants_do_not_mutate_their_input(): assert variants[0].mcp is not None assert variants[0].mcp.servers["github"].exposure == "fabric_managed" assert variants[1].telemetry is not None - assert variants[1].telemetry.provider == "native" + assert "native" in variants[1].telemetry.providers assert variants[2].environment is not None assert variants[2].environment.provider == "opensandbox" assert variants[3].telemetry is not None - assert variants[3].telemetry.provider == "relay" + assert "relay" in variants[3].telemetry.providers def test_variants_plan_without_file_profiles(): @@ -112,10 +113,12 @@ def test_example_entrypoint_plans_without_starting_a_runtime(): plan = json.loads(completed.stdout) assert plan["agent_name"] == "code-review-agent" assert plan["profiles"] == [] - assert ( - plan["adapter_descriptor"]["descriptor"]["adapter_id"] == adapter_id - ) - assert plan["telemetry_plan"]["relay_enabled"] is relay_enabled + assert plan["adapter_descriptor"]["descriptor"]["adapter_id"] == adapter_id + telemetry_plan = plan.get("telemetry_plan") + if relay_enabled: + assert telemetry_plan["relay_enabled"] is True + else: + assert telemetry_plan is None async def test_example_entrypoint_shows_response_after_normalized_output( diff --git a/tests/python/test_native_sdk.py b/tests/python/test_native_sdk.py index fe29aba20..93829c7c6 100644 --- a/tests/python/test_native_sdk.py +++ b/tests/python/test_native_sdk.py @@ -11,8 +11,12 @@ import nemo_fabric._native as native import pytest -from examples.code_review_agent import BASE_DIR, base_config -from nemo_fabric import Fabric, FabricConfig, FabricProfileConfig, FabricRuntimeError +from examples.code_review_agent import BASE_DIR +from examples.code_review_agent import base_config +from nemo_fabric import Fabric +from nemo_fabric import FabricConfig +from nemo_fabric import FabricProfileConfig +from nemo_fabric import FabricRuntimeError async def test_native_sdk(hermes_shim_agent_dir: Path): @@ -116,7 +120,7 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: } } }, - "telemetry": {"enabled": False}, + "telemetry": None, "consumer_extension": { "base": True, "nested": {"first": 1}, @@ -126,7 +130,8 @@ async def smoke(client: Fabric, fixture_agent: Path) -> None: typed_profile = FabricProfileConfig( name="typed_relay", harness={"settings": {"timeout_seconds": 30}}, - telemetry={"enabled": True, "output_dir": "./artifacts/relay"}, + telemetry={"providers": {"relay": {}}}, + relay={"output_dir": "./artifacts/relay"}, consumer_extension={ "profile": True, "nested": {"second": 2}, diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index 9019743a3..cc5e166b0 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -8,42 +8,45 @@ import json from inspect import signature from pathlib import Path -from typing import Any, get_overloads - -import pytest -from pydantic import ValidationError +from typing import Any +from typing import get_overloads import nemo_fabric import nemo_fabric.errors as fabric_errors - -from nemo_fabric import ( - AdapterInfo, - DoctorReport, - EffectiveConfig, - EnvironmentConfig, - Fabric, - FabricCapabilityError, - FabricConfig, - FabricConfigError, - FabricError, - FabricProfileConfig, - FabricNativeUnavailableError, - FabricRuntimeError, - FabricStateError, - HarnessConfig, - McpConfig, - MetadataConfig, - RunOutput, - RunPlan, - RunRequest, - RunResult, - RuntimeCapabilities, - RuntimeConfig, - Runtime, - RuntimeHandle, - SkillConfig, - TelemetryConfig, -) +import pytest +from nemo_fabric import AdapterInfo +from nemo_fabric import DoctorReport +from nemo_fabric import EffectiveConfig +from nemo_fabric import EnvironmentConfig +from nemo_fabric import Fabric +from nemo_fabric import FabricCapabilityError +from nemo_fabric import FabricConfig +from nemo_fabric import FabricConfigError +from nemo_fabric import FabricError +from nemo_fabric import FabricNativeUnavailableError +from nemo_fabric import FabricProfileConfig +from nemo_fabric import FabricRuntimeError +from nemo_fabric import FabricStateError +from nemo_fabric import HarnessConfig +from nemo_fabric import McpConfig +from nemo_fabric import MetadataConfig +from nemo_fabric import RelayAtifConfig +from nemo_fabric import RelayAtofConfig +from nemo_fabric import RelayComponentConfig +from nemo_fabric import RelayConfigPolicy +from nemo_fabric import RelayObservabilityConfig +from nemo_fabric import RunOutput +from nemo_fabric import RunPlan +from nemo_fabric import RunRequest +from nemo_fabric import RunResult +from nemo_fabric import Runtime +from nemo_fabric import RuntimeCapabilities +from nemo_fabric import RuntimeConfig +from nemo_fabric import RuntimeHandle +from nemo_fabric import SkillConfig +from nemo_fabric import TelemetryConfig +from nemo_fabric.types import _ResolvedFabricConfig +from pydantic import ValidationError def test_public_contract_has_no_unreleased_aliases(): @@ -137,7 +140,6 @@ def test_typed_config_authoring_helpers_emit_schema_shape(): config.enable_relay( project="fabric-tests", output_dir="./artifacts/relay", - config={"version": 1}, ) assert isinstance(config.mcp, McpConfig) @@ -155,11 +157,12 @@ def test_typed_config_authoring_helpers_emit_schema_shape(): } } assert config.to_mapping()["telemetry"] == { - "enabled": True, - "provider": "relay", + "providers": {"relay": {}}, + } + assert config.to_mapping()["relay"] == { "project": "fabric-tests", "output_dir": "./artifacts/relay", - "config": {"version": 1}, + "components": [], } config.remove_mcp_server("github").remove_mcp_server("missing") @@ -176,8 +179,80 @@ def test_typed_config_authoring_helpers_emit_schema_shape(): url="http://example.invalid", exposure="sideways", ) - with pytest.raises(ValidationError, match="provider"): - TelemetryConfig(provider="sideways") + with pytest.raises(ValidationError, match="providers"): + TelemetryConfig(providers={"sideways": {}}) + + +def test_fabric_config_authors_first_class_relay_observability(): + config = _fabric_config() + + config.enable_relay( + output_dir="./artifacts/relay", + observability=RelayObservabilityConfig( + atof=RelayAtofConfig( + enabled=True, + output_directory="./artifacts/relay", + filename="events.atof.jsonl", + mode="overwrite", + ), + atif=RelayAtifConfig( + enabled=True, + output_directory="./artifacts/relay", + filename_template="trajectory-{session_id}.atif.json", + agent_name="fabric-tests", + ), + ), + components=[ + RelayComponentConfig(kind="switchyard", config={"route": "canary"}), + ], + policy=RelayConfigPolicy(unknown_component="error"), + ) + + assert config.to_mapping()["telemetry"] == { + "providers": {"relay": {}}, + } + assert config.to_mapping()["relay"] == { + "output_dir": "./artifacts/relay", + "observability": { + "version": 1, + "atof": { + "enabled": True, + "output_directory": "./artifacts/relay", + "filename": "events.atof.jsonl", + "mode": "overwrite", + }, + "atif": { + "enabled": True, + "agent_name": "fabric-tests", + "model_name": "unknown", + "output_directory": "./artifacts/relay", + "filename_template": "trajectory-{session_id}.atif.json", + }, + }, + "components": [ + { + "kind": "switchyard", + "enabled": True, + "config": {"route": "canary"}, + }, + ], + "policy": { + "unknown_component": "error", + "unknown_field": "warn", + "unsupported_value": "error", + }, + } + + +def test_telemetry_config_enable_native_preserves_existing_config(): + telemetry = TelemetryConfig() + + telemetry.enable_native(config={"components": [{"kind": "observability"}]}) + telemetry.enable_native() + + assert telemetry.to_mapping()["providers"]["native"]["config"] == { + "components": [{"kind": "observability"}], + } def test_config_emits_schema_shape_and_validates(): @@ -211,7 +286,12 @@ def test_config_emits_schema_shape_and_validates(): assert emitted["models"]["default"]["model"] == "test-model" assert emitted["skills"] == {"paths": ["./skills/review"]} assert emitted["mcp"]["servers"]["github"]["exposure"] == "fabric_managed" - assert emitted["telemetry"]["provider"] == "relay" + assert emitted["telemetry"]["providers"] == {"relay": {}} + assert emitted["relay"] == { + "project": "fabric-tests", + "output_dir": "./artifacts/relay", + "components": [], + } assert config.extra_fields == {"future_top_level": {"enabled": True}} normalized = FabricConfig.model_validate(config) @@ -359,6 +439,33 @@ def test_run_plan_requires_profiles(): RunPlan.from_mapping(raw) +def test_run_plan_config_enable_relay_preserves_existing_relay_fields(): + config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) + + config.enable_relay( + output_dir="./artifacts/relay", + observability={"atif": {"enabled": True}}, + ) + config.enable_relay(policy={"unknown_component": "error"}) + + assert config.to_mapping()["relay"] == { + "output_dir": "./artifacts/relay", + "observability": {"atif": {"enabled": True}}, + "policy": {"unknown_component": "error"}, + } + + +def test_run_plan_config_enable_native_preserves_existing_native_config(): + config = _ResolvedFabricConfig.from_mapping(_plan()["config"]) + + config.telemetry.enable_native(config={"components": [{"kind": "observability"}]}) + config.telemetry.enable_native() + + assert config.to_mapping()["telemetry"]["providers"]["native"]["config"] == { + "components": [{"kind": "observability"}], + } + + def test_runtime_capabilities_reject_non_boolean_values(): with pytest.raises(FabricConfigError, match="streaming capability"): RuntimeCapabilities.from_mapping({"streaming": "false"}) @@ -500,9 +607,7 @@ def resolve_config( base_dir: str | None = None, ) -> str: assert json.loads(config_json)["metadata"]["name"] == "demo" - self.config_profile_calls.append( - None if profiles_json is None else json.loads(profiles_json) - ) + self.config_profile_calls.append(None if profiles_json is None else json.loads(profiles_json)) return json.dumps(_plan()["effective_config"]) def plan_config( @@ -512,18 +617,14 @@ def plan_config( base_dir: str | None = None, ) -> str: assert json.loads(config_json)["metadata"]["name"] == "demo" - self.config_profile_calls.append( - None if profiles_json is None else json.loads(profiles_json) - ) + self.config_profile_calls.append(None if profiles_json is None else json.loads(profiles_json)) return json.dumps(_plan()) def start_runtime(self, plan_json: str) -> str: assert json.loads(plan_json)["agent_name"] == "demo" return json.dumps(_runtime()) - def invoke_runtime( - self, plan_json: str, runtime_json: str, request_json: str - ) -> str: + def invoke_runtime(self, plan_json: str, runtime_json: str, request_json: str) -> str: if self.fail_invoke: raise RuntimeError("native invoke failed") request = json.loads(request_json) @@ -590,9 +691,7 @@ def test_run_request_is_validated_and_json_safe(): overrides["limits"]["turns"] = 2 assert request.request_id == "request-1" - assert request.to_mapping()["input"] == { - "messages": [{"role": "user", "content": "hello"}] - } + assert request.to_mapping()["input"] == {"messages": [{"role": "user", "content": "hello"}]} assert request.to_mapping()["context"] == {"run_id": "run-1", "labels": ["sdk"]} assert request.to_mapping()["overrides"] == { "temperature": 0, @@ -654,9 +753,7 @@ def test_run_request_preserves_extension_fields(): future_request={"enabled": True}, ) - assert request.to_mapping()["input"] == { - "messages": [{"role": "user", "content": "hello"}] - } + assert request.to_mapping()["input"] == {"messages": [{"role": "user", "content": "hello"}]} assert request.context == {"job_id": "job-1"} assert request.extra_fields["future_request"] == {"enabled": True} @@ -820,7 +917,7 @@ def test_run_result_preserves_native_telemetry_provider(): _run_result( telemetry={ "relay_enabled": False, - "metadata": {"telemetry_provider": "native"}, + "metadata": {"telemetry_providers": ["native"]}, }, ) ) @@ -949,6 +1046,7 @@ async def test_runtime_invoke_accepts_run_request(): }, } + async def test_runtime_handle_is_typed_and_detached(): runtime = Runtime( client=NativeClient(NativeRecorder()), diff --git a/tests/python/test_typed_config.py b/tests/python/test_typed_config.py index 5e2537369..f96ca783b 100644 --- a/tests/python/test_typed_config.py +++ b/tests/python/test_typed_config.py @@ -23,14 +23,11 @@ from shutil import copytree import yaml - -from nemo_fabric import ( - Fabric, - FabricConfig, - FabricProfileConfig, - RunRequest, - RunResult, -) +from nemo_fabric import Fabric +from nemo_fabric import FabricConfig +from nemo_fabric import FabricProfileConfig +from nemo_fabric import RunRequest +from nemo_fabric import RunResult ROOT = Path(__file__).resolve().parents[2] COMMAND = ("cargo", "run", "-q", "-p", "fabric-cli", "--") @@ -67,7 +64,7 @@ def _repository_adapter_config() -> FabricConfig: "workspace": "./ws", "artifacts": "./artifacts/local", }, - "telemetry": {"enabled": False}, + "telemetry": None, } ) diff --git a/uv.lock b/uv.lock index 1c2a0a500..1675c5b83 100644 --- a/uv.lock +++ b/uv.lock @@ -2095,7 +2095,7 @@ requires-dist = [ { name = "nemo-fabric-adapters-hermes-sdk", marker = "extra == 'adapters-hermes-sdk'", editable = "adapters/hermes-sdk" }, { name = "nemo-fabric-adapters-hermes-sdk", marker = "extra == 'hermes'", editable = "adapters/hermes-sdk" }, { name = "nemo-fabric-runtime", marker = "extra == 'runtime'", editable = "python" }, - { name = "nemo-relay", marker = "extra == 'relay'", specifier = "~=0.4" }, + { name = "nemo-relay", marker = "extra == 'relay'", specifier = "~=0.5.0" }, { name = "pyyaml", marker = "extra == 'harbor'", specifier = ">=6.0" }, { name = "tomli-w", marker = "extra == 'relay'", specifier = "~=1.2" }, ] @@ -2188,7 +2188,7 @@ requires-dist = [ { name = "langgraph", specifier = ">=1.2,<2.0" }, { name = "langgraph-checkpoint-sqlite", specifier = ">=3.0,<4.0" }, { name = "nemo-fabric-adapters-common", editable = "adapters/common" }, - { name = "nemo-relay", specifier = "~=0.4" }, + { name = "nemo-relay", specifier = "~=0.5.0" }, ] [[package]] @@ -2239,14 +2239,14 @@ requires-dist = [ [[package]] name = "nemo-relay" -version = "0.4.0" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/f0/1626672dd86005570afbcd4e368f5f271bcd2104ceaee883e839b11bfc81/nemo_relay-0.4.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:d5f14fe5c8e5fbcc26827cc88e5b867f4ad37d6f983100cbf31705234c39f9d3", size = 6674344, upload-time = "2026-06-12T23:24:02.707Z" }, - { url = "https://files.pythonhosted.org/packages/c1/35/956142c558334ec8594d97da6a1a717e70ba43685659dbdf18965c87c3f5/nemo_relay-0.4.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a5a5f5dec1428085f6c41c3645f1773f9cfb154cfa47306c93d6514091a8006", size = 5989959, upload-time = "2026-06-12T23:24:04.496Z" }, - { url = "https://files.pythonhosted.org/packages/6c/56/e5172ec8486deb23dabf5c948be81b8172840efdfcda42b1da1a8d9ef969/nemo_relay-0.4.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f92883b81540076e4b6c5e754eb7726225336634204ebd22ff730ccbcfb2723", size = 6332992, upload-time = "2026-06-12T23:24:06.288Z" }, - { url = "https://files.pythonhosted.org/packages/e4/ea/f44b16ecfebc1de47e2d832e63d9cf56ad24952f09d6182d9fa9fa276b05/nemo_relay-0.4.0-cp311-abi3-win_amd64.whl", hash = "sha256:a5d02d9d0a2d19bf341bf9fd1faae01713bc2021a5b144ef79f91a07ec4c0bfb", size = 6332134, upload-time = "2026-06-12T23:24:08.017Z" }, - { url = "https://files.pythonhosted.org/packages/3a/45/eeedc7b7b5d2dea6be24f6ccca529abf3f97568d8dccff35de6430187ebe/nemo_relay-0.4.0-cp311-abi3-win_arm64.whl", hash = "sha256:dc1effd4052d1d47a1cdc553415c878a73d1e01c81a7e9f9194b30a3c88d1947", size = 5964432, upload-time = "2026-06-12T23:24:09.789Z" }, + { url = "https://files.pythonhosted.org/packages/aa/fc/6892859ffbfb60f8cc09a4181c8d407b574d49490e35cd563e7f0a61bf54/nemo_relay-0.5.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:203409e0f392fc4f40f52277347ed75ba9969b2af28ddb3b6ae53f789185a0ed", size = 7769721, upload-time = "2026-07-08T18:43:57.745Z" }, + { url = "https://files.pythonhosted.org/packages/dd/13/25c4fcf6fb979af07c9562238819183588a910c2ae09767603500d6948c2/nemo_relay-0.5.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de61ad0644c5e8c300de814f6884c5de7ae51db14694f5057f938b4da54bfaaf", size = 7037083, upload-time = "2026-07-08T18:44:00.135Z" }, + { url = "https://files.pythonhosted.org/packages/49/97/bd4c77e975d50f7d09f8bfcb74d7e704e05a8b2205f555bf557f84edc5fe/nemo_relay-0.5.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb323569f104506f4f1a0e2104b22d74c9a0bbf3280ec4d58ad3d768de76fb15", size = 7430817, upload-time = "2026-07-08T18:44:02.165Z" }, + { url = "https://files.pythonhosted.org/packages/da/8b/5681f3430e6d25bf7419dbe576da14599e1cfadc083edb74e691a0c3a980/nemo_relay-0.5.0-cp311-abi3-win_amd64.whl", hash = "sha256:da1bd1e1dec79b7bda3984dd8c59b32ba97abf96c37e85cd1f38d208a29f42b9", size = 7359048, upload-time = "2026-07-08T18:44:04.169Z" }, + { url = "https://files.pythonhosted.org/packages/4e/f2/65c3ef0435e671c829063d4872897f9054f369f0aeacab12c5aefe486705/nemo_relay-0.5.0-cp311-abi3-win_arm64.whl", hash = "sha256:87462585f17b40ce82c54347acef5ace96ee11de3015f234e0b902f23b3793fb", size = 6934235, upload-time = "2026-07-08T18:44:06.078Z" }, ] [[package]] From 4270bd0cc326e7435e426b53e2d79f2fcd38f6b0 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:22:55 -0700 Subject: [PATCH 2/3] fix(relay): address telemetry review feedback Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- adapters/codex-cli/fabric-adapter.json | 13 +- adapters/deepagents/fabric-adapter.json | 9 +- adapters/hermes-cli/README.md | 3 +- adapters/hermes-cli/fabric-adapter.json | 6 +- adapters/hermes-sdk/README.md | 3 +- adapters/hermes-sdk/fabric-adapter.json | 15 +- crates/fabric-core/src/config.rs | 167 ++++++++++++++++-- crates/fabric-core/src/lib.rs | 20 +-- crates/fabric-core/src/runtime.rs | 17 ++ .../config/enum-capabilitykind.mdx | 2 +- .../config/enum-capabilitytarget.mdx | 2 +- .../config/enum-controllocation.mdx | 2 +- .../config/enum-environmentownership.mdx | 2 +- .../config/enum-fabricdocument.mdx | 2 +- .../fabric-core/config/enum-mcpexposure.mdx | 2 +- .../config/enum-relayatifstorageconfig.mdx | 2 +- .../enum-relayatofendpointfieldnamepolicy.mdx | 2 +- .../enum-relayatofendpointtransport.mdx | 2 +- .../fabric-core/config/enum-relayatofmode.mdx | 2 +- .../config/enum-relayotlptransport.mdx | 2 +- .../config/enum-relayunsupportedbehavior.mdx | 2 +- .../config/enum-resolutionstrategy.mdx | 2 +- .../config/enum-telemetryprovider.mdx | 2 +- .../config/fn-load-adapter-descriptor.mdx | 2 +- .../config/fn-load-fabric-document.mdx | 2 +- ...n-resolve-effective-config-from-config.mdx | 2 +- ...resolve-effective-config-with-profiles.mdx | 2 +- .../config/fn-resolve-effective-config.mdx | 2 +- .../fn-resolve-run-plan-from-config.mdx | 2 +- ...resolve-run-plan-from-effective-config.mdx | 2 +- .../fn-resolve-run-plan-with-profiles.mdx | 2 +- .../config/fn-resolve-run-plan.mdx | 2 +- .../config/fn-validate-agent-directory.mdx | 2 +- .../fabric-core/config/index.mdx | 3 +- ...struct-adaptertelemetryprovidersupport.mdx | 110 ++++++++++++ .../config/struct-adaptertelemetrysupport.mdx | 8 +- .../config/struct-capabilityplan.mdx | 2 +- .../config/struct-capabilityroute.mdx | 2 +- .../config/struct-capabilitytargetplan.mdx | 2 +- .../config/struct-effectiveconfig.mdx | 2 +- .../config/struct-environmentconfig.mdx | 2 +- .../config/struct-environmentplan.mdx | 2 +- .../config/struct-fabricconfig.mdx | 2 +- .../config/struct-harnessconfig.mdx | 2 +- .../fabric-core/config/struct-mcpconfig.mdx | 2 +- .../config/struct-mcpserverconfig.mdx | 2 +- .../config/struct-mcpserverplan.mdx | 2 +- .../config/struct-metadataconfig.mdx | 2 +- .../fabric-core/config/struct-modelconfig.mdx | 2 +- .../config/struct-profileconfig.mdx | 2 +- .../config/struct-profileregistryconfig.mdx | 2 +- .../config/struct-relayatifconfig.mdx | 2 +- .../config/struct-relayatofconfig.mdx | 4 +- .../config/struct-relayatofendpointconfig.mdx | 2 +- .../config/struct-relaycomponentconfig.mdx | 2 +- .../fabric-core/config/struct-relayconfig.mdx | 2 +- .../config/struct-relayconfigpolicy.mdx | 2 +- .../struct-relayobservabilityconfig.mdx | 2 +- .../config/struct-relayotlpconfig.mdx | 2 +- .../config/struct-resolvecontext.mdx | 2 +- .../struct-resolvedadapterdescriptor.mdx | 2 +- .../fabric-core/config/struct-runplan.mdx | 2 +- .../config/struct-runtimecapabilities.mdx | 2 +- .../config/struct-runtimeconfig.mdx | 2 +- .../fabric-core/config/struct-skillconfig.mdx | 2 +- .../config/struct-telemetryconfig.mdx | 2 +- .../config/struct-telemetryplan.mdx | 2 +- .../config/struct-telemetryproviderconfig.mdx | 2 +- .../fabric-core/doctor/enum-doctorstatus.mdx | 2 +- .../fabric-core/doctor/fn-doctor-plan.mdx | 2 +- .../fabric-core/doctor/index.mdx | 2 +- .../fabric-core/doctor/struct-doctorcheck.mdx | 2 +- .../doctor/struct-doctorreport.mdx | 2 +- .../fabric-core/error/enum-fabricerror.mdx | 2 +- .../fabric-core/error/index.mdx | 2 +- .../fabric-core/error/type-result.mdx | 2 +- .../fabric-core/fn-version.mdx | 2 +- .../fabric-core/index.mdx | 1 + .../fabric-core/runtime/index.mdx | 2 +- .../fabric-core/schema/index.mdx | 2 +- python/src/nemo_fabric/models.py | 35 ++-- schemas/adapter-descriptor.schema.json | 29 ++- schemas/run-plan.schema.json | 29 ++- tests/python/test_sdk_contract.py | 31 +++- 84 files changed, 493 insertions(+), 142 deletions(-) create mode 100644 docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetryprovidersupport.mdx diff --git a/adapters/codex-cli/fabric-adapter.json b/adapters/codex-cli/fabric-adapter.json index 506d92455..e5d7045d4 100644 --- a/adapters/codex-cli/fabric-adapter.json +++ b/adapters/codex-cli/fabric-adapter.json @@ -11,6 +11,17 @@ "binaries": ["codex"] }, "config": { - "accepts": ["models"] + "accepts": ["models", "telemetry"] + }, + "telemetry": { + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"], + "integration_modes": ["hooks", "gateway"] + }, + "native": { + "outputs": ["otel"] + } + } } } diff --git a/adapters/deepagents/fabric-adapter.json b/adapters/deepagents/fabric-adapter.json index d6b47e318..c69e3b842 100644 --- a/adapters/deepagents/fabric-adapter.json +++ b/adapters/deepagents/fabric-adapter.json @@ -12,6 +12,13 @@ "accepts": ["models", "tools", "mcp", "skills", "telemetry"] }, "telemetry": { - "supports": ["relay", "atif", "otel", "openinference"] + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"] + }, + "native": { + "outputs": ["otel", "openinference"] + } + } } } diff --git a/adapters/hermes-cli/README.md b/adapters/hermes-cli/README.md index 43d270ee2..23d58f4bf 100644 --- a/adapters/hermes-cli/README.md +++ b/adapters/hermes-cli/README.md @@ -44,7 +44,8 @@ Keep `fabric-adapter.json` aligned with the Python implementation: - `requirements` powers `fabric doctor`; keep required env vars and the `hermes` binary requirement current. - `config.accepts` must match the Fabric sections this adapter maps into Hermes. -- `telemetry.supports` lists telemetry paths the adapter can produce or forward. +- `telemetry.providers` declares provider-specific outputs and integration modes + the adapter can produce or forward. When Hermes CLI flags or config files change, update the mapping code and the descriptor together. User-facing run variations belong in `agent.yaml` or diff --git a/adapters/hermes-cli/fabric-adapter.json b/adapters/hermes-cli/fabric-adapter.json index 7fb247045..249c30be6 100644 --- a/adapters/hermes-cli/fabric-adapter.json +++ b/adapters/hermes-cli/fabric-adapter.json @@ -15,6 +15,10 @@ "accepts": ["tools", "mcp", "skills", "telemetry"] }, "telemetry": { - "supports": ["relay", "atif", "otel", "openinference"] + "providers": { + "relay": { + "outputs": ["atif", "otel", "openinference"] + } + } } } diff --git a/adapters/hermes-sdk/README.md b/adapters/hermes-sdk/README.md index 9854d756c..65e642ca8 100644 --- a/adapters/hermes-sdk/README.md +++ b/adapters/hermes-sdk/README.md @@ -40,7 +40,8 @@ Keep `fabric-adapter.json` aligned with the Python implementation: - `requirements` powers `fabric doctor`; keep required env vars, binaries, or packages current. - `config.accepts` must match the Fabric sections this adapter maps into Hermes. -- `telemetry.supports` lists telemetry paths the adapter can produce or forward. +- `telemetry.providers` declares provider-specific outputs and integration modes + the adapter can produce or forward. Do not put end-user agent settings in this directory. Users vary harness, model, skills, MCP, tools, telemetry, and runtime behavior through `agent.yaml` diff --git a/adapters/hermes-sdk/fabric-adapter.json b/adapters/hermes-sdk/fabric-adapter.json index 78661afc1..ecd409348 100644 --- a/adapters/hermes-sdk/fabric-adapter.json +++ b/adapters/hermes-sdk/fabric-adapter.json @@ -21,11 +21,14 @@ ] }, "telemetry": { - "supports": [ - "relay", - "atif", - "otel", - "openinference" - ] + "providers": { + "relay": { + "outputs": [ + "atif", + "otel", + "openinference" + ] + } + } } } diff --git a/crates/fabric-core/src/config.rs b/crates/fabric-core/src/config.rs index 6b41f825e..d6de68be9 100644 --- a/crates/fabric-core/src/config.rs +++ b/crates/fabric-core/src/config.rs @@ -4,7 +4,7 @@ //! Fabric config models and loading helpers. use std::borrow::Cow; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::fs; use std::path::{Path, PathBuf}; @@ -361,14 +361,28 @@ pub struct AdapterConfigSupport { /// Adapter telemetry support. #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct AdapterTelemetrySupport { - /// Telemetry outputs supported by this adapter. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub supports: Vec, + /// Provider-specific telemetry capabilities supported by this adapter. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub providers: BTreeMap, /// Additive adapter telemetry fields. #[serde(default, flatten)] pub extensions: BTreeMap, } +/// Telemetry capabilities for one adapter-supported provider. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)] +pub struct AdapterTelemetryProviderSupport { + /// Telemetry outputs the adapter can produce or forward for this provider. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub outputs: Vec, + /// Integration modes implemented by the adapter for this provider. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub integration_modes: Vec, + /// Additive provider capability fields. + #[serde(default, flatten)] + pub extensions: BTreeMap, +} + /// Profile config applied on top of a Fabric config. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)] pub struct ProfileConfig { @@ -1295,7 +1309,7 @@ pub fn resolve_run_plan_from_effective_config( let capability_plan = resolve_capability_plan(&config, &config_root, adapter_descriptor.as_ref()); let capabilities = resolve_runtime_capabilities(&config, descriptor); - let telemetry_plan = resolve_telemetry_plan(&config, descriptor); + let telemetry_plan = resolve_telemetry_plan(&config, descriptor)?; Ok(RunPlan { agent_name: effective_config.agent_name.clone(), profiles: effective_config.profiles.clone(), @@ -1676,10 +1690,12 @@ fn mcp_exposure_name(exposure: McpExposure) -> &'static str { fn resolve_telemetry_plan( config: &FabricConfig, adapter_descriptor: Option<&AdapterDescriptor>, -) -> Option { - let telemetry = config.telemetry.as_ref()?; +) -> Result> { + let Some(telemetry) = config.telemetry.as_ref() else { + return Ok(None); + }; if telemetry.providers.is_empty() { - return None; + return Ok(None); } let relay_provider = telemetry.providers.get(&TelemetryProvider::Relay); let native_provider = telemetry.providers.get(&TelemetryProvider::Native); @@ -1688,8 +1704,30 @@ fn resolve_telemetry_plan( let providers = [TelemetryProvider::Relay, TelemetryProvider::Native] .into_iter() .filter(|provider| telemetry.providers.contains_key(provider)) - .collect(); - Some(TelemetryPlan { + .collect::>(); + if let Some(descriptor) = adapter_descriptor { + for provider in &providers { + if !descriptor.telemetry.providers.contains_key(provider) { + return Err(FabricError::AdapterDescriptorUnsupported { + adapter_id: descriptor.adapter_id.clone(), + field: "telemetry.providers", + value: provider.as_str().to_string(), + }); + } + } + } + let adapter_outputs = adapter_descriptor + .map(|descriptor| { + providers + .iter() + .filter_map(|provider| descriptor.telemetry.providers.get(provider)) + .flat_map(|support| support.outputs.iter().cloned()) + .collect::>() + .into_iter() + .collect() + }) + .unwrap_or_default(); + Ok(Some(TelemetryPlan { providers, relay_enabled, relay_project: relay_enabled @@ -1702,10 +1740,8 @@ fn resolve_telemetry_plan( .then(|| resolve_relay_plugin_config(relay)) .flatten(), native_config: native_provider.and_then(|provider| provider.config.clone()), - adapter_outputs: adapter_descriptor - .map(|descriptor| descriptor.telemetry.supports.clone()) - .unwrap_or_default(), - }) + adapter_outputs, + })) } fn resolve_relay_plugin_config(relay: Option<&RelayConfig>) -> Option { @@ -2082,7 +2118,9 @@ telemetry: .expect("config with relay telemetry provider"); let telemetry = config.telemetry.as_ref().expect("telemetry config"); - let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); + let plan = resolve_telemetry_plan(&config, None) + .expect("resolve telemetry plan") + .expect("telemetry plan"); assert!(telemetry.providers.contains_key(&TelemetryProvider::Relay)); assert_eq!(plan.providers, vec![TelemetryProvider::Relay]); @@ -2112,7 +2150,9 @@ relay: ) .expect("config with native telemetry provider"); - let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); + let plan = resolve_telemetry_plan(&config, None) + .expect("resolve telemetry plan") + .expect("telemetry plan"); assert_eq!(plan.providers, vec![TelemetryProvider::Native]); assert!(!plan.relay_enabled); @@ -2166,7 +2206,9 @@ relay: ) .expect("config with typed relay telemetry"); - let plan = resolve_telemetry_plan(&config, None).expect("telemetry plan"); + let plan = resolve_telemetry_plan(&config, None) + .expect("resolve telemetry plan") + .expect("telemetry plan"); let relay_config = plan.relay_config.expect("relay plugin config"); assert_eq!(plan.relay_project.as_deref(), Some("typed-project")); @@ -2214,6 +2256,90 @@ providers: assert!(result.is_err()); } + #[test] + fn telemetry_plan_rejects_provider_not_declared_by_adapter() { + let config: FabricConfig = serde_yaml::from_str( + r#" +schema_version: fabric.agent/v1alpha1 +metadata: + name: demo +harness: + adapter_id: nvidia.fabric.claude +runtime: +telemetry: + providers: + relay: {} +"#, + ) + .expect("config with relay telemetry provider"); + let descriptor: AdapterDescriptor = serde_json::from_value(serde_json::json!({ + "contract_version": ADAPTER_CONTRACT_VERSION, + "adapter_id": "nvidia.fabric.claude", + "harness": "claude", + "adapter_kind": "python" + })) + .expect("adapter descriptor"); + + let error = resolve_telemetry_plan(&config, Some(&descriptor)) + .expect_err("unsupported relay provider"); + + assert!(matches!( + error, + FabricError::AdapterDescriptorUnsupported { + adapter_id, + field: "telemetry.providers", + value, + } if adapter_id == "nvidia.fabric.claude" && value == "relay" + )); + } + + #[test] + fn telemetry_plan_uses_outputs_for_selected_adapter_providers() { + let config: FabricConfig = serde_yaml::from_str( + r#" +schema_version: fabric.agent/v1alpha1 +metadata: + name: demo +harness: + adapter_id: nvidia.fabric.codex.cli +runtime: +telemetry: + providers: + relay: {} + native: {} +"#, + ) + .expect("config with relay and native telemetry providers"); + let descriptor: AdapterDescriptor = serde_json::from_value(serde_json::json!({ + "contract_version": ADAPTER_CONTRACT_VERSION, + "adapter_id": "nvidia.fabric.codex.cli", + "harness": "codex", + "adapter_kind": "python", + "telemetry": { + "providers": { + "relay": { + "outputs": ["atif", "otel"], + "integration_modes": ["hooks", "gateway"] + }, + "native": { + "outputs": ["otel"] + } + } + } + })) + .expect("adapter descriptor"); + + let plan = resolve_telemetry_plan(&config, Some(&descriptor)) + .expect("resolve telemetry plan") + .expect("telemetry plan"); + + assert_eq!( + plan.providers, + vec![TelemetryProvider::Relay, TelemetryProvider::Native] + ); + assert_eq!(plan.adapter_outputs, vec!["atif", "otel"]); + } + #[test] fn loads_adapter_descriptor() { let descriptor = @@ -2238,7 +2364,12 @@ providers: Some("run") ); assert!(descriptor.config.accepts.contains(&"telemetry".to_string())); - assert!(descriptor.telemetry.supports.contains(&"relay".to_string())); + let relay = descriptor + .telemetry + .providers + .get(&TelemetryProvider::Relay) + .expect("relay telemetry support"); + assert!(relay.outputs.contains(&"atif".to_string())); } #[test] diff --git a/crates/fabric-core/src/lib.rs b/crates/fabric-core/src/lib.rs index f7d2be22a..80e78f511 100644 --- a/crates/fabric-core/src/lib.rs +++ b/crates/fabric-core/src/lib.rs @@ -11,16 +11,16 @@ pub mod schema; pub use config::{ ADAPTER_CONTRACT_VERSION, AdapterConfigSupport, AdapterDescriptor, AdapterDescriptorSource, - AdapterKind, AdapterRequirements, AdapterTelemetrySupport, CapabilityPlan, ControlLocation, - EffectiveConfig, EnvironmentConfig, EnvironmentOwnership, EnvironmentPlan, FabricConfig, - FabricDocument, HarnessConfig, McpConfig, McpExposure, McpServerPlan, MetadataConfig, - ModelConfig, ProfileConfig, ResolutionStrategy, ResolveContext, ResolvedAdapterDescriptor, - RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, TelemetryConfig, TelemetryPlan, - TelemetryProvider, TelemetryProviderConfig, load_adapter_descriptor, load_fabric_document, - resolve_effective_config, resolve_effective_config_from_config, - resolve_effective_config_with_profiles, resolve_run_plan, resolve_run_plan_from_config, - resolve_run_plan_from_effective_config, resolve_run_plan_with_profiles, - validate_agent_directory, + AdapterKind, AdapterRequirements, AdapterTelemetryProviderSupport, AdapterTelemetrySupport, + CapabilityPlan, ControlLocation, EffectiveConfig, EnvironmentConfig, EnvironmentOwnership, + EnvironmentPlan, FabricConfig, FabricDocument, HarnessConfig, McpConfig, McpExposure, + McpServerPlan, MetadataConfig, ModelConfig, ProfileConfig, ResolutionStrategy, ResolveContext, + ResolvedAdapterDescriptor, RunPlan, RuntimeCapabilities, RuntimeConfig, SkillConfig, + TelemetryConfig, TelemetryPlan, TelemetryProvider, TelemetryProviderConfig, + load_adapter_descriptor, load_fabric_document, resolve_effective_config, + resolve_effective_config_from_config, resolve_effective_config_with_profiles, resolve_run_plan, + resolve_run_plan_from_config, resolve_run_plan_from_effective_config, + resolve_run_plan_with_profiles, validate_agent_directory, }; 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 c0d2cb2f2..54f79c4fe 100644 --- a/crates/fabric-core/src/runtime.rs +++ b/crates/fabric-core/src/runtime.rs @@ -2088,6 +2088,23 @@ environment: fn native_telemetry_skips_relay_and_reaches_adapter_payload() { let root = temp_process_agent_dir(); let config_path = root.join("agent.yaml"); + fs::write( + root.join("adapters/process/fabric-adapter.json"), + r#"{ + "contract_version": "fabric.adapter/v1alpha1", + "adapter_id": "acme.fabric.process", + "harness": "process", + "adapter_kind": "process", + "telemetry": { + "providers": { + "native": { + "outputs": ["otel"] + } + } + } +}"#, + ) + .expect("write adapter telemetry support"); let mut config = fs::read_to_string(&config_path).expect("read agent config"); config.push_str( r#" diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx index 41eea0cce..3b19223b2 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitykind.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Kind" sidebar-title: "CapabilityKind" description: "Capability kind." -position: 39 +position: 40 --- {/* 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/fabric-core/config/enum-capabilitytarget.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx index 76f2c1609..0ee4a6260 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-capabilitytarget.mdx @@ -2,7 +2,7 @@ title: "Enum Capability Target" sidebar-title: "CapabilityTarget" description: "Capability routing target." -position: 40 +position: 41 --- {/* 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/fabric-core/config/enum-controllocation.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-controllocation.mdx index 5db0df5d3..0124fba83 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-controllocation.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-controllocation.mdx @@ -2,7 +2,7 @@ title: "Enum Control Location" sidebar-title: "ControlLocation" description: "Where Fabric control code runs relative to the environment." -position: 9 +position: 10 --- {/* 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/fabric-core/config/enum-environmentownership.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-environmentownership.mdx index 0d9796677..baa74b2cb 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-environmentownership.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-environmentownership.mdx @@ -2,7 +2,7 @@ title: "Enum Environment Ownership" sidebar-title: "EnvironmentOwnership" description: "Whether 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 */} diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/enum-fabricdocument.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-fabricdocument.mdx index a3b4bb4b9..e4050a902 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-fabricdocument.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-fabricdocument.mdx @@ -2,7 +2,7 @@ title: "Enum Fabric Document" sidebar-title: "FabricDocument" description: "A loaded Fabric document with resolved source path and agent root." -position: 15 +position: 16 --- {/* 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/fabric-core/config/enum-mcpexposure.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-mcpexposure.mdx index f3c98067b..f54eb91fc 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-mcpexposure.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-mcpexposure.mdx @@ -2,7 +2,7 @@ title: "Enum McpExposure" sidebar-title: "McpExposure" description: "MCP exposure strategy." -position: 18 +position: 19 --- {/* 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/fabric-core/config/enum-relayatifstorageconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx index f8991a59c..f81358900 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatifstorageconfig.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atif Storage Config" sidebar-title: "RelayAtifStorageConfig" description: "Relay ATIF remote storage configuration." -position: 45 +position: 46 --- {/* 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/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx index fbfd3c737..89e2e527e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointfieldnamepolicy.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Field Name Policy" sidebar-title: "RelayAtofEndpointFieldNamePolicy" description: "Relay ATOF endpoint field-name policy." -position: 46 +position: 47 --- {/* 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/fabric-core/config/enum-relayatofendpointtransport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx index 12ca939ed..1374c2980 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofendpointtransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Endpoint Transport" sidebar-title: "RelayAtofEndpointTransport" description: "Relay ATOF endpoint transport." -position: 47 +position: 48 --- {/* 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/fabric-core/config/enum-relayatofmode.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx index bb11f456b..202286f11 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayatofmode.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Atof Mode" sidebar-title: "RelayAtofMode" description: "Relay ATOF file mode." -position: 48 +position: 49 --- {/* 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/fabric-core/config/enum-relayotlptransport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx index cf581a45f..9f9a7964f 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayotlptransport.mdx @@ -2,7 +2,7 @@ title: "Enum Relay Otlp Transport" sidebar-title: "RelayOtlpTransport" description: "Relay OTLP transport." -position: 49 +position: 50 --- {/* 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/fabric-core/config/enum-relayunsupportedbehavior.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx index 6b3d3dc09..1360c4910 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-relayunsupportedbehavior.mdx +++ b/docs/reference/api/rust-library-reference/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: 50 +position: 51 --- {/* 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/fabric-core/config/enum-resolutionstrategy.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-resolutionstrategy.mdx index ab52714b4..82915bef4 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-resolutionstrategy.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-resolutionstrategy.mdx @@ -2,7 +2,7 @@ title: "Enum Resolution Strategy" sidebar-title: "ResolutionStrategy" description: "Adapter install or availability strategy." -position: 23 +position: 24 --- {/* 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/fabric-core/config/enum-telemetryprovider.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx index 27483ad33..31637c555 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/enum-telemetryprovider.mdx @@ -2,7 +2,7 @@ title: "Enum Telemetry Provider" sidebar-title: "TelemetryProvider" description: "Telemetry runtime provider." -position: 32 +position: 33 --- {/* 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/fabric-core/config/fn-load-adapter-descriptor.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx index 2e49d35ff..a94813a3e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-adapter-descriptor.mdx @@ -2,7 +2,7 @@ title: "Function load_adapter_descriptor" sidebar-title: "load_adapter_descriptor" description: "Load an adapter descriptor from JSON package metadata." -position: 34 +position: 35 --- {/* 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/fabric-core/config/fn-load-fabric-document.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx index bd93f4f8d..1c4dfa615 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-load-fabric-document.mdx @@ -2,7 +2,7 @@ title: "Function load_fabric_document" sidebar-title: "load_fabric_document" description: "Load a Fabric document from an agent directory or single agent config." -position: 35 +position: 36 --- {/* 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/fabric-core/config/fn-resolve-effective-config-from-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx index 9457fe190..07e640db2 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-from-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config_from_config" sidebar-title: "resolve_effective_config_from_config" description: "Resolve typed config/profile overlays into merged effective config." -position: 37 +position: 38 --- {/* 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/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx index 1fed0c9e5..0ea380ffe 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config-with-profiles.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config_with_profiles" sidebar-title: "resolve_effective_config_with_profiles" description: "Resolve an agent directory or single agent config with ordered profiles into merged effective config." -position: 38 +position: 39 --- {/* 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/fabric-core/config/fn-resolve-effective-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx index f6338a28e..0e70aa65a 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-effective-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_effective_config" sidebar-title: "resolve_effective_config" description: "Resolve an agent directory or single agent config into merged effective config." -position: 36 +position: 37 --- {/* 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/fabric-core/config/fn-resolve-run-plan-from-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx index 72ccd8c13..8f5e70049 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_config" sidebar-title: "resolve_run_plan_from_config" description: "Resolve a typed Fabric config and typed profile overlays into a runnable plan." -position: 40 +position: 41 --- {/* 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/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx index 124795331..22adf63e3 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-from-effective-config.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_from_effective_config" sidebar-title: "resolve_run_plan_from_effective_config" description: "Resolve execution planning metadata from merged effective config." -position: 41 +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/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx index 7f5cdb105..c2606b61e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan-with-profiles.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan_with_profiles" sidebar-title: "resolve_run_plan_with_profiles" description: "Resolve an agent directory or single agent config with ordered profile application." -position: 42 +position: 43 --- {/* 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/fabric-core/config/fn-resolve-run-plan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx index af7b915b8..c74eafcf1 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-resolve-run-plan.mdx @@ -2,7 +2,7 @@ title: "Function resolve_run_plan" sidebar-title: "resolve_run_plan" description: "Resolve an agent directory or single agent config into a runnable plan." -position: 39 +position: 40 --- {/* 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/fabric-core/config/fn-validate-agent-directory.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx index 00347b51c..97825ac72 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/fn-validate-agent-directory.mdx @@ -2,7 +2,7 @@ title: "Function validate_agent_directory" sidebar-title: "validate_agent_directory" description: "Validate an agent directory or config, including discoverable profile YAMLs." -position: 43 +position: 44 --- {/* 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/fabric-core/config/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx index b87c9ddbd..4905c7147 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/index.mdx @@ -2,7 +2,7 @@ title: "Module config" sidebar-title: "config" description: "Fabric config models and loading helpers." -position: 75 +position: 76 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -16,6 +16,7 @@ Fabric config models and loading helpers. - [AdapterConfigSupport](/reference/api/rust-library-reference/fabric-core/config/struct-adapterconfigsupport): Adapter config support. - [AdapterDescriptor](/reference/api/rust-library-reference/fabric-core/config/struct-adapterdescriptor): Language-neutral adapter descriptor for a harness integration. - [AdapterRequirements](/reference/api/rust-library-reference/fabric-core/config/struct-adapterrequirements): Adapter runtime requirements. +- [AdapterTelemetryProviderSupport](/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetryprovidersupport): Telemetry capabilities for one adapter-supported provider. - [AdapterTelemetrySupport](/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetrysupport): Adapter telemetry support. - [CapabilityPlan](/reference/api/rust-library-reference/fabric-core/config/struct-capabilityplan): Resolved capability configuration. - [CapabilityRoute](/reference/api/rust-library-reference/fabric-core/config/struct-capabilityroute): One capability routing decision. diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetryprovidersupport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetryprovidersupport.mdx new file mode 100644 index 000000000..4b6938b3c --- /dev/null +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetryprovidersupport.mdx @@ -0,0 +1,110 @@ +--- +title: "Struct Adapter Telemetry Provider Support" +sidebar-title: "AdapterTelemetryProviderSupport" +description: "Telemetry capabilities for one adapter-supported provider." +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 fabric-core`. + +
Vec<String>,\n    pub integration_modes: Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+ +Telemetry capabilities for one adapter-supported provider. + +## Fields + +### `outputs: Vec` + +Telemetry outputs the adapter can produce or forward for this provider. + +### `integration_modes: Vec` + +Integration modes implemented by the adapter for this provider. + +### `extensions: BTreeMap` + +Additive provider capability fields. + +## Trait Implementations + +### `impl Clone for AdapterTelemetryProviderSupport` + +
Clone for AdapterTelemetryProviderSupport"}} />
+ +#### `clone` + +
clone(&self) -> AdapterTelemetryProviderSupport"}} />
+ +#### `clone_from` + +
clone_from(&mut self, source: &Self)"}} />
+ +### `impl Debug for AdapterTelemetryProviderSupport` + +
Debug for AdapterTelemetryProviderSupport"}} />
+ +#### `fmt` + +
fmt(&self, f: &mut Formatter<'_>) -> Result"}} />
+ +### `impl Default for AdapterTelemetryProviderSupport` + +
Default for AdapterTelemetryProviderSupport"}} />
+ +#### `default` + +
default() -> AdapterTelemetryProviderSupport"}} />
+ +### `impl<'de> Deserialize<'de> for AdapterTelemetryProviderSupport` + +
Deserialize<'de> for AdapterTelemetryProviderSupport"}} />
+ +#### `deserialize` + +
deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where\n    __D: Deserializer<'de>,"}} />
+ +### `impl JsonSchema for AdapterTelemetryProviderSupport` + +
AdapterTelemetryProviderSupport"}} />
+ +#### `schema_name` + +
Cow<'static, str>"}} />
+ +#### `schema_id` + +
Cow<'static, str>"}} />
+ +#### `json_schema` + +
+ +#### `inline_schema` + +
bool"}} />
+ +### `impl PartialEq for AdapterTelemetryProviderSupport` + +
PartialEq for AdapterTelemetryProviderSupport"}} />
+ +#### `eq` + +
eq(&self, other: &AdapterTelemetryProviderSupport) -> bool"}} />
+ +#### `ne` + +
ne(&self, other: &Rhs) -> bool"}} />
+ +### `impl Serialize for AdapterTelemetryProviderSupport` + +
Serialize for AdapterTelemetryProviderSupport"}} />
+ +#### `serialize` + +
serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>where\n    __S: Serializer,"}} />
+ +### `impl StructuralPartialEq for AdapterTelemetryProviderSupport` + +
StructuralPartialEq for AdapterTelemetryProviderSupport"}} />
diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetrysupport.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetrysupport.mdx index 49accd8b7..588d6b139 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetrysupport.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-adaptertelemetrysupport.mdx @@ -2,22 +2,22 @@ title: "Struct Adapter Telemetry Support" sidebar-title: "AdapterTelemetrySupport" description: "Adapter telemetry support." -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 fabric-core`. -
Vec<String>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
+
BTreeMap<TelemetryProvider, AdapterTelemetryProviderSupport>,\n    pub extensions: BTreeMap<String, Value>,\n}"}} />
Adapter telemetry support. ## Fields -### `supports: Vec` +### `providers: BTreeMap` -Telemetry outputs supported by this adapter. +Provider-specific telemetry capabilities supported by this adapter. ### `extensions: BTreeMap` diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityplan.mdx index 84700ab35..35648ded6 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityplan.mdx @@ -2,7 +2,7 @@ title: "Struct Capability Plan" sidebar-title: "CapabilityPlan" description: "Resolved capability configuration." -position: 8 +position: 9 --- {/* 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/fabric-core/config/struct-capabilityroute.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityroute.mdx index a97861931..71543bc90 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityroute.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilityroute.mdx @@ -2,7 +2,7 @@ title: "Struct Capability Route" sidebar-title: "CapabilityRoute" description: "One capability routing decision." -position: 6 +position: 7 --- {/* 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/fabric-core/config/struct-capabilitytargetplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilitytargetplan.mdx index e60af403b..f1e387841 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilitytargetplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-capabilitytargetplan.mdx @@ -2,7 +2,7 @@ title: "Struct Capability Target Plan" sidebar-title: "CapabilityTargetPlan" description: "Capabilities routed to one target." -position: 7 +position: 8 --- {/* 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/fabric-core/config/struct-effectiveconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-effectiveconfig.mdx index 29957e6e7..b673bb728 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-effectiveconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-effectiveconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Effective Config" sidebar-title: "EffectiveConfig" description: "Merged Fabric config after applying selected profiles." -position: 10 +position: 11 --- {/* 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/fabric-core/config/struct-environmentconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentconfig.mdx index d6f6c4262..7dc2d584e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentconfig.mdx @@ -2,7 +2,7 @@ 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 */} diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentplan.mdx index 18c2d5f4f..713bca953 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-environmentplan.mdx @@ -2,7 +2,7 @@ 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 */} diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx index 5c0afd460..b0ad8de9c 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-fabricconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Fabric Config" sidebar-title: "FabricConfig" description: "Versioned Fabric agent config." -position: 14 +position: 15 --- {/* 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/fabric-core/config/struct-harnessconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-harnessconfig.mdx index a426f513e..31656204d 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-harnessconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-harnessconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Harness Config" sidebar-title: "HarnessConfig" description: "Harness selection." -position: 16 +position: 17 --- {/* 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/fabric-core/config/struct-mcpconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpconfig.mdx index 5dee0a83f..8d427b272 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct McpConfig" sidebar-title: "McpConfig" description: "MCP capability configuration." -position: 17 +position: 18 --- {/* 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/fabric-core/config/struct-mcpserverconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverconfig.mdx index d32d5c809..203d931e8 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverconfig.mdx @@ -2,7 +2,7 @@ title: "Struct McpServer Config" sidebar-title: "McpServerConfig" description: "MCP server configuration." -position: 14 +position: 15 --- {/* 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/fabric-core/config/struct-mcpserverplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverplan.mdx index b45663fba..0e5b58350 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-mcpserverplan.mdx @@ -2,7 +2,7 @@ title: "Struct McpServer Plan" sidebar-title: "McpServerPlan" description: "Resolved MCP server exposure." -position: 19 +position: 20 --- {/* 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/fabric-core/config/struct-metadataconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-metadataconfig.mdx index c3888d5d8..af37aa70c 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-metadataconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-metadataconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Metadata Config" sidebar-title: "MetadataConfig" description: "Human-readable metadata." -position: 20 +position: 21 --- {/* 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/fabric-core/config/struct-modelconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-modelconfig.mdx index 8784c9840..325bfe13e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-modelconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-modelconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Model Config" sidebar-title: "ModelConfig" description: "Model configuration." -position: 21 +position: 22 --- {/* 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/fabric-core/config/struct-profileconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileconfig.mdx index 44dfd8a2a..326dc689c 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Profile Config" sidebar-title: "ProfileConfig" description: "Profile config applied on top of a Fabric config." -position: 22 +position: 23 --- {/* 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/fabric-core/config/struct-profileregistryconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileregistryconfig.mdx index 997207487..7eb7a4407 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileregistryconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-profileregistryconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Profile Registry Config" sidebar-title: "ProfileRegistryConfig" description: "Profile discovery config for curated package profiles." -position: 19 +position: 20 --- {/* 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/fabric-core/config/struct-relayatifconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx index c0d8e8dd4..f73fcf949 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatifconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atif Config" sidebar-title: "RelayAtifConfig" description: "Relay ATIF export configuration." -position: 20 +position: 21 --- {/* 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/fabric-core/config/struct-relayatofconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx index 4486582dc..700b08004 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Config" sidebar-title: "RelayAtofConfig" description: "Relay ATOF export configuration." -position: 21 +position: 22 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} @@ -67,7 +67,7 @@ Additive ATOF fields. #### `default` -
default() -> Self"}} />
+
default() -> RelayAtofConfig"}} />
### `impl<'de> Deserialize<'de> for RelayAtofConfig` diff --git a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx index 77471a8c7..9bb43d299 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayatofendpointconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Atof Endpoint Config" sidebar-title: "RelayAtofEndpointConfig" description: "Relay ATOF endpoint configuration." -position: 22 +position: 23 --- {/* 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/fabric-core/config/struct-relaycomponentconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx index d07cd4327..e6a216028 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relaycomponentconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Component Config" sidebar-title: "RelayComponentConfig" description: "Generic NeMo Relay plugin component configuration." -position: 23 +position: 24 --- {/* 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/fabric-core/config/struct-relayconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx index 87cdeb362..5965a74a4 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config" sidebar-title: "RelayConfig" description: "NeMo Relay integration configuration." -position: 24 +position: 25 --- {/* 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/fabric-core/config/struct-relayconfigpolicy.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx index a6e3f4e86..5e9a0bc55 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayconfigpolicy.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Config Policy" sidebar-title: "RelayConfigPolicy" description: "Relay validation policy." -position: 25 +position: 26 --- {/* 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/fabric-core/config/struct-relayobservabilityconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx index e929a6e76..dba4be143 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayobservabilityconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Observability Config" sidebar-title: "RelayObservabilityConfig" description: "NeMo Relay observability component configuration." -position: 26 +position: 27 --- {/* 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/fabric-core/config/struct-relayotlpconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx index 71aaa9860..2b18ee102 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-relayotlpconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Relay Otlp Config" sidebar-title: "RelayOtlpConfig" description: "Relay OpenTelemetry/OpenInference export configuration." -position: 27 +position: 28 --- {/* 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/fabric-core/config/struct-resolvecontext.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvecontext.mdx index 074e68c6f..05fc8241d 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvecontext.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvecontext.mdx @@ -2,7 +2,7 @@ title: "Struct Resolve Context" sidebar-title: "ResolveContext" description: "Source context used when resolving an in-memory Fabric config." -position: 24 +position: 25 --- {/* 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/fabric-core/config/struct-resolvedadapterdescriptor.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvedadapterdescriptor.mdx index 8bc5b3e9b..275740793 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvedadapterdescriptor.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-resolvedadapterdescriptor.mdx @@ -2,7 +2,7 @@ title: "Struct Resolved Adapter Descriptor" sidebar-title: "ResolvedAdapterDescriptor" description: "Adapter descriptor selected for a run plan." -position: 25 +position: 26 --- {/* 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/fabric-core/config/struct-runplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runplan.mdx index a576fd6a0..d28ca141a 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-runplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runplan.mdx @@ -2,7 +2,7 @@ title: "Struct RunPlan" sidebar-title: "RunPlan" description: "Resolved Fabric run plan." -position: 26 +position: 27 --- {/* 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/fabric-core/config/struct-runtimecapabilities.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimecapabilities.mdx index 0470e459c..078e7e823 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimecapabilities.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimecapabilities.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Capabilities" sidebar-title: "RuntimeCapabilities" description: "Lifecycle behavior implemented by a resolved runtime path." -position: 27 +position: 28 --- {/* 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/fabric-core/config/struct-runtimeconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimeconfig.mdx index 2f08aa522..d7e11f99b 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimeconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-runtimeconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Runtime Config" sidebar-title: "RuntimeConfig" description: "Runtime input/output contract." -position: 28 +position: 29 --- {/* 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/fabric-core/config/struct-skillconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-skillconfig.mdx index f52d76b09..b9b920f78 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-skillconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-skillconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Skill Config" sidebar-title: "SkillConfig" description: "Skill capability configuration." -position: 29 +position: 30 --- {/* 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/fabric-core/config/struct-telemetryconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx index 7d201c34e..04cb64914 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Config" sidebar-title: "TelemetryConfig" description: "Telemetry configuration." -position: 30 +position: 31 --- {/* 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/fabric-core/config/struct-telemetryplan.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx index 97ca74a41..db48c6855 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryplan.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Plan" sidebar-title: "TelemetryPlan" description: "Resolved telemetry plan." -position: 31 +position: 32 --- {/* 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/fabric-core/config/struct-telemetryproviderconfig.mdx b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx index 6081568d0..37e97f357 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/config/struct-telemetryproviderconfig.mdx @@ -2,7 +2,7 @@ title: "Struct Telemetry Provider Config" sidebar-title: "TelemetryProviderConfig" description: "Provider-specific telemetry configuration." -position: 33 +position: 34 --- {/* 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/fabric-core/doctor/enum-doctorstatus.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx index 5ef34bb27..453849104 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/enum-doctorstatus.mdx @@ -2,7 +2,7 @@ title: "Enum Doctor Status" sidebar-title: "DoctorStatus" description: "Diagnostic status." -position: 46 +position: 47 --- {/* 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/fabric-core/doctor/fn-doctor-plan.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/fn-doctor-plan.mdx index 733398689..169bf5c1e 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/fn-doctor-plan.mdx +++ b/docs/reference/api/rust-library-reference/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: 47 +position: 48 --- {/* 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/fabric-core/doctor/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx index e79ae26a8..02811f3e6 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/index.mdx @@ -2,7 +2,7 @@ title: "Module doctor" sidebar-title: "doctor" description: "Plan diagnostics for Fabric." -position: 76 +position: 77 --- {/* 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/fabric-core/doctor/struct-doctorcheck.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx index 8e94ca38d..940cb9162 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorcheck.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Check" sidebar-title: "DoctorCheck" description: "Diagnostic check result." -position: 44 +position: 45 --- {/* 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/fabric-core/doctor/struct-doctorreport.mdx b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx index 4d6c153d2..f77ed28a3 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/doctor/struct-doctorreport.mdx @@ -2,7 +2,7 @@ title: "Struct Doctor Report" sidebar-title: "DoctorReport" description: "Diagnostic report for a resolved run plan." -position: 45 +position: 46 --- {/* 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/fabric-core/error/enum-fabricerror.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx index d53f38e54..9c40ce633 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/enum-fabricerror.mdx @@ -2,7 +2,7 @@ title: "Enum Fabric Error" sidebar-title: "FabricError" description: "Errors raised by Fabric config loading and validation." -position: 48 +position: 49 --- {/* 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/fabric-core/error/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx index dfb44afde..95d1744e4 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/index.mdx @@ -2,7 +2,7 @@ title: "Module error" sidebar-title: "error" description: "Error types for Fabric core." -position: 77 +position: 78 --- {/* 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/fabric-core/error/type-result.mdx b/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx index 6c600f286..6c5f107c0 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/error/type-result.mdx @@ -2,7 +2,7 @@ title: "Type Alias Result" sidebar-title: "Result" description: "Core Fabric result type." -position: 49 +position: 50 --- {/* 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/fabric-core/fn-version.mdx b/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx index dd81b4a7c..9056836b4 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/fn-version.mdx @@ -2,7 +2,7 @@ title: "Function version" sidebar-title: "version" description: "Returns the crate version compiled into this build." -position: 80 +position: 81 --- {/* 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/fabric-core/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/index.mdx index 6acfef10b..632539f08 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/index.mdx @@ -19,6 +19,7 @@ Core config and runtime contract for NeMo Fabric. - `pub use config::AdapterDescriptorSource;` - `pub use config::AdapterKind;` - `pub use config::AdapterRequirements;` +- `pub use config::AdapterTelemetryProviderSupport;` - `pub use config::AdapterTelemetrySupport;` - `pub use config::CapabilityPlan;` - `pub use config::ControlLocation;` diff --git a/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx index ab02904ca..8095b5ae4 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/runtime/index.mdx @@ -2,7 +2,7 @@ title: "Module runtime" sidebar-title: "runtime" description: "Runtime invocation helpers." -position: 78 +position: 79 --- {/* 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/fabric-core/schema/index.mdx b/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx index 6a09ead74..3ade0087f 100644 --- a/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx +++ b/docs/reference/api/rust-library-reference/fabric-core/schema/index.mdx @@ -2,7 +2,7 @@ title: "Module schema" sidebar-title: "schema" description: "JSON Schema generation for the public Fabric contract." -position: 79 +position: 80 --- {/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. SPDX-License-Identifier: Apache-2.0 */} diff --git a/python/src/nemo_fabric/models.py b/python/src/nemo_fabric/models.py index 098851634..2bcf09a34 100644 --- a/python/src/nemo_fabric/models.py +++ b/python/src/nemo_fabric/models.py @@ -486,22 +486,25 @@ def enable_relay( if self.telemetry is None: self.telemetry = TelemetryConfig() self.telemetry.enable_relay() - relay_observability = ( - observability - if observability is None or isinstance(observability, RelayObservabilityConfig) - else dict(observability) - ) - relay_components = [ - item if isinstance(item, RelayComponentConfig) else dict(item) for item in (components or []) - ] - relay_policy = policy if policy is None or isinstance(policy, RelayConfigPolicy) else dict(policy) - self.relay = RelayConfig( - project=project, - output_dir=output_dir, - observability=relay_observability, - components=relay_components, - policy=relay_policy, - ) + if self.relay is None: + relay = RelayConfig() + elif isinstance(self.relay, RelayConfig): + relay = self.relay.model_copy(deep=True) + else: + relay = RelayConfig.from_mapping(self.relay) + if project is not None: + relay.project = project + if output_dir is not None: + relay.output_dir = output_dir + if observability is not None: + relay.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] + if policy is not None: + relay.policy = policy if isinstance(policy, RelayConfigPolicy) else dict(policy) + self.relay = relay return self diff --git a/schemas/adapter-descriptor.schema.json b/schemas/adapter-descriptor.schema.json index 937d84cf8..9362e01f2 100644 --- a/schemas/adapter-descriptor.schema.json +++ b/schemas/adapter-descriptor.schema.json @@ -88,16 +88,37 @@ }, "type": "object" }, - "AdapterTelemetrySupport": { + "AdapterTelemetryProviderSupport": { "additionalProperties": true, - "description": "Adapter telemetry support.", + "description": "Telemetry capabilities for one adapter-supported provider.", "properties": { - "supports": { - "description": "Telemetry outputs supported by this adapter.", + "integration_modes": { + "description": "Integration modes implemented by the adapter for this provider.", "items": { "type": "string" }, "type": "array" + }, + "outputs": { + "description": "Telemetry outputs the adapter can produce or forward for this provider.", + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "AdapterTelemetrySupport": { + "additionalProperties": true, + "description": "Adapter telemetry support.", + "properties": { + "providers": { + "additionalProperties": { + "$ref": "#/$defs/AdapterTelemetryProviderSupport" + }, + "description": "Provider-specific telemetry capabilities supported by this adapter.", + "type": "object" } }, "type": "object" diff --git a/schemas/run-plan.schema.json b/schemas/run-plan.schema.json index 1f14a4793..fe8805ace 100644 --- a/schemas/run-plan.schema.json +++ b/schemas/run-plan.schema.json @@ -165,12 +165,19 @@ }, "type": "object" }, - "AdapterTelemetrySupport": { + "AdapterTelemetryProviderSupport": { "additionalProperties": true, - "description": "Adapter telemetry support.", + "description": "Telemetry capabilities for one adapter-supported provider.", "properties": { - "supports": { - "description": "Telemetry outputs supported by this adapter.", + "integration_modes": { + "description": "Integration modes implemented by the adapter for this provider.", + "items": { + "type": "string" + }, + "type": "array" + }, + "outputs": { + "description": "Telemetry outputs the adapter can produce or forward for this provider.", "items": { "type": "string" }, @@ -179,6 +186,20 @@ }, "type": "object" }, + "AdapterTelemetrySupport": { + "additionalProperties": true, + "description": "Adapter telemetry support.", + "properties": { + "providers": { + "additionalProperties": { + "$ref": "#/$defs/AdapterTelemetryProviderSupport" + }, + "description": "Provider-specific telemetry capabilities supported by this adapter.", + "type": "object" + } + }, + "type": "object" + }, "CapabilityKind": { "description": "Capability kind.", "oneOf": [ diff --git a/tests/python/test_sdk_contract.py b/tests/python/test_sdk_contract.py index cc5e166b0..153d63324 100644 --- a/tests/python/test_sdk_contract.py +++ b/tests/python/test_sdk_contract.py @@ -244,6 +244,29 @@ def test_fabric_config_authors_first_class_relay_observability(): } +def test_fabric_config_enable_relay_preserves_omitted_fields(): + config = _fabric_config() + + config.enable_relay( + project="fabric-tests", + output_dir="./artifacts/relay", + observability={"atif": {"enabled": True}}, + components=[{"kind": "switchyard"}], + ) + initial = config.to_mapping()["relay"] + config.enable_relay(policy={"unknown_component": "error"}) + + relay = config.to_mapping()["relay"] + assert relay["project"] == initial["project"] + assert relay["output_dir"] == initial["output_dir"] + assert relay["observability"] == initial["observability"] + assert relay["components"] == initial["components"] + assert relay["policy"]["unknown_component"] == "error" + + config.enable_relay(components=[]) + assert config.to_mapping()["relay"]["components"] == [] + + def test_telemetry_config_enable_native_preserves_existing_config(): telemetry = TelemetryConfig() @@ -835,9 +858,7 @@ def test_run_output_exposes_response_and_preserves_extensions(): def test_run_result_wraps_object_output_as_run_output(): - result = RunResult.from_mapping( - _run_result(output={"response": "hello", "usage": {"tokens": 1}}) - ) + result = RunResult.from_mapping(_run_result(output={"response": "hello", "usage": {"tokens": 1}})) assert isinstance(result.output, RunOutput) assert result.output.response == "hello" @@ -873,9 +894,7 @@ def test_run_output_preserves_non_string_response_without_raising(): def test_run_result_preserves_structured_response_from_core_valid_output(): - result = RunResult.from_mapping( - _run_result(output={"response": {"text": "hello"}, "usage": {"tokens": 1}}) - ) + result = RunResult.from_mapping(_run_result(output={"response": {"text": "hello"}, "usage": {"tokens": 1}})) assert isinstance(result.output, RunOutput) assert result.output.response == {"text": "hello"} From a069ee9e45161220b2dddf144514c417d931031d Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:38:51 -0700 Subject: [PATCH 3/3] test(relay): align fixtures with telemetry validation Signed-off-by: Anuradha Karuppiah <26330987+AnuradhaKaruppiah@users.noreply.github.com> --- tests/adapters/test_hermes_cli.py | 18 ++++++++++-------- .../adapters/hermes-shim/fabric-adapter.json | 8 +++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/tests/adapters/test_hermes_cli.py b/tests/adapters/test_hermes_cli.py index 1cd1d1f15..20bdaae0c 100644 --- a/tests/adapters/test_hermes_cli.py +++ b/tests/adapters/test_hermes_cli.py @@ -4,7 +4,9 @@ import types from pathlib import Path +import pytest from nemo_fabric import Fabric +from nemo_fabric import FabricConfigError async def test_hermes_cli_fields(hermes_command: Path, hermes_agent_dir: Path, hermes_cli_profile: str): @@ -57,14 +59,14 @@ async def test_hermes_cli_rejects_native_telemetry( encoding="utf-8", ) - result = await Fabric().run( - hermes_agent_dir, - profiles=[hermes_cli_profile, "native_telemetry"], - input="who are you?", - ) - - assert result["status"] == "failed" - assert "only relay telemetry is supported for Hermes" in result["error"]["message"] + with pytest.raises( + FabricConfigError, + match=r"does not support `telemetry\.providers` value `native`", + ): + Fabric().plan( + hermes_agent_dir, + profiles=[hermes_cli_profile, "native_telemetry"], + ) async def test_hermes_cli_multi_turn( 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 7c0fdb59f..b5c7a26c3 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 @@ -20,7 +20,13 @@ "accepts": [ "tools", "mcp", - "skills" + "skills", + "telemetry" ] + }, + "telemetry": { + "providers": { + "relay": {} + } } }