Skip to content

feat(hermes): use upstream Relay integration - #192

Open
zhongxuanwang-nv wants to merge 11 commits into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/hermes-adapter-v1a2
Open

feat(hermes): use upstream Relay integration#192
zhongxuanwang-nv wants to merge 11 commits into
NVIDIA:mainfrom
zhongxuanwang-nv:feat/hermes-adapter-v1a2

Conversation

@zhongxuanwang-nv

@zhongxuanwang-nv zhongxuanwang-nv commented Aug 10, 2026

Copy link
Copy Markdown
Member

Overview

Update the Hermes adapter to use Hermes Agent's bundled
observability/nemo_relay integration. Relay remains a loose, upstream-compatible
Hermes adapter dependency: nemo-relay>=0.6.0,<0.8.

The published Hermes Agent 0.19.0 package supports Relay 0.6.x and exposes the
upstream on_session_finalize plugin hook. Newer upstream Hermes releases can
resolve the 0.7.x line; the range allows each Hermes release to resolve its
compatible Relay version.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Stage Fabric's resolved Relay configuration, including ATIF, in the TOML
    file consumed by Hermes' upstream plugin. No ATIF TOML-to-environment mapping.
  • Stage Relay v2 TOML for Relay 0.6 and Relay v3 TOML for Relay 0.7+.
  • Finalize each session through Hermes' lifecycle after an invocation so
    TOML-configured ATIF is complete before Fabric returns. Newer Hermes uses its
    public lifecycle API; published 0.19 uses the equivalent upstream plugin hook.
  • Keep a cancelled caller from closing Hermes resources while its synchronous
    invocation worker is still finalizing.
  • Correlate both current Hermes Relay scopes and Hermes 0.19 turn markers for
    Fabric streaming.
  • Keep environment snapshot/restore and prevent inherited fallback variables
    from affecting a failed initialization.
  • Do not update Relay repository-wide: the root/DeepAgents dependencies and
    Claude/Codex gateways remain on Relay 0.6's public observability-v2 contract.

Validation

  • ruff format and ruff check on changed Python files
  • Focused Python validation: 76 passed
  • Hermes adapter and complete E2E suite passed with Relay 0.6.0 and with
    Relay 0.7.2 injected to exercise the v3 path.
  • uv lock --check, adapter lock check, and Hermes wheel metadata verification

Not run: just test-python (just unavailable); Rust license and attribution
checks (cargo/cargo-about unavailable).

Where should the reviewer start?

Start with adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py, then
review tests/adapters/test_hermes_adapter.py and tests/e2e/test_hermes_e2e.py.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

Summary by CodeRabbit

  • New Features

    • Improved Hermes Relay integration with automatic configuration staging, environment setup, and cleanup.
    • Added support for Relay observability configuration versions 2 and 3.
    • Improved streaming correlation for current and legacy Hermes execution records.
    • Enhanced Hermes tool-call bridging, lifecycle handling, and output reporting.
    • Improved support for newer Relay releases.
  • Bug Fixes

    • Improved startup-failure cleanup and persistent runtime shutdown handling.
    • Prevented concurrent Hermes turn invocations from interfering with one another.
    • Improved compatibility with varying Hermes event and response formats.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Hermes now stages Relay plugin configuration, applies Relay environment values, executes turns through Hermes lifecycle APIs, correlates Fabric and legacy Hermes records, and restores runtime state during shutdown. Tests cover configuration, concurrency, MCP bridging, event metadata, and dependency compatibility.

Changes

Hermes Relay integration

Layer / File(s) Summary
Relay configuration and lifecycle
adapters/hermes/..., adapters/common/..., tests/adapters/test_hermes_adapter.py, adapters/hermes/pyproject.toml, tests/adapters/test_adapter_package_metadata.py
Hermes writes sanitized Relay TOML, converts exporter settings to Relay v3, applies supported environment variables, and restores prior values during shutdown. The Relay dependency range now includes versions below 0.8.
Hermes execution and event correlation
adapters/hermes/..., python/src/nemo_fabric/streaming.py, tests/python/test_streaming.py, tests/adapters/test_hermes_streaming.py
Hermes executes turns in a worker thread, rejects concurrent invocations, and finalizes sessions through Hermes lifecycle APIs. Streaming now correlates Fabric turn scopes and legacy Hermes turn_id records. Shutdown tests cover cancellation synchronization.
Hermes integration validation
tests/e2e/test_hermes_e2e.py, tests/e2e/test_hermes_runtime.py, tests/adapters/test_hermes_adapter.py
Tests validate version-specific MCP tool-call bridging, Relay emitter metadata, event scopes, normalized responses, runtime artifacts, configuration staging, and startup-failure cleanup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HermesAdapter
  participant RelayConfig
  participant HermesAgent
  participant FabricStreaming
  HermesAdapter->>RelayConfig: Stage translated plugin TOML
  HermesAdapter->>HermesAgent: Apply Relay environment values
  HermesAdapter->>HermesAgent: Execute turn in worker thread
  HermesAgent->>FabricStreaming: Emit Hermes turn records
  FabricStreaming-->>HermesAdapter: Correlate turn records
  HermesAdapter->>HermesAgent: Finalize Relay session
  HermesAdapter->>RelayConfig: Restore environment and clear staged path
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title uses valid Conventional Commits syntax, has a concise imperative summary, stays under 72 characters, and matches the Hermes Relay integration change.
Description check ✅ Passed The description explains the change, identifies review starting points, includes related issues with valid action keywords, and completes both required attestations.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py`:
- Around line 438-441: Update the Hermes adapter lifecycle around invoke(), the
asyncio.to_thread(invoke_turn) call, and stop() to track the active worker task,
shield it from caller cancellation, and await its completion before closing
agent/session_db or restoring Relay environment state. Preserve one local
adapter host across ordered start → invoke* → stop operations, and add a
regression test verifying cleanup waits for a cancelled worker turn.
- Around line 146-167: Update the inline comment in
write_hermes_relay_plugin_config to reference Hermes Agent v0.19.0, or make it
version-neutral; do not alter the ATIF handling logic or dependency metadata.

In `@tests/e2e/test_hermes_e2e.py`:
- Around line 350-359: Update the turn_marks collection and assertions in the
Hermes E2E test to filter for records named "hermes.turn" with the expected
scope_category values, matching the established ATOF naming contract. Add an
explicit non-empty assertion before validating metadata so the model and
platform checks cannot vacuously pass.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5678506e-42d4-4c4f-aba8-a80580f078e2

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0881e and 5fbdd5a.

📒 Files selected for processing (6)
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_adapter.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
📜 Review details
⏰ Context from checks skipped due to timeout. (16)
  • GitHub Check: Preview docs
  • GitHub Check: Test (Python 3.12, windows-amd64)
  • GitHub Check: Test (Python 3.13, linux-amd64)
  • GitHub Check: Test (Python 3.12, linux-arm64)
  • GitHub Check: Test (Python 3.11, linux-amd64)
  • GitHub Check: Test (Python 3.14, linux-arm64)
  • GitHub Check: Test (Python 3.11, macos-arm64)
  • GitHub Check: Test (Python 3.14, macos-arm64)
  • GitHub Check: Test (Python 3.14, windows-amd64)
  • GitHub Check: Test (Python 3.13, windows-amd64)
  • GitHub Check: Test (Python 3.12, macos-arm64)
  • GitHub Check: Test (Python 3.13, linux-arm64)
  • GitHub Check: Test (Python 3.13, macos-arm64)
  • GitHub Check: Test (Python 3.12, linux-amd64)
  • GitHub Check: Test (Python 3.11, linux-arm64)
  • GitHub Check: Test (Python 3.14, linux-amd64)
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi,rs}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
python/src/nemo_fabric/**/*.py

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

For Python API reference changes, update source docstrings under python/src/nemo_fabric/ instead of generated API reference files.

Files:

  • python/src/nemo_fabric/streaming.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • python/src/nemo_fabric/streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
python/src/nemo_fabric/**/*.{py,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure native extension naming and placement remain compatible with downstream consumers, including the editable maturin build producing nemo_fabric._native.

Files:

  • python/src/nemo_fabric/streaming.py
python/src/nemo_fabric/**/*

⚙️ CodeRabbit configuration file

python/src/nemo_fabric/**/*: Review Python SDK changes for typed API consistency, import-time dependency neutrality, async/session behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/src/nemo_fabric/streaming.py
tests/**/*.{rs,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the corresponding Rust crate or the relevant area under tests/.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/python-tests/SKILL.md)

tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; async tests are automatically detected by the async runner.
Do not add -> None return type annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or AsyncMock, using the spec argument when necessary, rather than defining a new class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
If a fixture is needed in multiple test files, define it once in conftest.py rather than repeating it.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.
Use @pytest.mark.usefixtures when a fixture is needed but its returned value is unused or it returns no value.
Avoid defensive programming in tests; access expected values directly so missing data raises a clear failure, such as using results["data"] instead of results.get("data").
When adapter installation metadata changes, packaging metadata tests must directly assert that the root project depends unconditionally on the exact-version nemo-fabric-runtime distribution.
Packaging metadata tests must verify that each root harness extra delegates to the matching version of the leaf adapter's harness extra.
Packaging metadata tests must verify that bare leaf dependencies remain adapter-owned and that the root adapter-tests dependency group installs each leaf through its harness extra.
Packaging metadata tests must verify that every leaf provides full; only adapters importing NeMo Relay Python APIs provide relay, while adapters using an external Relay executable have full equal to harness.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/e2e/test_hermes_runtime.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
tests/adapters/**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

tests/adapters/**/*.py: If an adapter or integration changes, run its focused tests.
For adapter behavior changes, run focused adapter tests under tests/adapters, then run just test-python.

Files:

  • tests/adapters/test_hermes_adapter.py
adapters/*/src/**/*.py

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/src/**/*.py: Implement adapters using the existing Fabric python or process runner and normalized request/result contracts; do not add a runner or one-off abstraction for a single adapter.
Treat normalized config, Fabric-resolved plans, and runtime_context as authoritative; reserve harness.settings for adapter-wide behavior and apply precedence in the order: normalized config, plans/context, harness settings, descriptor/default values.
Reject conflicting duplicate declarations and unsupported behavior with actionable errors naming the field and supported alternatives; never silently drop configuration.
Validate dependency versions, hooks, and credentials before harness invocation, and never expose credential values in outputs, errors, events, logs, or fixtures.
Forward only required system, selected credential, telemetry, and documented harness-specific environment variables; never forward or log unrelated environment values.
Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations. Emit one JSON lifecycle response per request on stdout and diagnostics on stderr.
Return harness-level invocation failures as successful lifecycle responses containing response: null, failed: true, and structured error fields (code, message, retryable, and optional metadata).
Do not emit NeMo Relay stream records on adapter stdout; return exactly one terminal lifecycle response while streaming occurs through the SDK-owned out-of-band endpoint.
Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.
Use start to initialize adapter-owned harness state, retain it for continuation across repeated invoke calls on the same runtime, and release it in stop.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🧠 Learnings (2)
📚 Learning: 2026-06-28T04:03:32.877Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 26
File: python/tests/smoke_typed_config.py:163-177
Timestamp: 2026-06-28T04:03:32.877Z
Learning: In NVIDIA NeMo Fabric Python SDK serialization of `RuntimeCapabilities` (to satisfy the “parity contract” with Rust core and the CLI), do not emit metadata keys when the corresponding metadata is absent. Instead, omit those fields entirely so the produced JSON matches the Rust/CLI output (e.g., avoid `null`, empty objects, or placeholder metadata). During review, verify the serializer/builders follow this omission rule and that Python outputs/parity tests reflect the same shape.

Applied to files:

  • python/src/nemo_fabric/streaming.py
📚 Learning: 2026-07-09T22:28:51.689Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 43
File: adapters/claude-sdk/src/nemo_fabric_adapters/claude_sdk/adapter.py:164-168
Timestamp: 2026-07-09T22:28:51.689Z
Learning: In the NeMo-Fabric adapters, treat path values used in Fabric adapter configuration (including logic like `_resolve_path` in adapter.py) as config-root-relative. Do not apply `Path.expanduser()` (or otherwise apply `~`/home or shell-style expansion), because it will make the resolved paths normalize inconsistently across adapters. Also, do not rely on or add any resolution behavior that uses `harness.settings.cwd` as an override point for these adapter paths—`harness.settings.cwd` is explicitly unsupported in this adapter context.

Applied to files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🪛 ast-grep (0.45.0)
tests/adapters/test_hermes_adapter.py

[info] 67-79: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
"relay": {
"config": {
"atof": {
"enabled": True,
"sinks": [{"type": "file"}],
},
"atif": {"enabled": True},
}
}
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 Ruff (0.16.1)
tests/adapters/test_hermes_adapter.py

[warning] 134-134: Avoid specifying long messages outside the exception class

(TRY003)

adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py

[warning] 166-166: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 477-477: Too many branches (13 > 12)

(PLR0912)


[warning] 477-477: Too many statements (52 > 50)

(PLR0915)

🔇 Additional comments (8)
tests/e2e/test_hermes_e2e.py (1)

113-119: LGTM!

Also applies to: 185-191, 252-252, 266-269, 327-349, 390-396

tests/e2e/test_hermes_runtime.py (1)

34-34: LGTM!

Also applies to: 104-104

adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (3)

14-14: LGTM!

Also applies to: 39-47


170-198: LGTM!

Also applies to: 280-282, 313-331, 355-360


469-475: LGTM!

tests/adapters/test_hermes_adapter.py (1)

13-15: LGTM!

Also applies to: 62-156, 704-704, 785-785

python/src/nemo_fabric/streaming.py (1)

559-561: LGTM!

tests/python/test_streaming.py (1)

883-901: LGTM!

Comment thread adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
Comment thread adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py Outdated
Comment thread tests/e2e/test_hermes_e2e.py Outdated
@zhongxuanwang-nv zhongxuanwang-nv self-assigned this Aug 10, 2026
@zhongxuanwang-nv zhongxuanwang-nv added this to the 0.2 milestone Aug 10, 2026
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (2)

432-454: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Serialize invocations for each HermesRuntime.

asyncio.to_thread() releases the event loop while Hermes runs. A second invoke() can then use the same agent, session database, session ID, and conversation history at the same time. The last completion can overwrite history, and concurrent finalize_session() calls can mix Relay artifacts.

Add a per-runtime operation lock around _invoke_hermes_turn() and finalize_session(). Integrate it with the existing cancellation task tracking. Add a concurrent-invocation regression test.

As per coding guidelines, “Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 432
- 454, Add a per-HermesRuntime operation lock that serializes the full
invoke_turn sequence, including _invoke_hermes_turn and finalize_session, so
concurrent invokes cannot share mutable agent, session, or conversation state.
Acquire and release this lock through the existing cancellation task tracking,
preserving cancellation behavior and ordered start → invoke* → stop lifecycle
operations. Add a regression test that launches concurrent invocations on one
runtime and verifies they execute serially.

Source: Coding guidelines


317-332: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Do not store runtime-specific Relay configuration in process-global environment variables.

Two active runtimes cannot safely own different HERMES_NEMO_RELAY_* values. For example, runtime B snapshots runtime A’s value, and after A then B stop, B restores A’s stale plugin path instead of the original environment. This also permits one runtime to affect another runtime’s Relay configuration.

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py#L317-L332: isolate Relay configuration per runtime, such as with a dedicated process, instead of mutating shared os.environ.
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py#L515-L523: remove restoration logic that attempts to reconstruct nested ownership of shared environment variables.

As per coding guidelines, “Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 317
- 332, The Relay startup flow around the environment-application logic at
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py:317-332 must stop
mutating process-global os.environ; isolate each runtime’s HERMES_NEMO_RELAY_*
configuration through a dedicated process or equivalent runtime-scoped
mechanism. Remove the nested environment restoration logic at
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py:515-523, including
its ownership snapshots, and ensure Relay state remains scoped to the resolved
Fabric runtime ID.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/hermes/pyproject.toml`:
- Around line 37-41: Do not introduce the v0.20-dependent Hermes Agent extra
until an installable 0.20 release exists. In adapters/hermes/pyproject.toml
lines 37-41, either constrain the extra to the available 0.19.0 release and
support its behavior, or wait for v0.20; in
tests/adapters/test_adapter_package_metadata.py lines 53-54, align the expected
constraint with that same tested version, and keep uv.lock and documentation
consistent.

---

Outside diff comments:
In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py`:
- Around line 432-454: Add a per-HermesRuntime operation lock that serializes
the full invoke_turn sequence, including _invoke_hermes_turn and
finalize_session, so concurrent invokes cannot share mutable agent, session, or
conversation state. Acquire and release this lock through the existing
cancellation task tracking, preserving cancellation behavior and ordered start →
invoke* → stop lifecycle operations. Add a regression test that launches
concurrent invocations on one runtime and verifies they execute serially.
- Around line 317-332: The Relay startup flow around the environment-application
logic at adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py:317-332 must
stop mutating process-global os.environ; isolate each runtime’s
HERMES_NEMO_RELAY_* configuration through a dedicated process or equivalent
runtime-scoped mechanism. Remove the nested environment restoration logic at
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py:515-523, including
its ownership snapshots, and ensure Relay state remains scoped to the resolved
Fabric runtime ID.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 4c1e0341-c6d6-458f-8797-fd6a48ede272

📥 Commits

Reviewing files that changed from the base of the PR and between 5fbdd5a and 1c911d1.

⛔ Files ignored due to path filters (2)
  • adapters/hermes/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • adapters/hermes/pyproject.toml
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
📜 Review details
⏰ Context from checks skipped due to timeout. (19)
  • GitHub Check: Preview docs
  • GitHub Check: Pre-commit
  • GitHub Check: Test (arm64)
  • GitHub Check: Test (Python 3.13, linux-amd64)
  • GitHub Check: Test (Python 3.11, linux-amd64)
  • GitHub Check: Test (Python 3.14, linux-amd64)
  • GitHub Check: Test (Python 3.13, windows-amd64)
  • GitHub Check: Test (Python 3.14, windows-amd64)
  • GitHub Check: Test (Python 3.11, windows-amd64)
  • GitHub Check: Test (Python 3.12, macos-arm64)
  • GitHub Check: Test (Python 3.14, macos-arm64)
  • GitHub Check: Test (Python 3.14, linux-arm64)
  • GitHub Check: Test (Python 3.12, windows-amd64)
  • GitHub Check: Test (Python 3.13, macos-arm64)
  • GitHub Check: Test (Python 3.11, linux-arm64)
  • GitHub Check: Test (Python 3.12, linux-amd64)
  • GitHub Check: Test (Python 3.13, linux-arm64)
  • GitHub Check: Test (Python 3.12, linux-arm64)
  • GitHub Check: Test (Python 3.11, macos-arm64)
🧰 Additional context used
📓 Path-based instructions (25)
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For any Rust change, run just test-rust and cargo fmt --all -- --check.

For Rust core, CLI, or shared runtime semantic changes, run Rust formatting and tests, and add Python tests when behavior is exposed through the SDK.

Use Rust stable tooling; format Rust code with cargo fmt --all, verify formatting with cargo fmt --all -- --check, and compile with cargo check --workspace --locked.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,Cargo.lock,pyproject.toml,package.json}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For new or updated dependencies, document the functional need, alternatives considered, and why the selected dependency is the narrowest fit.

Files:

  • adapters/hermes/pyproject.toml
**/*.{toml,lock}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a manifest or lockfile changes, run the license-diff script against origin/main, review transitive license changes, and run the attributions-rust and attributions-python pre-commit hooks.

Files:

  • adapters/hermes/pyproject.toml
**/*.{yml,yaml,toml,lock}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For CI or packaging changes, use maintain-ci or maintain-packaging, then run recipes and checks whose behavior changed.

Files:

  • adapters/hermes/pyproject.toml
{pyproject.toml,adapters/**/pyproject.toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

{pyproject.toml,adapters/**/pyproject.toml}: Update the literal project.version in the root setuptools project and every adapter pyproject.toml.
Keep internal exact-version requirements aligned: root nemo-fabric-* == <version> optional dependencies and each adapter's nemo-fabric-adapters-common == <version> dependency.

Files:

  • adapters/hermes/pyproject.toml
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,yaml,yml,sh,bash}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

TOML, YAML, and shell files must use the specified # SPDX copyright and Apache-2.0 license headers.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,pyproject.toml}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

**/{Cargo.toml,pyproject.toml}: Keep package names, dependency declarations, import paths, module names, and workspace or Python package metadata internally consistent.
Prefer the standard library, an existing dependency, or a small local implementation before adding a new direct dependency.
When multiple dependencies satisfy the requirement, prefer a maintained OSS option with clear SPDX metadata, a smaller transitive graph, and permissive licensing such as Apache-2.0, MIT, BSD, or ISC.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,pyproject.toml,Cargo.lock,uv.lock}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

**/{Cargo.toml,pyproject.toml,Cargo.lock,uv.lock}: For new dependencies, record the functional need, viable alternatives considered, why the selected dependency is the narrowest fit, and any unresolved licensing question.
After updating manifests or lockfiles, run uv run --no-project python scripts/licensing/license_diff.py --base-ref origin/main and review added packages and license changes.
Keep workspace, Python package, and lockfile versions aligned wherever the packaging contract requires alignment.

Files:

  • adapters/hermes/pyproject.toml
adapters/*/pyproject.toml

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/pyproject.toml: Keep leaf adapter runtime dependencies razor-thin and adapter-owned. Do not declare the wrapped harness/SDK or dependencies already declared by its supported package; provide dependency-free fallbacks for optional libraries.
Provide every installable Python leaf adapter with a harness extra and a full extra for package-installable integrations; provide a relay extra when importing NeMo Relay Python APIs, but not when Relay is an external executable.
For packaged harnesses, declare and document a harness version constraint supported by upstream contracts and test evidence; do not claim a broader range than the evidence supports.

Files:

  • adapters/hermes/pyproject.toml
adapters/*/{pyproject.toml,uv.lock}

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

Keep each adapter package independent and small, using Python package metadata and lock files as applicable.

Files:

  • adapters/hermes/pyproject.toml
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • adapters/hermes/pyproject.toml
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
tests/adapters/**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

tests/adapters/**/*.py: If an adapter or integration changes, run its focused tests.
For adapter behavior changes, run focused adapter tests under tests/adapters, then run just test-python.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
**/*.{py,pyi,rs}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
tests/**/*.{rs,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the corresponding Rust crate or the relevant area under tests/.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/python-tests/SKILL.md)

tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; async tests are automatically detected by the async runner.
Do not add -> None return type annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or AsyncMock, using the spec argument when necessary, rather than defining a new class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
If a fixture is needed in multiple test files, define it once in conftest.py rather than repeating it.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.
Use @pytest.mark.usefixtures when a fixture is needed but its returned value is unused or it returns no value.
Avoid defensive programming in tests; access expected values directly so missing data raises a clear failure, such as using results["data"] instead of results.get("data").
When adapter installation metadata changes, packaging metadata tests must directly assert that the root project depends unconditionally on the exact-version nemo-fabric-runtime distribution.
Packaging metadata tests must verify that each root harness extra delegates to the matching version of the leaf adapter's harness extra.
Packaging metadata tests must verify that bare leaf dependencies remain adapter-owned and that the root adapter-tests dependency group installs each leaf through its harness extra.
Packaging metadata tests must verify that every leaf provides full; only adapters importing NeMo Relay Python APIs provide relay, while adapters using an external Relay executable have full equal to harness.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
adapters/*/src/**/*.py

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/src/**/*.py: Implement adapters using the existing Fabric python or process runner and normalized request/result contracts; do not add a runner or one-off abstraction for a single adapter.
Treat normalized config, Fabric-resolved plans, and runtime_context as authoritative; reserve harness.settings for adapter-wide behavior and apply precedence in the order: normalized config, plans/context, harness settings, descriptor/default values.
Reject conflicting duplicate declarations and unsupported behavior with actionable errors naming the field and supported alternatives; never silently drop configuration.
Validate dependency versions, hooks, and credentials before harness invocation, and never expose credential values in outputs, errors, events, logs, or fixtures.
Forward only required system, selected credential, telemetry, and documented harness-specific environment variables; never forward or log unrelated environment values.
Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations. Emit one JSON lifecycle response per request on stdout and diagnostics on stderr.
Return harness-level invocation failures as successful lifecycle responses containing response: null, failed: true, and structured error fields (code, message, retryable, and optional metadata).
Do not emit NeMo Relay stream records on adapter stdout; return exactly one terminal lifecycle response while streaming occurs through the SDK-owned out-of-band endpoint.
Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.
Use start to initialize adapter-owned harness state, retain it for continuation across repeated invoke calls on the same runtime, and release it in stop.

Files:

  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🧠 Learnings (1)
📚 Learning: 2026-07-09T22:28:51.689Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 43
File: adapters/claude-sdk/src/nemo_fabric_adapters/claude_sdk/adapter.py:164-168
Timestamp: 2026-07-09T22:28:51.689Z
Learning: In the NeMo-Fabric adapters, treat path values used in Fabric adapter configuration (including logic like `_resolve_path` in adapter.py) as config-root-relative. Do not apply `Path.expanduser()` (or otherwise apply `~`/home or shell-style expansion), because it will make the resolved paths normalize inconsistently across adapters. Also, do not rely on or add any resolution behavior that uses `harness.settings.cwd` as an override point for these adapter paths—`harness.settings.cwd` is explicitly unsupported in this adapter context.

Applied to files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🪛 ast-grep (0.45.0)
tests/adapters/test_hermes_adapter.py

[info] 118-135: use jsonify instead of json.dumps for JSON output
Context: json.dumps(
{
"relay": {
"config": {
"opentelemetry": {
"enabled": True,
"endpoint": "https://otel.example/v1/traces",
"service_name": "fabric",
},
"openinference": {
"enabled": True,
"endpoint": "https://openinference.example/v1/traces",
"service_name": "fabric",
},
}
}
}
)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 Ruff (0.16.1)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py

[warning] 189-190: Use a single if statement instead of nested if statements

(SIM102)

🔇 Additional comments (3)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (1)

157-195: LGTM!

tests/adapters/test_hermes_adapter.py (1)

100-110: LGTM!

Also applies to: 113-168

adapters/common/src/nemo_fabric_adapters/common/utils.py (1)

223-224: LGTM!

Also applies to: 414-414

Comment thread adapters/hermes/pyproject.toml Outdated
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (3)

333-343: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Isolate ownership of the process Relay environment.

If two Hermes runtimes share a process, their environment snapshots interleave. If runtime A starts, runtime B starts, A stops, and B stops, B restores A's staged path after A has stopped. A later runtime can then load the stale Relay configuration.

Use a process-wide environment owner manager, or reject concurrent Hermes runtimes in one process. Restore the original baseline only after the last owner stops. Add an overlapping-runtime regression test.

As per coding guidelines, “Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.”

Also applies to: 526-534

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 333
- 343, Update the Relay environment lifecycle around the adapter’s startup and
shutdown methods to prevent overlapping Hermes runtimes from interleaving
snapshots and restorations. Introduce a process-wide ownership mechanism keyed
by the resolved Fabric runtime ID, or reject concurrent runtimes; ensure the
original environment baseline is restored only when the final owner stops, while
each runtime’s applied configuration remains isolated. Add a regression test
covering A starts, B starts, A stops, and B stops, verifying no stale Relay
configuration remains.

Source: Coding guidelines


443-459: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Finalize Relay when the Hermes turn fails.

If _invoke_hermes_turn() raises, finalize_session() does not run. Move finalization into a finally block when Relay is enabled, preserve the original turn error if finalization also fails, and add a regression test for this failure path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 443
- 459, Update the turn execution flow around _invoke_hermes_turn so
finalize_session runs in a finally block whenever _relay_plugin_config is
enabled, including when the Hermes turn raises. Preserve the original turn
exception if finalization also fails, and add a regression test covering this
failure path.

493-507: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Isolate Hermes runtime state and finalize failed turns.

  • HermesRuntime.start() mutates process-global environment variables, but stop() restores only Relay variables. A later runtime can inherit another runtime’s HOME, HERMES_HOME, or plugin path. Scope or serialize this state by runtime ID.
  • _invoke_hermes_turn() propagates exceptions. Therefore, finalize_session() does not run and the adapter does not return the required failed lifecycle response. Finalize in finally, normalize the exception, and add failed-turn coverage.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 493
- 507, Isolate process-global environment state in HermesRuntime.start/stop by
scoping or serializing it per runtime ID, including HOME, HERMES_HOME, and
plugin paths, and restore the matching state during stop. Update
_invoke_hermes_turn() to always call finalize_session() in a finally path,
normalize propagated exceptions into the failed-turn lifecycle response, and add
coverage for failed turns.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/hermes/pyproject.toml`:
- Around line 37-41: Pin nemo-relay to ==0.7.2 in both Hermes extras in
adapters/hermes/pyproject.toml, update the expected package metadata in
tests/adapters/test_adapter_package_metadata.py (lines 53-54), and regenerate
uv.lock so all resolved Relay versions and metadata match 0.7.2.

---

Outside diff comments:
In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py`:
- Around line 333-343: Update the Relay environment lifecycle around the
adapter’s startup and shutdown methods to prevent overlapping Hermes runtimes
from interleaving snapshots and restorations. Introduce a process-wide ownership
mechanism keyed by the resolved Fabric runtime ID, or reject concurrent
runtimes; ensure the original environment baseline is restored only when the
final owner stops, while each runtime’s applied configuration remains isolated.
Add a regression test covering A starts, B starts, A stops, and B stops,
verifying no stale Relay configuration remains.
- Around line 443-459: Update the turn execution flow around _invoke_hermes_turn
so finalize_session runs in a finally block whenever _relay_plugin_config is
enabled, including when the Hermes turn raises. Preserve the original turn
exception if finalization also fails, and add a regression test covering this
failure path.
- Around line 493-507: Isolate process-global environment state in
HermesRuntime.start/stop by scoping or serializing it per runtime ID, including
HOME, HERMES_HOME, and plugin paths, and restore the matching state during stop.
Update _invoke_hermes_turn() to always call finalize_session() in a finally
path, normalize propagated exceptions into the failed-turn lifecycle response,
and add coverage for failed turns.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: dd62e1b7-fa83-4259-89bb-a549987ac4f3

📥 Commits

Reviewing files that changed from the base of the PR and between 1c911d1 and 072ab71.

⛔ Files ignored due to path filters (2)
  • adapters/hermes/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • adapters/hermes/pyproject.toml
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
📜 Review details
⏰ Context from checks skipped due to timeout. (18)
  • GitHub Check: Preview docs
  • GitHub Check: Pre-commit
  • GitHub Check: Test (Python 3.14, linux-amd64)
  • GitHub Check: Test (Python 3.14, windows-amd64)
  • GitHub Check: Test (Python 3.11, windows-amd64)
  • GitHub Check: Test (Python 3.13, windows-amd64)
  • GitHub Check: Test (Python 3.13, macos-arm64)
  • GitHub Check: Test (Python 3.12, windows-amd64)
  • GitHub Check: Test (Python 3.12, linux-arm64)
  • GitHub Check: Test (Python 3.12, macos-arm64)
  • GitHub Check: Test (Python 3.13, linux-arm64)
  • GitHub Check: Test (Python 3.14, macos-arm64)
  • GitHub Check: Test (Python 3.11, linux-amd64)
  • GitHub Check: Test (Python 3.11, macos-arm64)
  • GitHub Check: Test (Python 3.12, linux-amd64)
  • GitHub Check: Test (Python 3.11, linux-arm64)
  • GitHub Check: Test (Python 3.13, linux-amd64)
  • GitHub Check: Test (Python 3.14, linux-arm64)
🧰 Additional context used
📓 Path-based instructions (25)
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,toml}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For any Rust change, run just test-rust and cargo fmt --all -- --check.

For Rust core, CLI, or shared runtime semantic changes, run Rust formatting and tests, and add Python tests when behavior is exposed through the SDK.

Use Rust stable tooling; format Rust code with cargo fmt --all, verify formatting with cargo fmt --all -- --check, and compile with cargo check --workspace --locked.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,Cargo.lock,pyproject.toml,package.json}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For new or updated dependencies, document the functional need, alternatives considered, and why the selected dependency is the narrowest fit.

Files:

  • adapters/hermes/pyproject.toml
**/*.{toml,lock}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a manifest or lockfile changes, run the license-diff script against origin/main, review transitive license changes, and run the attributions-rust and attributions-python pre-commit hooks.

Files:

  • adapters/hermes/pyproject.toml
**/*.{yml,yaml,toml,lock}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For CI or packaging changes, use maintain-ci or maintain-packaging, then run recipes and checks whose behavior changed.

Files:

  • adapters/hermes/pyproject.toml
{pyproject.toml,adapters/**/pyproject.toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

{pyproject.toml,adapters/**/pyproject.toml}: Update the literal project.version in the root setuptools project and every adapter pyproject.toml.
Keep internal exact-version requirements aligned: root nemo-fabric-* == <version> optional dependencies and each adapter's nemo-fabric-adapters-common == <version> dependency.

Files:

  • adapters/hermes/pyproject.toml
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • adapters/hermes/pyproject.toml
  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,yaml,yml,sh,bash}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

TOML, YAML, and shell files must use the specified # SPDX copyright and Apache-2.0 license headers.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,pyproject.toml}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

**/{Cargo.toml,pyproject.toml}: Keep package names, dependency declarations, import paths, module names, and workspace or Python package metadata internally consistent.
Prefer the standard library, an existing dependency, or a small local implementation before adding a new direct dependency.
When multiple dependencies satisfy the requirement, prefer a maintained OSS option with clear SPDX metadata, a smaller transitive graph, and permissive licensing such as Apache-2.0, MIT, BSD, or ISC.

Files:

  • adapters/hermes/pyproject.toml
**/{Cargo.toml,pyproject.toml,Cargo.lock,uv.lock}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

**/{Cargo.toml,pyproject.toml,Cargo.lock,uv.lock}: For new dependencies, record the functional need, viable alternatives considered, why the selected dependency is the narrowest fit, and any unresolved licensing question.
After updating manifests or lockfiles, run uv run --no-project python scripts/licensing/license_diff.py --base-ref origin/main and review added packages and license changes.
Keep workspace, Python package, and lockfile versions aligned wherever the packaging contract requires alignment.

Files:

  • adapters/hermes/pyproject.toml
adapters/*/pyproject.toml

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/pyproject.toml: Keep leaf adapter runtime dependencies razor-thin and adapter-owned. Do not declare the wrapped harness/SDK or dependencies already declared by its supported package; provide dependency-free fallbacks for optional libraries.
Provide every installable Python leaf adapter with a harness extra and a full extra for package-installable integrations; provide a relay extra when importing NeMo Relay Python APIs, but not when Relay is an external executable.
For packaged harnesses, declare and document a harness version constraint supported by upstream contracts and test evidence; do not claim a broader range than the evidence supports.

Files:

  • adapters/hermes/pyproject.toml
adapters/*/{pyproject.toml,uv.lock}

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

Keep each adapter package independent and small, using Python package metadata and lock files as applicable.

Files:

  • adapters/hermes/pyproject.toml
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • adapters/hermes/pyproject.toml
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
tests/adapters/**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

tests/adapters/**/*.py: If an adapter or integration changes, run its focused tests.
For adapter behavior changes, run focused adapter tests under tests/adapters, then run just test-python.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
**/*.{py,pyi,rs}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
tests/**/*.{rs,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the corresponding Rust crate or the relevant area under tests/.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/python-tests/SKILL.md)

tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; async tests are automatically detected by the async runner.
Do not add -> None return type annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or AsyncMock, using the spec argument when necessary, rather than defining a new class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
If a fixture is needed in multiple test files, define it once in conftest.py rather than repeating it.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.
Use @pytest.mark.usefixtures when a fixture is needed but its returned value is unused or it returns no value.
Avoid defensive programming in tests; access expected values directly so missing data raises a clear failure, such as using results["data"] instead of results.get("data").
When adapter installation metadata changes, packaging metadata tests must directly assert that the root project depends unconditionally on the exact-version nemo-fabric-runtime distribution.
Packaging metadata tests must verify that each root harness extra delegates to the matching version of the leaf adapter's harness extra.
Packaging metadata tests must verify that bare leaf dependencies remain adapter-owned and that the root adapter-tests dependency group installs each leaf through its harness extra.
Packaging metadata tests must verify that every leaf provides full; only adapters importing NeMo Relay Python APIs provide relay, while adapters using an external Relay executable have full equal to harness.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/adapters/test_adapter_package_metadata.py
  • tests/adapters/test_hermes_adapter.py
adapters/*/src/**/*.py

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/src/**/*.py: Implement adapters using the existing Fabric python or process runner and normalized request/result contracts; do not add a runner or one-off abstraction for a single adapter.
Treat normalized config, Fabric-resolved plans, and runtime_context as authoritative; reserve harness.settings for adapter-wide behavior and apply precedence in the order: normalized config, plans/context, harness settings, descriptor/default values.
Reject conflicting duplicate declarations and unsupported behavior with actionable errors naming the field and supported alternatives; never silently drop configuration.
Validate dependency versions, hooks, and credentials before harness invocation, and never expose credential values in outputs, errors, events, logs, or fixtures.
Forward only required system, selected credential, telemetry, and documented harness-specific environment variables; never forward or log unrelated environment values.
Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations. Emit one JSON lifecycle response per request on stdout and diagnostics on stderr.
Return harness-level invocation failures as successful lifecycle responses containing response: null, failed: true, and structured error fields (code, message, retryable, and optional metadata).
Do not emit NeMo Relay stream records on adapter stdout; return exactly one terminal lifecycle response while streaming occurs through the SDK-owned out-of-band endpoint.
Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.
Use start to initialize adapter-owned harness state, retain it for continuation across repeated invoke calls on the same runtime, and release it in stop.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🧠 Learnings (1)
📚 Learning: 2026-07-09T22:28:51.689Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 43
File: adapters/claude-sdk/src/nemo_fabric_adapters/claude_sdk/adapter.py:164-168
Timestamp: 2026-07-09T22:28:51.689Z
Learning: In the NeMo-Fabric adapters, treat path values used in Fabric adapter configuration (including logic like `_resolve_path` in adapter.py) as config-root-relative. Do not apply `Path.expanduser()` (or otherwise apply `~`/home or shell-style expansion), because it will make the resolved paths normalize inconsistently across adapters. Also, do not rely on or add any resolution behavior that uses `harness.settings.cwd` as an override point for these adapter paths—`harness.settings.cwd` is explicitly unsupported in this adapter context.

Applied to files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🪛 Ruff (0.16.1)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py

[warning] 160-162: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (3)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (1)

20-20: LGTM!

Also applies to: 147-209, 290-293, 367-372

tests/adapters/test_hermes_adapter.py (2)

62-121: LGTM!

Also applies to: 124-180


62-180: 📐 Maintainability & Code Quality

Run the required Python validation before merge.

This PR changes a Python adapter and its tests. Run the focused Hermes tests first, then run just test-python. The PR states that just test-python was not run because required tools were unavailable.

As per coding guidelines, “If Python code or a Python-facing adapter changes, run just test-python.”

Source: Coding guidelines

Comment thread adapters/hermes/pyproject.toml
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (2)

147-211: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a literal-tilde path regression test.

normalize_relay_output_dirs already roots relative paths at base_dir and does not use Path.expanduser() or harness.settings.cwd. Existing tests cover ordinary relative paths; add coverage for literal ~.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 147
- 211, Add a regression test for normalize_relay_output_dirs using a literal "~"
output path, verifying it remains rooted under base_dir rather than being
expanded via Path.expanduser() or harness.settings.cwd. Keep the existing
ordinary-relative-path tests and assert the expected normalized path.

Sources: Path instructions, Learnings


442-460: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use a lifecycle API supported by Hermes 0.19.0.

The lock resolves hermes-agent to 0.19.0, which does not provide hermes_cli.lifecycle. A Relay-enabled invoke therefore raises ModuleNotFoundError. Use a compatible finalization hook or raise the minimum Hermes version and update the lock. Run finalization in finally and preserve the original turn exception if cleanup fails.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py` around lines 442
- 460, Update invoke_turn’s Relay finalization to use a lifecycle API available
in Hermes 0.19.0, or raise the minimum Hermes dependency and regenerate the lock
accordingly. Move finalization into a finally block so it runs for both
successful and failed turns, while ensuring any finalization error does not
replace the original turn exception.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py`:
- Around line 147-211: Add a regression test for normalize_relay_output_dirs
using a literal "~" output path, verifying it remains rooted under base_dir
rather than being expanded via Path.expanduser() or harness.settings.cwd. Keep
the existing ordinary-relative-path tests and assert the expected normalized
path.
- Around line 442-460: Update invoke_turn’s Relay finalization to use a
lifecycle API available in Hermes 0.19.0, or raise the minimum Hermes dependency
and regenerate the lock accordingly. Move finalization into a finally block so
it runs for both successful and failed turns, while ensuring any finalization
error does not replace the original turn exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 4ac34667-4d43-4307-aa0e-55e5de3d8389

📥 Commits

Reviewing files that changed from the base of the PR and between 072ab71 and 629d1c1.

📒 Files selected for processing (1)
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
📜 Review details
⏰ Context from checks skipped due to timeout. (18)
  • GitHub Check: Preview docs
  • GitHub Check: Test (Python 3.11, linux-arm64)
  • GitHub Check: Test (Python 3.12, linux-arm64)
  • GitHub Check: Test (Python 3.13, windows-amd64)
  • GitHub Check: Test (Python 3.14, linux-amd64)
  • GitHub Check: Test (Python 3.12, windows-amd64)
  • GitHub Check: Test (Python 3.11, windows-amd64)
  • GitHub Check: Test (Python 3.14, windows-amd64)
  • GitHub Check: Test (Python 3.12, macos-arm64)
  • GitHub Check: Test (Python 3.14, macos-arm64)
  • GitHub Check: Test (Python 3.12, linux-amd64)
  • GitHub Check: Test (Python 3.11, macos-arm64)
  • GitHub Check: Test (Python 3.13, linux-arm64)
  • GitHub Check: Test (Python 3.13, macos-arm64)
  • GitHub Check: Test (Python 3.13, linux-amd64)
  • GitHub Check: Test (Python 3.11, linux-amd64)
  • GitHub Check: Test (Python 3.14, linux-arm64)
  • GitHub Check: Pre-commit
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi,rs}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
adapters/*/src/**/*.py

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/src/**/*.py: Implement adapters using the existing Fabric python or process runner and normalized request/result contracts; do not add a runner or one-off abstraction for a single adapter.
Treat normalized config, Fabric-resolved plans, and runtime_context as authoritative; reserve harness.settings for adapter-wide behavior and apply precedence in the order: normalized config, plans/context, harness settings, descriptor/default values.
Reject conflicting duplicate declarations and unsupported behavior with actionable errors naming the field and supported alternatives; never silently drop configuration.
Validate dependency versions, hooks, and credentials before harness invocation, and never expose credential values in outputs, errors, events, logs, or fixtures.
Forward only required system, selected credential, telemetry, and documented harness-specific environment variables; never forward or log unrelated environment values.
Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations. Emit one JSON lifecycle response per request on stdout and diagnostics on stderr.
Return harness-level invocation failures as successful lifecycle responses containing response: null, failed: true, and structured error fields (code, message, retryable, and optional metadata).
Do not emit NeMo Relay stream records on adapter stdout; return exactly one terminal lifecycle response while streaming occurs through the SDK-owned out-of-band endpoint.
Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.
Use start to initialize adapter-owned harness state, retain it for continuation across repeated invoke calls on the same runtime, and release it in stop.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🧠 Learnings (1)
📚 Learning: 2026-07-09T22:28:51.689Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 43
File: adapters/claude-sdk/src/nemo_fabric_adapters/claude_sdk/adapter.py:164-168
Timestamp: 2026-07-09T22:28:51.689Z
Learning: In the NeMo-Fabric adapters, treat path values used in Fabric adapter configuration (including logic like `_resolve_path` in adapter.py) as config-root-relative. Do not apply `Path.expanduser()` (or otherwise apply `~`/home or shell-style expansion), because it will make the resolved paths normalize inconsistently across adapters. Also, do not rely on or add any resolution behavior that uses `harness.settings.cwd` as an override point for these adapter paths—`harness.settings.cwd` is explicitly unsupported in this adapter context.

Applied to files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🔇 Additional comments (4)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (4)

466-466: The cancellation race remains unresolved.

asyncio.to_thread(invoke_turn) is not tracked. If invoke() is cancelled, stop() can clear and close _agent and _session_db and restore Relay environment state while invoke_turn() still runs. Track and await the worker before cleanup.

Also applies to: 502-535


450-460: 🗄️ Data Integrity & Integration

Verify the per-invocation finalization boundary.

HermesRuntime reuses one agent, session ID, and conversation history across invoke*, but finalize_session() is a session-finalization API. The current Hermes lifecycle implementation closes the Relay session and dispatches on_session_finalize; Hermes documentation describes this hook as session teardown. (raw.githubusercontent.com)

Confirm that the pinned Hermes versions support reopening the same session ID for each invocation. Add a two-invocation test for ATIF output and plugin lifecycle callbacks.


324-343: 📐 Maintainability & Code Quality

Run the required Python suite before merge.

The adapter changed, but the PR validation did not run just test-python. Run the focused Hermes tests first, then just test-python, and format the changed file before the final pass.

As per coding guidelines, if Python code or a Python-facing adapter changes, run just test-python.

Source: Coding guidelines


324-343: 🗄️ Data Integrity & Integration

Do not add a process-wide Relay lease for this path.

The lifecycle host owns one runtime and rejects a second start while it is active. Supported Hermes runtimes therefore do not overlap in one process.

			> Likely an incorrect or invalid review comment.

Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py`:
- Around line 514-525: Add a completion callback to the invoke_task created in
the invocation flow so it clears _active_invoke_task when that task finishes,
but only if the field still references the same task. Keep the existing finally
cleanup for already-completed tasks and preserve the current cancellation
behavior while ensuring later invoke() calls can proceed after an abandoned
worker turn completes.

In `@python/src/nemo_fabric/streaming.py`:
- Around line 527-541: Update the turn-scope matching logic around
_legacy_hermes_turn_id and _turn_scope_uuids so a scope-start record matched by
metadata.turn_id records its uuid in _turn_scope_uuids before returning.
Preserve existing matching behavior, and add regression coverage ensuring
descendants without metadata.turn_id remain included.
- Around line 562-570: Update _matches_turn_root() and the begin_stream()
listener lifecycle so late hermes.turn.start marks from a prior ATOF invocation
cannot establish a new root; require invocation/request correlation (or
guarantee the connection is drained before re-enabling the shared listener) in
addition to the existing Fabric and turn_id checks. Add a sequential regression
test covering a late mark emitted after one stream ends and before the next
begins, verifying stale records are not admitted.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 7e098f3e-9787-4e6a-af36-a768a4768a0a

📥 Commits

Reviewing files that changed from the base of the PR and between 629d1c1 and efa0ef4.

📒 Files selected for processing (6)
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_adapter.py
  • tests/adapters/test_hermes_streaming.py
  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
📜 Review details
⏰ Context from checks skipped due to timeout. (18)
  • GitHub Check: Preview docs
  • GitHub Check: Test (Python 3.12, windows-amd64)
  • GitHub Check: Test (Python 3.14, windows-amd64)
  • GitHub Check: Test (Python 3.13, windows-amd64)
  • GitHub Check: Test (Python 3.11, windows-amd64)
  • GitHub Check: Test (Python 3.13, macos-arm64)
  • GitHub Check: Test (Python 3.13, linux-amd64)
  • GitHub Check: Test (Python 3.14, macos-arm64)
  • GitHub Check: Test (Python 3.11, linux-arm64)
  • GitHub Check: Test (Python 3.13, linux-arm64)
  • GitHub Check: Test (Python 3.11, macos-arm64)
  • GitHub Check: Test (Python 3.14, linux-arm64)
  • GitHub Check: Test (Python 3.11, linux-amd64)
  • GitHub Check: Test (Python 3.12, linux-arm64)
  • GitHub Check: Test (Python 3.12, macos-arm64)
  • GitHub Check: Test (Python 3.12, linux-amd64)
  • GitHub Check: Test (Python 3.14, linux-amd64)
  • GitHub Check: Pre-commit
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{rs,py,pyi,json,yaml,yml}

📄 CodeRabbit inference engine (.agents/skills/contribute-api/SKILL.md)

Determine and update every affected public surface, including the CLI, PyO3 bindings, Python SDK, type stubs, schemas, and adapter contract, so they remain in parity.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*

📄 CodeRabbit inference engine (.agents/skills/karpathy-guidelines/SKILL.md)

**/*: Before implementing, explicitly state assumptions, surface ambiguity and tradeoffs, present multiple interpretations when relevant, and ask for clarification rather than silently deciding or proceeding when requirements are unclear.
Prefer the minimum code needed to solve the requested problem: avoid speculative features, unnecessary abstractions, unrequested flexibility, and handling of impossible scenarios; simplify overcomplicated solutions.
When editing existing code, make surgical changes only: do not modify unrelated code, comments, formatting, or pre-existing dead code; match the existing style, and remove only unused imports, variables, or functions introduced by your changes.
Define verifiable success criteria for each task, such as writing regression tests for bugs and invalid-input tests for validation, then verify the implementation against those criteria. For multi-step work, state a brief plan with a verification check for each step.

**/*: Always spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun, because the name begins with an “en” sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company; use trademark symbols with product names only when required by the document type or legal guidance.
Verify official capitalization, spacing, hyphenation, and spelling for NVIDIA and third-party product names; do not rewrite official product names for grammar or title-case rules.
Precede NVIDIA product names with NVIDIA on first mention when natural and accurate, and link the first mention when the destination helps the reader.
On first use, include the company name and full model qualifier when it helps identify the model; preserve official capitalization and punctuation, and use shorter family names only after establishing the full name.
For learning-oriented and developer content, do not force trademark symbols unless explicitly required; for press, ...

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py}

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

For native binding changes, run cargo check -p fabric-python --locked.

Use snake_case for functions and variables; use PascalCase for Rust types and Python classes.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If Python code or a Python-facing adapter changes, run just test-python.

In Python SDK, adapters, examples, and tests, follow the existing style, use type annotations for public APIs, and keep native binding declarations synchronized with their Rust implementations.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,pyi}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*.{rs,py,pyi}: If public configuration types change, confirm schema snapshot tests in just test-rust pass and review generated schema diffs.
For schema or public contract changes, run both language suites and review changes under schemas/ and generated API references.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{py,pyi,rs}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then just test-python; rebuild with just build-python when native code or packaging changes.

Public contract changes must keep native Python binding declarations synchronized with their Rust implementations.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{rs,py,toml}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

When editing version helpers, verify every nemo-fabric-* workspace package through Cargo metadata and reject a static version in python/pyproject.toml.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{toml,rs,py}

📄 CodeRabbit inference engine (.agents/skills/update-project-version/SKILL.md)

Avoid blind repository-wide replacement of version-like strings; distinguish package-version references from examples and unrelated dependency versions.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
**/*.{md,mdx,yml,py,rs,sh}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

Keep documentation aligned with current NeMo Fabric behavior, repository layout, entry points, commands, package names, APIs, bindings, and support claims.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • python/src/nemo_fabric/streaming.py
  • tests/adapters/test_hermes_streaming.py
  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
tests/**/*.{rs,py}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When adding functionality, include tests in the corresponding Rust crate or the relevant area under tests/.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • tests/adapters/test_hermes_streaming.py
tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/python-tests/SKILL.md)

tests/**/*.py: Use pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; async tests are automatically detected by the async runner.
Do not add -> None return type annotations to test functions.
When mocking a class, use unittest.mock.MagicMock or AsyncMock, using the spec argument when necessary, rather than defining a new class.
Prefix mocked class names with mock, not fake.
Prefer pytest fixtures over helper methods.
If a fixture is needed in multiple test files, define it once in conftest.py rather than repeating it.
Define fixtures using @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and a <fixture_name>_fixture function; specify scope only when it is not function.
Prefer pytest.mark.parametrize over separate tests for different input types.
Use @pytest.mark.usefixtures when a fixture is needed but its returned value is unused or it returns no value.
Avoid defensive programming in tests; access expected values directly so missing data raises a clear failure, such as using results["data"] instead of results.get("data").
When adapter installation metadata changes, packaging metadata tests must directly assert that the root project depends unconditionally on the exact-version nemo-fabric-runtime distribution.
Packaging metadata tests must verify that each root harness extra delegates to the matching version of the leaf adapter's harness extra.
Packaging metadata tests must verify that bare leaf dependencies remain adapter-owned and that the root adapter-tests dependency group installs each leaf through its harness extra.
Packaging metadata tests must verify that every leaf provides full; only adapters importing NeMo Relay Python APIs provide relay, while adapters using an external Relay executable have full equal to harness.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • tests/adapters/test_hermes_streaming.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/e2e/test_hermes_e2e.py
  • tests/python/test_streaming.py
  • tests/adapters/test_hermes_adapter.py
  • tests/adapters/test_hermes_streaming.py
tests/adapters/**/*.py

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

tests/adapters/**/*.py: If an adapter or integration changes, run its focused tests.
For adapter behavior changes, run focused adapter tests under tests/adapters, then run just test-python.

Files:

  • tests/adapters/test_hermes_adapter.py
  • tests/adapters/test_hermes_streaming.py
python/src/nemo_fabric/**/*.py

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

For Python API reference changes, update source docstrings under python/src/nemo_fabric/ instead of generated API reference files.

Files:

  • python/src/nemo_fabric/streaming.py
python/src/nemo_fabric/**/*.{py,rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure native extension naming and placement remain compatible with downstream consumers, including the editable maturin build producing nemo_fabric._native.

Files:

  • python/src/nemo_fabric/streaming.py
python/src/nemo_fabric/**/*

⚙️ CodeRabbit configuration file

python/src/nemo_fabric/**/*: Review Python SDK changes for typed API consistency, import-time dependency neutrality, async/session behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/src/nemo_fabric/streaming.py
adapters/*/src/**/*.py

📄 CodeRabbit inference engine (.agents/skills/contribute-adapter/SKILL.md)

adapters/*/src/**/*.py: Implement adapters using the existing Fabric python or process runner and normalized request/result contracts; do not add a runner or one-off abstraction for a single adapter.
Treat normalized config, Fabric-resolved plans, and runtime_context as authoritative; reserve harness.settings for adapter-wide behavior and apply precedence in the order: normalized config, plans/context, harness settings, descriptor/default values.
Reject conflicting duplicate declarations and unsupported behavior with actionable errors naming the field and supported alternatives; never silently drop configuration.
Validate dependency versions, hooks, and credentials before harness invocation, and never expose credential values in outputs, errors, events, logs, or fixtures.
Forward only required system, selected credential, telemetry, and documented harness-specific environment variables; never forward or log unrelated environment values.
Maintain one local adapter host per Fabric runtime across ordered startinvoke*stop operations. Emit one JSON lifecycle response per request on stdout and diagnostics on stderr.
Return harness-level invocation failures as successful lifecycle responses containing response: null, failed: true, and structured error fields (code, message, retryable, and optional metadata).
Do not emit NeMo Relay stream records on adapter stdout; return exactly one terminal lifecycle response while streaming occurs through the SDK-owned out-of-band endpoint.
Scope workspace, generated configuration, state, sessions, and artifacts to the resolved runtime context, and isolate stateful adapter instances by Fabric runtime ID.
Use start to initialize adapter-owned harness state, retain it for continuation across repeated invoke calls on the same runtime, and release it in stop.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public NeMo Fabric contracts.

Files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🧠 Learnings (2)
📚 Learning: 2026-06-28T04:03:32.877Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 26
File: python/tests/smoke_typed_config.py:163-177
Timestamp: 2026-06-28T04:03:32.877Z
Learning: In NVIDIA NeMo Fabric Python SDK serialization of `RuntimeCapabilities` (to satisfy the “parity contract” with Rust core and the CLI), do not emit metadata keys when the corresponding metadata is absent. Instead, omit those fields entirely so the produced JSON matches the Rust/CLI output (e.g., avoid `null`, empty objects, or placeholder metadata). During review, verify the serializer/builders follow this omission rule and that Python outputs/parity tests reflect the same shape.

Applied to files:

  • python/src/nemo_fabric/streaming.py
📚 Learning: 2026-07-09T22:28:51.689Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 43
File: adapters/claude-sdk/src/nemo_fabric_adapters/claude_sdk/adapter.py:164-168
Timestamp: 2026-07-09T22:28:51.689Z
Learning: In the NeMo-Fabric adapters, treat path values used in Fabric adapter configuration (including logic like `_resolve_path` in adapter.py) as config-root-relative. Do not apply `Path.expanduser()` (or otherwise apply `~`/home or shell-style expansion), because it will make the resolved paths normalize inconsistently across adapters. Also, do not rely on or add any resolution behavior that uses `harness.settings.cwd` as an override point for these adapter paths—`harness.settings.cwd` is explicitly unsupported in this adapter context.

Applied to files:

  • adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
🪛 Ruff (0.16.1)
tests/e2e/test_hermes_e2e.py

[warning] 121-121: Async functions should not call blocking HTTP methods

(ASYNC210)

tests/adapters/test_hermes_adapter.py

[warning] 227-227: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 906-906: Missing return type annotation for private function run_turn

(ANN202)


[warning] 906-906: Missing type annotation for **_kwargs

(ANN003)

adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py

[warning] 567-567: Do not catch blind exception: BaseException

(BLE001)

🔇 Additional comments (7)
adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py (2)

40-42: LGTM!

Also applies to: 54-66, 328-332, 363-382, 561-604


69-86: 🎯 Functional Correctness

No change needed: the sink lookup matches the staged v2 and v3 configuration paths.

tests/adapters/test_hermes_adapter.py (1)

8-14: LGTM!

Also applies to: 185-199, 222-236, 255-257, 900-951

python/src/nemo_fabric/streaming.py (1)

258-258: LGTM!

Also applies to: 312-312, 327-327, 580-583

tests/adapters/test_hermes_streaming.py (1)

85-89: LGTM!

tests/python/test_streaming.py (1)

934-975: LGTM!

tests/e2e/test_hermes_e2e.py (1)

10-14: LGTM!

Also applies to: 111-123, 187-195, 331-366, 396-402

Comment thread adapters/hermes/src/nemo_fabric_adapters/hermes/adapter.py
Comment thread python/src/nemo_fabric/streaming.py Outdated
Comment thread python/src/nemo_fabric/streaming.py
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
@zhongxuanwang-nv
zhongxuanwang-nv marked this pull request as ready for review August 10, 2026 23:05
@zhongxuanwang-nv
zhongxuanwang-nv requested a review from a team as a code owner August 10, 2026 23:05
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Signed-off-by: Zhongxuan Wang <daniewang@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant