Skip to content

feat: add LangChain Deep Agents adapter - #47

Merged
rapids-bot[bot] merged 14 commits into
NVIDIA:mainfrom
yczhang-nv:feat/deepagents-adapter
Jul 10, 2026
Merged

feat: add LangChain Deep Agents adapter#47
rapids-bot[bot] merged 14 commits into
NVIDIA:mainfrom
yczhang-nv:feat/deepagents-adapter

Conversation

@yczhang-nv

@yczhang-nv yczhang-nv commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Overview

Adds a LangChain Deep Agents (deepagents) harness adapter as a standalone, independently installable wheel (nemo-fabric-adapters-deepagents), following the per-adapter-package layout.

The adapter supports one-shot and multi-turn/resumed execution, keyed by the Fabric runtime_id via a persistent LangGraph SQLite checkpointer (mirroring the codex-cli adapter). It maps Fabric config onto create_deep_agent:

  • Model — builds a LangChain chat model from models.default (NVIDIA OpenAI-compatible by default, with an init_chat_model hook for other providers).
  • Workspace — roots the agent's FilesystemBackend at environment.workspace.
  • Tools / MCP — loads MCP servers as tools via langchain-mcp-adapters, filtered by the tools allow-list.
  • System prompt & harness settings — passed through.
  • Result — normalizes the final response, buffered messages, usage, LangGraph thread id, and errors into RunResult.
  • Telemetry — relay (via nemo_relay.integrations.deepagents, ATOF/ATIF artifacts) and native (telemetry.config OTLP/OpenInference export, no artifacts).
  • Doctor / preflight — checks that the deepagents package is importable and the model-provider credential is set.

Wiring: root pyproject.toml, justfile (python_projects, wheels), and the Python CI extras; plus a deepagents_config() example builder and a file-config profile.

Where should the reviewer start?

adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py — the run_deepagents flow (model/tools/workspace mapping, runtime_id-keyed resume, and the unified relay/native telemetry path). tests/adapters/test_deepagents.py is the mocked adapter contract; tests/e2e/test_deepagents.py is the opt-in real smoke.

Validation: just test-python (full suite) passes; the adapter's mocked tests cover one-shot normalization, runtime resume, relay + native telemetry, MCP/allow-list mapping, workspace rooting, and preflight failures.

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

  • N/A — no associated GitHub issue.

  • 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.

Summary by CodeRabbit

  • New Features
    • Added a LangChain Deep Agents adapter with a dedicated package, adapter configuration, and an example Deep Agents profile.
  • Documentation
    • Updated core concepts to reference the Deep Agents adapter and added detailed adapter documentation.
  • Bug Fixes
    • Improved Deep Agents runtime normalization, deterministic session/thread reuse with persisted state, tool allow-list enforcement, and telemetry handling.
  • Tests
    • Added comprehensive unit tests plus opt-in end-to-end smoke tests (oneshot, multi-turn resume, and doctor).
  • Chores
    • Updated CI, packaging, and dependency groups to include the Deep Agents extra.

Add a Deep Agents (deepagents) harness adapter as a standalone wheel
(nemo-fabric-adapters-deepagents), supporting one-shot and multi-turn/
resumed execution keyed by the Fabric runtime_id via a persistent LangGraph
SQLite checkpointer.

- Map Fabric input, model config (NVIDIA OpenAI-compatible default with a
  generic init_chat_model hook), workspace (FilesystemBackend root), system
  prompt, allowed tools, and MCP servers (via langchain-mcp-adapters) onto
  create_deep_agent; normalize response, messages, usage, LangGraph thread id,
  and errors into the Fabric result.
- Telemetry through relay (nemo_relay.integrations.deepagents, ATOF/ATIF
  artifacts) and native (telemetry.config OTLP/OpenInference export, no
  artifacts).
- Adapter preflight covering the deepagents package and model credential, plus
  the descriptor requirement.env doctor check.
- Mocked adapter tests and an opt-in real integration test; a deepagents_config
  example builder and a file-config profile.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jul 9, 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

Adds a LangChain Deep Agents adapter for Fabric, including runtime execution, model and tool resolution, checkpointing, telemetry, packaging, documentation, example configuration, CI wiring, and unit/integration tests.

Changes

Deep Agents Adapter

Layer / File(s) Summary
Adapter packaging, metadata, and documentation
adapters/deepagents/...
Adds adapter metadata, packaging constraints, installation guidance, runtime behavior documentation, and telemetry documentation.
Entrypoints, preflight, and model construction
adapters/deepagents/src/.../adapter.py
Adds CLI and SDK entrypoints, dependency/API-key validation, provider selection, endpoint resolution, and chat model construction.
Workspace, tools, skills, and agent wiring
adapters/deepagents/src/.../adapter.py
Maps workspace roots, MCP connections, tool allow-lists, skill paths, and Deep Agents settings into agent inputs.
Runtime resume, checkpointing, and execution
adapters/deepagents/src/.../adapter.py
Implements runtime thread persistence, SQLite checkpointing, streaming invocation, and event buffering.
Telemetry and output normalization
adapters/deepagents/src/.../adapter.py
Supports relay/native observability and normalizes messages, responses, usage, costs, errors, and telemetry artifacts.
Adapter and integration validation
tests/adapters/test_deepagents.py, tests/e2e/test_deepagents.py, tests/fixtures/.../deepagents.yaml
Tests adapter execution, mappings, failures, telemetry, checkpointing, normalization, runtime resume, and opt-in integration flows.
Build, CI, README, and example configuration
.github/workflows/ci_python.yml, justfile, pyproject.toml, README.md, examples/code_review_agent/*
Wires the adapter into CI, project tooling, dependency groups, documentation, and the code-review example configuration.

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

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Adapter
  participant ChatModel
  participant DeepAgents
  participant Checkpointer

  Client->>Adapter: run(payload)
  Adapter->>ChatModel: build_chat_model(payload)
  Adapter->>Checkpointer: load or open runtime state
  Adapter->>DeepAgents: invoke_agent(agent_kwargs, user_message, thread_id)
  DeepAgents-->>Adapter: streamed result and events
  Adapter->>Checkpointer: save thread_id and close checkpoint
  Adapter-->>Client: normalized Fabric output
Loading

Possibly related PRs

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.33% 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
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the new Deep Agents adapter.
Description check ✅ Passed All required sections are present; the Related Issues entry is a placeholder, but the template is otherwise mostly complete.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@linear

linear Bot commented Jul 9, 2026

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: 9

Caution

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

⚠️ Outside diff range comments (1)
README.md (1)

153-155: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Adapter guides list is missing the new Deep Agents README link.

Core Concepts now mentions adapters/deepagents/, but this "Adapter guides" bullet only links Hermes SDK, Hermes CLI, and Codex CLI READMEs. Since a new adapters/deepagents/README.md is added in this PR, it should be linked here too for consistency.

📝 Proposed fix
 - Adapter guides: [Hermes SDK](adapters/hermes-sdk/README.md),
   [Hermes CLI](adapters/hermes-cli/README.md), and
-  [Codex CLI](adapters/codex-cli/README.md).
+  [Codex CLI](adapters/codex-cli/README.md), and
+  [LangChain Deep Agents](adapters/deepagents/README.md).
🤖 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 `@README.md` around lines 153 - 155, The “Adapter guides” bullet in README is
missing the new Deep Agents guide, so update that list to include the
adapters/deepagents/README.md link alongside the existing Hermes SDK, Hermes
CLI, and Codex CLI entries. Keep the wording and ordering consistent with the
current Adapter guides section, and make sure the new Deep Agents README is
referenced from the same bullet where the other adapter guide links live.

Source: Path instructions

🤖 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/deepagents/pyproject.toml`:
- Around line 16-24: The dependency list in the deepagents pyproject is missing
upper bounds for langchain-mcp-adapters and langgraph-checkpoint-sqlite, unlike
the other pinned packages. Update the dependency spec in the pyproject.toml for
those two entries to include compatible upper version limits, matching the
existing pattern used by deepagents, langchain, and langgraph so future breaking
releases are not pulled in unexpectedly.

In `@adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py`:
- Around line 307-343: The checkpointer is opened before the guarded scope, so
failures in `resolve_tools`, `resolve_backend`, or `resolve_observability` can
skip `close_checkpointer` and leak the SQLite connection. Move the
`open_checkpointer` setup for `checkpointer`/`thread_id` into the same `try`
block in the deepagents adapter flow, or otherwise ensure acquisition and
cleanup are wrapped together so `finally` always runs. Keep `observability`
available where it is later reused, but make sure the lifecycle of
`checkpointer` is fully covered by the cleanup path.
- Around line 269-280: The open_checkpointer() function creates a synchronous
SqliteSaver but the runtime uses asynchronous agent.ainvoke(), causing
incompatibility with runtime_id resume functionality. Replace the import
statement to use AsyncSqliteSaver from langgraph.checkpoint.sqlite.aio instead
of SqliteSaver, update the SqliteSaver.from_conn_string() call to use
AsyncSqliteSaver.from_conn_string(), and ensure the context manager and cleanup
code (referenced around lines 372-380) are also updated to properly handle the
async checkpointer cleanup.

In `@tests/adapters/test_deepagents.py`:
- Around line 119-142: The shared test setup helpers are implemented as local
helper functions instead of pytest fixtures, which conflicts with the test
guidelines and makes reuse across test files harder. Convert _payload,
_install_fake_relay, and _install_fake_langgraph in test_deepagents.py into
fixtures (or move them to conftest.py if they must be shared with other test
modules) and update the tests to consume those fixtures rather than calling
helper methods directly.
- Around line 31-117: The test setup is using hand-rolled Fake classes instead
of the project’s required mock style. Replace `_FakeChatOpenAI`,
`_FakeFilesystemBackend`, `_FakeAgent`, `_FakeSaverCM`, `_FakeSaver`, and
`_FakeSqliteSaver` with `unittest.mock.MagicMock` or `AsyncMock` instances (use
`spec` where appropriate) inside `fake_sdks_fixture` and
`_install_fake_langgraph`, and rename any helper mock variables with a `mock`
prefix. Keep the same behavior captured in `create_deep_agent`,
`_FakeAgent.ainvoke`, and `SqliteSaver.from_conn_string`, but implement it
through mocks rather than custom classes.
- Around line 164-165: The assertions in the deepagents tests are using
defensive `.get()` lookups on expected dictionaries, which should be replaced
with direct key access so missing keys fail loudly. अपडेट the assertions in the
relevant test cases in test_deepagents.py to use direct indexing on
fake_sdks["create_kwargs"] and calls instead of .get(), keeping the checks in
the existing test functions that verify system_prompt, instructions, and call
contents.
- Around line 318-322: The _FakeMCPClient test helper has a mutable class-level
default for connections that triggers RUF012; update the class definition to
make the intent explicit by marking connections as a ClassVar, or remove the
class attribute entirely and keep the reassignment inside __init__. Preserve the
existing _FakeMCPClient behavior while ensuring the class no longer advertises a
mutable default shared across instances.

In `@tests/e2e/test_deepagents.py`:
- Around line 30-31: Remove the `-> None` return type annotation from the test
functions in `test_deepagents_oneshot_and_runtime` and `test_deepagents_doctor`
so they match the repo’s test conventions. Update the function signatures only,
keeping the existing test bodies and any calls like `_require_integration()`
unchanged.
- Around line 19-27: Convert the integration gate from the plain helper
`_require_integration` into a pytest fixture in `tests/e2e/test_deepagents.py`,
since the repo prefers fixtures over helper methods for test gating. Make the
gate run as a fixture with no return value, then attach it to the relevant tests
using `@pytest.mark.usefixtures("_require_integration")` (or equivalent fixture
wiring) instead of calling it at the top of each test. Keep the existing checks
for `RUN_FABRIC_DEEPAGENTS_INTEGRATION`, `deepagents`, `nemo_fabric._native`,
and `NVIDIA_API_KEY` inside that fixture.

---

Outside diff comments:
In `@README.md`:
- Around line 153-155: The “Adapter guides” bullet in README is missing the new
Deep Agents guide, so update that list to include the
adapters/deepagents/README.md link alongside the existing Hermes SDK, Hermes
CLI, and Codex CLI entries. Keep the wording and ordering consistent with the
current Adapter guides section, and make sure the new Deep Agents README is
referenced from the same bullet where the other adapter guide links live.
🪄 Autofix (Beta)

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: 38698144-5f69-429a-8266-ef8c9e0624cd

📥 Commits

Reviewing files that changed from the base of the PR and between 5ceb133 and 21f0618.

⛔ Files ignored due to path filters (2)
  • adapters/deepagents/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • .github/workflows/ci_python.yml
  • README.md
  • adapters/deepagents/README.md
  • adapters/deepagents/fabric-adapter.json
  • adapters/deepagents/pyproject.toml
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/__init__.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
  • examples/code_review_agent/__init__.py
  • examples/code_review_agent/config.py
  • justfile
  • pyproject.toml
  • tests/adapters/test_deepagents.py
  • tests/e2e/test_deepagents.py
  • tests/fixtures/file-config-agent/profiles/deepagents.yaml
📜 Review details
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{py,pyi}

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

**/*.{py,pyi}: If Python code or a Python-facing adapter changed, run just test-python.
For Python SDK or PyO3 binding changes, use python-tests, run focused pytest tests first, then run just test-python; rebuild with just build-python when native code or packaging changed.

Files:

  • examples/code_review_agent/__init__.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/__init__.py
  • examples/code_review_agent/config.py
  • tests/e2e/test_deepagents.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
  • tests/adapters/test_deepagents.py
**/*

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

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

Files:

  • examples/code_review_agent/__init__.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/__init__.py
  • adapters/deepagents/fabric-adapter.json
  • adapters/deepagents/README.md
  • tests/fixtures/file-config-agent/profiles/deepagents.yaml
  • examples/code_review_agent/config.py
  • README.md
  • adapters/deepagents/pyproject.toml
  • pyproject.toml
  • justfile
  • tests/e2e/test_deepagents.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
  • tests/adapters/test_deepagents.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 Fabric contracts.

Files:

  • examples/code_review_agent/__init__.py
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/__init__.py
  • adapters/deepagents/fabric-adapter.json
  • adapters/deepagents/README.md
  • examples/code_review_agent/config.py
  • adapters/deepagents/pyproject.toml
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
**/README.md

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

Update relevant adapter or example README.md files when examples or adapters have changed

Files:

  • adapters/deepagents/README.md
  • README.md
**/*.{md,mdx}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

**/*.{md,mdx}: Spell NVIDIA in all caps; do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun because NVIDIA starts 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 the document type or legal guidance requires them.
Verify official capitalization, spacing, and hyphenation for NVIDIA product names.
Precede NVIDIA product names with NVIDIA on first mention when it is natural and accurate.
Link the first mention of a product name when the destination helps the reader.
Do not rewrite product names for grammar or title-case rules.
Preserve third-party product names according to the owner's spelling.
Include the company name and full model qualifier on first use when it helps identify the model.
Preserve the official capitalization and punctuation of model names.
Use shorter family names only after the full model name is established.
For learning-oriented docs, technical blog posts, GTC sessions, tutorials, and developer guides: do not force trademark symbols unless the source, platform, or legal guidance explicitly requires them.
For learning-oriented docs, technical blog posts, GTC sessions, tutorials, and developer guides: keep the product name accurate and consistent.
For press releases, product landing pages, packaging, sales content, or legal copy: follow the current NVIDIA trademark and copyright guidance.
For press releases, product landing pages, packaging, sales content, or legal copy: attribute trademarks on first use when required.
For press releases, product landing pages, packaging, sales content, or legal copy: use the required trademark symbol for the specific product or service.
For press releases, product landing pages, packaging, sales content, or legal copy: do not invent trademark attributions; verify current legal copy.
If the platform requires legal copy, confirm the current source of truth instead...

Files:

  • adapters/deepagents/README.md
  • README.md
**/*.{md,mdx,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-technical-docs.md)

**/*.{md,mdx,rst}: When reviewing technical documentation, verify that commands, examples, paths, APIs, and support claims match the current repository.
Make technical documents easy to scan by fixing headings, lead-in sentences, lists, tables, and procedure shape.
Preserve exact code, command, API, package, and UI strings unless they are factually wrong.
Prefer focused findings over broad rewrites.
Use title case consistently in technical documentation headings.
Avoid quotation marks, ampersands, and exclamation marks in technical documentation headings.
Keep product, event, research, and whitepaper names in their official title case.
Use title case for table headers.
Do not force social-media sentence case into technical documentation.
Format code elements, commands, parameters, package names, and expressions in monospace.
Format directories, file names, and paths in monospace.
Use angle brackets inside monospace for variables inside paths.
Format error messages and strings with quotation marks, using code formatting when that is clearer.
Format UI buttons, menus, fields, and labels in bold.
Use angle brackets between UI labels for menu paths.
Use italics on first use for new terms, and only when the term is introduced.
Italicize publication titles.
Write keyboard shortcuts in plain text.
Use Owner/repo link text for GitHub repositories.
Introduce every code block with a complete sentence.
Do not make a code block complete the grammar of the previous sentence.
Do not continue a sentence after a code block.
Use syntax highlighting when the format supports it.
Avoid the word "snippet" unless the surrounding documentation already uses it as a term of art.
Keep inline method, function, and class references consistent with nearby docs, and omit empty parentheses in prose when no call is shown.
Use descriptive anchor text that matches the destination title when possible.
Avoid raw URLs in running text.
Avoid generic anchors such as "here," "this page," and "read more....

Files:

  • adapters/deepagents/README.md
  • README.md
**/*.{md,mdx,rst,txt}

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

**/*.{md,mdx,rst,txt}: If documentation or examples changed, run just docs when practical and verify documented commands against the current repository.
For documentation-only changes, use contribute-docs and review-doc-style; run just docs for docs-site or generated-reference changes.

Files:

  • adapters/deepagents/README.md
  • README.md
.github/workflows/*.{yml,yaml}

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

.github/workflows/*.{yml,yaml}: Put permissions: on each job that needs token access.
Avoid workflow-level permissions: unless the repository intentionally centralizes them and the inheritance tradeoff is documented.
Keep third-party actions pinned to full commit SHAs and preserve the readable version comment after the SHA.
Prefer action-native or ecosystem-native caching over generic actions/cache.
Use lockfiles or dependency manifests to drive cache invalidation.
Keep deploy and publish permissions isolated to the jobs that need them.
Read both caller and callee when a workflow uses workflow_call.
The default minimum for checkout-based build, test, docs, and packaging jobs is contents: read.
pull-requests: read is required for PR metadata lookup jobs.
pages: write and id-token: write should be limited to Pages deployment jobs and any caller that invokes them through a reusable workflow.
For reusable workflows, the caller must grant every permission the called jobs require; the callee cannot elevate beyond what the caller provides.
Prefer astral-sh/setup-uv cache support with cache-dependency-glob anchored to uv.lock.
Prefer Swatinem/rust-cache with explicit shared-key and workspaces instead of ad hoc target-directory caching.
Avoid caching generated outputs that can hide stale behavior unless the repo already relies on them deliberately.
Each job should have the minimum permissions it needs.
Reusable workflow callers should grant only the scopes their callees require.
Every external action must be pinned to a full SHA.
Cache settings should be tied to lockfiles, manifests, or explicit tool versions.
Secrets should only be passed to the jobs that consume them.
Python, Rust, and documentation jobs should remain aligned with their lockfiles and justfile recipes.
Concurrency, branch filters, and documentation publish guards should still reflect repository intent.

Files:

  • .github/workflows/ci_python.yml
{.github/workflows/ci_python.yml,.github/workflows/ci_rust.yml}

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

Keep CI workflow install commands and example commands in the Python and Rust workflows consistent with the current package names and build/install flow.

Files:

  • .github/workflows/ci_python.yml
{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/fixtures/file-config-agent/profiles/deepagents.yaml
  • tests/e2e/test_deepagents.py
  • tests/adapters/test_deepagents.py
{README.md,docs/index.yml,docs/**/*.{md,mdx}}

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

Update entry-point docs when examples or reading paths change

Files:

  • README.md
{README.md,docs/index.yml}

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

Update README.md or docs/index.yml when entry points changed

Files:

  • README.md
{docs/**,README.md,AGENTS.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,AGENTS.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency with generated schemas.

Files:

  • README.md
**/*.{rs,toml}

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

If the PyO3 bridge or package metadata changed, run just build-python and cargo check -p fabric-python --locked.

Files:

  • adapters/deepagents/pyproject.toml
  • pyproject.toml
pyproject.toml

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

Keep the root pyproject.toml aligned with NeMo Fabric's Rust/Python packaging and maturin build configuration.

Files:

  • pyproject.toml
justfile

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

Keep local commands aligned with the corresponding justfile recipes when they provide equivalent behavior.

Keep justfile build, test, clean, and documentation recipes aligned with the current packaging and release workflow.

Use just --fmt --check for Justfile and patch hygiene.

Files:

  • justfile
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 test functions; async tests should be auto-detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, using spec when necessary.
Name mocked classes with a mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures across test files; if a fixture is needed in multiple test files, place it in conftest.py.
When creating a fixture, use the pattern @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) followed by def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.
If a fixture is needed for a test but does not return a value or its value is unused, use @pytest.mark.usefixtures.
If you need to modify environment variables in a test, use os.environ; tests/conftest.py provides an autouse restore_environ_fixture that restores the environment after each test, so monkeypatch.setenv is unnecessary.
Avoid defensive programming in tests; access expected dictionary keys directly (for example, results["data"]) instead of using .get(), so failures raise loudly and clearly.

Files:

  • tests/e2e/test_deepagents.py
  • tests/adapters/test_deepagents.py
tests/adapters/**/*.py

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

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

Files:

  • tests/adapters/test_deepagents.py
🧠 Learnings (1)
📚 Learning: 2026-06-29T22:34:52.407Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 27
File: adapters/codex-cli/fabric-adapter.json:13-15
Timestamp: 2026-06-29T22:34:52.407Z
Learning: In NeMo-Fabric adapter manifest files (e.g., `*/fabric-adapter.json`), keep `config.accepts` limited to the top-level Fabric capability sections that `resolve_capability_plan` consumes (such as `models`, `tools`, `mcp`, `skills`, `telemetry`). Do not add adapter-owned `harness.settings` keys to `config.accepts`; `harness.settings` should remain adapter-owned and be passed through unchanged.

Applied to files:

  • adapters/deepagents/fabric-adapter.json
🪛 ast-grep (0.44.1)
adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py

[info] 34-34: use jsonify instead of json.dumps for JSON output
Context: json.dumps(output, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 263-263: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"runtime_id": runtime_id, "thread_id": thread_id}, indent=2)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)


[info] 299-299: use jsonify instead of json.dumps for JSON output
Context: json.dumps(user_message, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 Ruff (0.15.20)
adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py

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

(TRY003)


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

(TRY003)


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

(TRY003)


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

(TRY003)


[warning] 139-139: Dynamically typed expressions (typing.Any) are disallowed in resolve_backend

(ANN401)


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

(TRY003)


[warning] 269-269: Dynamically typed expressions (typing.Any) are disallowed in open_checkpointer

(ANN401)


[warning] 282-282: Dynamically typed expressions (typing.Any) are disallowed in checkpointer

(ANN401)


[warning] 339-339: Do not catch blind exception: Exception

(BLE001)


[warning] 372-372: Dynamically typed expressions (typing.Any) are disallowed in invoke_agent

(ANN401)


[warning] 391-391: Boolean-typed positional argument in function definition

(FBT001)


[warning] 405-405: Boolean positional value in function call

(FBT003)


[warning] 411-411: Boolean positional value in function call

(FBT003)


[warning] 425-425: Dynamically typed expressions (typing.Any) are disallowed in result_state

(ANN401)


[warning] 458-458: Dynamically typed expressions (typing.Any) are disallowed in result_state

(ANN401)


[warning] 464-464: Use a list comprehension to create a transformed list

(PERF401)


[warning] 468-468: Dynamically typed expressions (typing.Any) are disallowed in message

(ANN401)


[warning] 481-481: Dynamically typed expressions (typing.Any) are disallowed in _final_response

(ANN401)


[warning] 506-506: Dynamically typed expressions (typing.Any) are disallowed in callable_obj

(ANN401)

tests/adapters/test_deepagents.py

[warning] 32-32: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


[warning] 37-37: Dynamically typed expressions (typing.Any) are disallowed in **_kwargs

(ANN401)


[warning] 66-66: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


[warning] 90-90: Remove quotes from type annotation

Remove quotes

(UP037)


[warning] 93-93: Dynamically typed expressions (typing.Any) are disallowed in *_exc

(ANN401)


[warning] 184-184: Dynamically typed expressions (typing.Any) are disallowed in *args

(ANN401)


[warning] 184-184: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


[warning] 184-184: Dynamically typed expressions (typing.Any) are disallowed in fake_find_spec

(ANN401)


[warning] 198-198: Dynamically typed expressions (typing.Any) are disallowed in **_kwargs

(ANN401)


[warning] 198-198: Dynamically typed expressions (typing.Any) are disallowed in boom

(ANN401)


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

(TRY003)


[warning] 212-212: Dynamically typed expressions (typing.Any) are disallowed in **_

(ANN401)


[warning] 219-219: Missing return type annotation for private function plugin_ctx

(ANN202)


[warning] 219-219: Dynamically typed expressions (typing.Any) are disallowed in _config

(ANN401)


[warning] 252-252: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)


[warning] 294-294: Assertion should be broken down into multiple parts

Break down assertion into multiple parts

(PT018)


[warning] 319-319: Mutable default value for class attribute

(RUF012)

🔇 Additional comments (17)
tests/adapters/test_deepagents.py (1)

145-207: LGTM! Coverage of one-shot normalization, preflight failures (missing API key, missing deepagents package), error normalization, relay/native telemetry, workspace-rooted backend, MCP tool filtering, and resume/thread-id behavior is thorough and matches the adapter's described contract.

Also applies to: 239-366

adapters/deepagents/src/nemo_fabric_adapters/deepagents/__init__.py (1)

1-5: LGTM!

adapters/deepagents/fabric-adapter.json (2)

10-12: 🗄️ Data Integrity & Integration | ⚡ Quick win

Static requirements.env doesn't track configurable api_key_env.

The manifest hardcodes NVIDIA_API_KEY as the required env var, but the README documents that models.default.api_key_env is configurable per-config. If a user sets a different api_key_env, this manifest requirement will not reflect the actual variable the adapter needs, potentially causing incorrect preflight pass/fail behavior.

Since adapter.py's preflight logic isn't in this review scope, please confirm whether preflight checks read requirements.env from this manifest or independently resolve api_key_env from the runtime config.


1-9: LGTM!

Also applies to: 13-19

adapters/deepagents/pyproject.toml (1)

1-15: LGTM!

Also applies to: 25-35

adapters/deepagents/README.md (2)

41-56: 🎯 Functional Correctness

Runtime/checkpointer behavior not verifiable in this cohort.

The described resume semantics (fresh runtime_id per one-shot run, thread ID correlation, SQLite checkpointer reuse) are consistent with the PR objectives, but adapter.py implementing this logic isn't part of this review batch.

Please confirm this matches the actual run_deepagents/checkpointer implementation in adapter.py once available for review.


1-40: LGTM!

Also applies to: 57-71

examples/code_review_agent/config.py (1)

161-186: LGTM!

.github/workflows/ci_python.yml (1)

58-58: LGTM!

README.md (1)

122-124: LGTM!

justfile (1)

13-13: LGTM!

Also applies to: 331-331

pyproject.toml (1)

29-32: LGTM!

Also applies to: 45-48, 73-73, 104-104

examples/code_review_agent/__init__.py (1)

10-10: LGTM!

Also applies to: 25-25

tests/e2e/test_deepagents.py (2)

40-60: LGTM!


63-74: LGTM!

tests/fixtures/file-config-agent/profiles/deepagents.yaml (2)

1-37: LGTM!


8-13: 🗄️ Data Integrity & Integration

harness.settings.workspace is consumed as a fallback, so this is not dead config. resolve_backend() uses environment.workspace first and falls back to common_utils.settings_payload(payload).get("workspace"); the duplicate value here is redundant, but active.

			> Likely an incorrect or invalid review comment.

Comment thread adapters/deepagents/pyproject.toml
Comment thread adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py Outdated
Comment thread adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/e2e/test_deepagents.py Outdated
Comment thread tests/e2e/test_deepagents.py Outdated
yczhang-nv and others added 2 commits July 9, 2026 15:31
- Default to NVIDIA's endpoint only for nvidia/unspecified providers; a plain
  openai provider now uses ChatOpenAI's own default endpoint.
- Read the tools allow-list from config.tools (the routed plan only marks
  native.tools_configured), and resolve the MCP url/command from
  McpServerPlan.url (there is no command field) so stdio servers work.
- Map routed native.skill_paths to Deep Agents skills, and pass
  harness.settings.deepagents through to create_deep_agent.
- Stream the run to buffer per-step events; surface token usage and cost when
  the provider reports it.
- Add nemo-relay to the adapter dependencies so relay and native telemetry work
  from the standalone wheel / documented install.
- Drop the provider-specific static NVIDIA_API_KEY doctor requirement; the
  runtime preflight validates the configured credential and deepagents import.
- Regenerate lockfiles with a current uv (restores lock revision 3, focused diff).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Address Deep Agents adapter review round 2:
- Use AsyncSqliteSaver (langgraph.checkpoint.sqlite.aio) instead of the sync
  SqliteSaver, whose async methods raise NotImplementedError under astream, so
  real one-shot and multi-turn runs no longer fail. Acquire/close it async.
- Enforce the config.tools allow-list across the full tool surface (Deep Agents
  built-ins such as write_file/execute/task and MCP tools) via a gating
  middleware, rather than only filtering the MCP tools list.
- Acquire the async checkpointer inside the guarded try/finally, after tools,
  backend, and observability are resolved, so a setup failure can no longer leak
  the SQLite connection in the inline runtime.
- Add real-stack regression tests: a real LangGraph graph + AsyncSqliteSaver
  driven via astream, and the allow-list gating middleware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@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

Caution

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

⚠️ Outside diff range comments (1)
tests/adapters/test_deepagents.py (1)

357-362: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename the MCP test to match middleware-based enforcement.

The assertion intentionally passes both tools to Deep Agents; filtering now happens in middleware. Rename the surrounding test so it no longer claims the tool list is filtered.

🤖 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 `@tests/adapters/test_deepagents.py` around lines 357 - 362, Rename the
surrounding MCP test to describe middleware-based tool enforcement rather than
filtered tool lists. Keep the assertions in the test unchanged, including
passing both “read_file” and “write_file” tools to Deep Agents; update only the
test name and any related wording that incorrectly claims filtering occurs
before invocation.
🤖 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 `@tests/adapters/test_deepagents.py`:
- Line 394: Remove the -> None return annotations from the pytest test functions
test_allowed_tools_middleware_blocks_disallowed_tools and the test function
introduced at line 416, leaving their async definitions unannotated.
- Around line 97-108: Update _FakeAsyncSaverCM to record __aexit__ invocations,
then extend the run_deepagents tests to assert the async checkpointer context is
exited exactly once on both successful and failing execution paths. Ensure the
shared spy/counter is reset or isolated between tests and that assertions verify
cleanup even when run_deepagents raises.
- Around line 439-444: Update the _build function to require a checkpointer from
kwargs using direct indexing, assert that the retrieved checkpointer is
non-null, and pass it to graph.compile so the regression test cannot silently
run without persistence wiring.

---

Outside diff comments:
In `@tests/adapters/test_deepagents.py`:
- Around line 357-362: Rename the surrounding MCP test to describe
middleware-based tool enforcement rather than filtered tool lists. Keep the
assertions in the test unchanged, including passing both “read_file” and
“write_file” tools to Deep Agents; update only the test name and any related
wording that incorrectly claims filtering occurs before invocation.
🪄 Autofix (Beta)

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: a42c6d40-d734-4bac-bdcc-c35f1712e500

📥 Commits

Reviewing files that changed from the base of the PR and between 63e22a7 and 063df8a.

📒 Files selected for processing (3)
  • adapters/deepagents/README.md
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
  • tests/adapters/test_deepagents.py
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Build and publish docs
  • GitHub Check: CodeRabbit / Review
🧰 Additional context used
📓 Path-based instructions (3)
{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 Fabric contracts.

Files:

  • adapters/deepagents/README.md
  • adapters/deepagents/src/nemo_fabric_adapters/deepagents/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 test functions; async tests are detected and run automatically.
Do not add -> None return type annotations to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, adding spec when needed.
Name mocked classes with a mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not duplicate fixtures across test files; if a fixture is needed in multiple test files, define it in conftest.py.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the function as <fixture_name>_fixture() -> <return_type>; only pass scope when it is not function.
Prefer pytest.mark.parametrize over creating separate tests for different input types.
If a fixture is needed for a test but does not return a value, or its value is unused, use @pytest.mark.usefixtures.
When modifying environment variables in a test, use os.environ; tests/conftest.py provides an autouse restore_environ_fixture that restores environment variables after each test, so monkeypatch.setenv is unnecessary.

Files:

  • tests/adapters/test_deepagents.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_deepagents.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/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py
🧬 Code graph analysis (1)
adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py (1)
tests/adapters/test_deepagents.py (1)
  • run_deepagents (156-156)
🪛 Ruff (0.15.20)
tests/adapters/test_deepagents.py

[warning] 98-98: Remove quotes from type annotation

Remove quotes

(UP037)


[warning] 101-101: Dynamically typed expressions (typing.Any) are disallowed in *_exc

(ANN401)


[warning] 401-401: Dynamically typed expressions (typing.Any) are disallowed in _request

(ANN401)


[warning] 436-436: Dynamically typed expressions (typing.Any) are disallowed in _state

(ANN401)


[warning] 439-439: Dynamically typed expressions (typing.Any) are disallowed in **kwargs

(ANN401)


[warning] 439-439: Dynamically typed expressions (typing.Any) are disallowed in _build

(ANN401)

adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py

[warning] 176-176: Dynamically typed expressions (typing.Any) are disallowed in allowed_tools_middleware

(ANN401)


[warning] 188-188: Dynamically typed expressions (typing.Any) are disallowed in request

(ANN401)


[warning] 188-188: Dynamically typed expressions (typing.Any) are disallowed in _blocked

(ANN401)


[warning] 197-197: Dynamically typed expressions (typing.Any) are disallowed in request

(ANN401)


[warning] 197-197: Dynamically typed expressions (typing.Any) are disallowed in handler

(ANN401)


[warning] 197-197: Dynamically typed expressions (typing.Any) are disallowed in awrap_tool_call

(ANN401)


[warning] 202-202: Dynamically typed expressions (typing.Any) are disallowed in request

(ANN401)


[warning] 202-202: Dynamically typed expressions (typing.Any) are disallowed in handler

(ANN401)


[warning] 202-202: Dynamically typed expressions (typing.Any) are disallowed in wrap_tool_call

(ANN401)


[warning] 303-303: Dynamically typed expressions (typing.Any) are disallowed in open_checkpointer

(ANN401)


[warning] 320-320: Dynamically typed expressions (typing.Any) are disallowed in checkpointer

(ANN401)


[warning] 330-330: Dynamically typed expressions (typing.Any) are disallowed in model

(ANN401)

🔇 Additional comments (5)
adapters/deepagents/README.md (1)

46-48: LGTM!

adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py (2)

159-163: LGTM!

Also applies to: 303-323, 329-349, 367-397


176-207: 🔒 Security & Privacy

No change needed.

tests/adapters/test_deepagents.py (2)

93-108: Replace hand-rolled SDK mocks with MagicMock/AsyncMock.

The async checkpointer stubs still define custom mock classes, repeating the prior finding. Use MagicMock/AsyncMock with spec where appropriate and mock_* variable names.

As per path instructions, tests must use MagicMock/AsyncMock when mocking classes.

Source: Path instructions


111-123: Move shared LangGraph setup into fixtures.

_install_fake_langgraph and _use_real_langgraph are reusable test setup helpers, repeating the prior fixture-guideline finding. Convert them into fixtures so module-state mutation and cleanup are declared dependencies.

As per coding guidelines, prefer pytest fixtures over helper methods.

Also applies to: 365-375

Source: Coding guidelines

Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
Comment thread tests/adapters/test_deepagents.py Outdated
yczhang-nv and others added 2 commits July 9, 2026 21:31
An explicitly configured `tools: []` produced an empty set that `names or None`
collapsed to `None`, which build_agent_kwargs read as "no allow-list" and so
attached no gating middleware, leaving every Deep Agents built-in and MCP tool
enabled. Return the set as-is (empty = deny-all) and only treat unconfigured
tools as `None`. Add deny-all regression tests.

Also correct the adapter README to describe the `astream` (updates/values)
event-buffering runtime instead of a single `ainvoke`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
- Bound langchain-mcp-adapters (<0.3.0) and langgraph-checkpoint-sqlite; the
  unbounded specs had already resolved to 0.3.0 / 3.1.0. Use <4.0 (not the
  suggested <3.0) for langgraph-checkpoint-sqlite: the 2.x line is incompatible
  with the langgraph core deepagents pulls (AsyncSqliteSaver fails with
  'Connection object has no attribute is_alive'); lock the working 3.1.0.
- Tests: use direct dict access instead of .get() in assertions; drop the
  `-> None` return annotations on test functions; mark _FakeMCPClient.connections
  ClassVar; convert the e2e integration gate to a fixture via usefixtures.
- Add checkpointer-cleanup coverage: the fake async saver records __aexit__ and a
  test asserts the connection is closed on both success and failure paths.
- Require the real-graph regression test to receive a non-null checkpointer.

CodeRabbit's async-checkpointer and checkpointer-leak comments were already fixed
in earlier commits. The Fake*->MagicMock and helpers->fixtures refactors are
skipped: async generators / async context managers do not map cleanly to
MagicMock, and CodeRabbit tagged the helper refactor a poor tradeoff.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@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

🤖 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/deepagents/pyproject.toml`:
- Around line 20-25: Update the langgraph-checkpoint-sqlite dependency
constraint in pyproject.toml to exclude the incompatible 2.x major and allow
only the supported release range, keeping the inline compatibility note
consistent with the constraint.
🪄 Autofix (Beta)

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: cdbf7a39-be96-4a70-b928-30bb6c1bb34d

📥 Commits

Reviewing files that changed from the base of the PR and between c74c889 and b1635b7.

⛔ Files ignored due to path filters (2)
  • adapters/deepagents/uv.lock is excluded by !**/*.lock
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • adapters/deepagents/pyproject.toml
  • tests/adapters/test_deepagents.py
  • tests/e2e/test_deepagents.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: CodeRabbit / Review
🧰 Additional context used
📓 Path-based instructions (3)
{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 Fabric contracts.

Files:

  • adapters/deepagents/pyproject.toml
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 test functions; async tests are detected and run automatically.
Do not add -> None return type annotations to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, adding spec when needed.
Name mocked classes with a mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not duplicate fixtures across test files; if a fixture is needed in multiple test files, define it in conftest.py.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the function as <fixture_name>_fixture() -> <return_type>; only pass scope when it is not function.
Prefer pytest.mark.parametrize over creating separate tests for different input types.
If a fixture is needed for a test but does not return a value, or its value is unused, use @pytest.mark.usefixtures.
When modifying environment variables in a test, use os.environ; tests/conftest.py provides an autouse restore_environ_fixture that restores environment variables after each test, so monkeypatch.setenv is unnecessary.

Files:

  • tests/e2e/test_deepagents.py
  • tests/adapters/test_deepagents.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_deepagents.py
  • tests/adapters/test_deepagents.py
🧬 Code graph analysis (1)
tests/adapters/test_deepagents.py (1)
adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py (2)
  • run_deepagents (353-425)
  • _allowed_tool_names (167-174)
🪛 Ruff (0.15.20)
tests/adapters/test_deepagents.py

[warning] 104-104: Dynamically typed expressions (typing.Any) are disallowed in *_exc

(ANN401)


[warning] 336-336: Dynamically typed expressions (typing.Any) are disallowed in **_kwargs

(ANN401)


[warning] 336-336: Dynamically typed expressions (typing.Any) are disallowed in _boom

(ANN401)

🔇 Additional comments (7)
tests/adapters/test_deepagents.py (6)

31-90: 📐 Maintainability & Code Quality

Hand-rolled Fake classes still used instead of unittest.mock.

_FakeChatOpenAI, _FakeFilesystemBackend, _FakeAgent, _FakeSaverCM/_FakeAsyncSaverCM, _FakeSaver, _FakeSqliteSaver, and the new _FakeMCPClient (lines 352-364) are all custom classes named with a Fake prefix, still not converted to MagicMock/AsyncMock with spec.

As per path instructions, tests/**/*.py: "When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, adding spec when needed. Name mocked classes with a mock prefix, not fake."

Also applies to: 352-364

Source: Path instructions


110-127: 📐 Maintainability & Code Quality

Helper functions still not converted to fixtures.

_install_fake_langgraph (and _payload/_install_fake_relay elsewhere in the file) remain plain helper functions rather than pytest fixtures, and are not centralized in conftest.py despite being reusable setup shared across tests.

As per coding guidelines, "Prefer pytest fixtures over helper methods" and "Do not duplicate fixtures across test files; if a fixture is needed in multiple test files, define it in conftest.py."

Source: Coding guidelines


178-179: .get() → direct indexing fix confirmed.

Assertions now use direct dict indexing (fake_sdks["create_kwargs"]["system_prompt"], calls["wrapped"], etc.) instead of .get(), resolving the prior review comment.

Also applies to: 266-267, 275-275, 309-310, 318-318


480-482: Regression test now requires a non-null checkpointer.

checkpointer = kwargs["checkpointer"] + assert checkpointer is not None before compiling closes the gap flagged previously.


327-343: 🎯 Functional Correctness | ⚡ Quick win

Assert the first (success) run actually succeeded before checking cleanup.

Line 331 discards the output of the first run_deepagents call and never asserts it succeeded (e.g. output["failed"] is False) before checking saver_exits == 1. If the "success" path silently failed, this test would still pass, undermining the "success and failure" cleanup coverage the test name promises.

As per path instructions, tests should cover "lifecycle cleanup" for the changed API surface — this test's success-path assertion is incomplete.

🧪 Proposed fix
-    await adapter.run_deepagents(_payload(tmp_path))
+    success_output = await adapter.run_deepagents(_payload(tmp_path))
+    assert success_output["failed"] is False
     assert fake_sdks["saver_exits"] == 1

Source: Path instructions


345-386: 🎯 Functional Correctness | ⚡ Quick win

Test name/coverage mismatch: no allow-list filtering is actually exercised.

test_mcp_servers_become_tools_filtered_by_allowed implies MCP tools are filtered by an allow-list, but the payload never sets effective_config.config.tools; the test only verifies unfiltered pass-through of both read_file and write_file. Per the adapter design (allow-listing is enforced via allowed_tools_middleware, not tool-list filtering — see test_allowed_tools_recorded_as_middleware), this test's name overstates what it covers.

As per path instructions, tests should cover the behavior promised by the API surface — rename to reflect actual coverage, or add an allow-list assertion (e.g. via middleware) to match the name.

Source: Path instructions

tests/e2e/test_deepagents.py (1)

19-28: Prior review comments resolved.

_require_integration is now a proper fixture (@pytest.fixture(name="_require_integration") / _require_integration_fixture() -> None) consumed via @pytest.mark.usefixtures, and both test functions no longer carry -> None annotations. Both previously-flagged issues are fixed.

Also applies to: 31-32, 63-64

Comment thread adapters/deepagents/pyproject.toml Outdated
yczhang-nv and others added 3 commits July 9, 2026 21:56
…pter

# Conflicts:
#	.github/workflows/ci_python.yml
#	README.md
#	justfile
#	pyproject.toml
#	uv.lock
- README: add runnable Fabric.run / start_runtime examples for one-shot and
  multi-turn resume; list the Deep Agents adapter in the root README adapter
  guides and the code-review example variant table; add a `deepagents` variant to
  the example entrypoint.
- Tighten langgraph-checkpoint-sqlite to >=3.0,<4.0 (2.x breaks AsyncSqliteSaver)
  so the resolver cannot pick the known-bad major; keep the inline note aligned.
- Drop the stale parenthetical claiming fabric doctor's requirement.env is the
  auth guard: the descriptor declares no static env requirement (auth is
  provider-specific and validated by the runtime preflight).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
Address the two open CodeRabbit threads on the Deep Agents adapter tests:
- Replace the hand-rolled Fake* classes (chat model, filesystem backend, agent,
  async saver, MCP client, tools, request) with unittest.mock
  MagicMock/AsyncMock; async-generator and async-context-manager behaviors are
  provided by plain functions rather than new classes.
- Convert the shared setup helpers (_payload, _install_fake_relay,
  _install_fake_langgraph, _use_real_langgraph) into pytest fixtures
  (make_payload factory, fake_relay, use_real_langgraph).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
@yczhang-nv yczhang-nv self-assigned this Jul 10, 2026
@yczhang-nv yczhang-nv added the enhancement New feature or request label Jul 10, 2026
yczhang-nv and others added 2 commits July 10, 2026 11:10
- Resumed usage/cost double-counted replayed prior-turn messages because usage
  was aggregated from the full final LangGraph state. Aggregate only the messages
  emitted this turn (the astream `updates` deltas) and add a resumed-usage
  regression test.
- Docs: list the Deep Agents adapter across the public entry points — the
  code-review example README (four variants), examples/README.md, the Python SDK
  guide variant list, and the adapters-deepagents (plus adapters-claude) install
  extras.
- Reword the preflight docstring so it describes invocation-time checks rather
  than implying `fabric doctor` performs the package/credential checks (core has
  no adapter-doctor hook).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
…pter

Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
@yczhang-nv
yczhang-nv marked this pull request as ready for review July 10, 2026 19:13
@yczhang-nv
yczhang-nv requested review from a team as code owners July 10, 2026 19:13
yczhang-nv and others added 3 commits July 10, 2026 12:14
Separate the bundled test_deepagents_oneshot_and_runtime into
test_deepagents_oneshot and test_deepagents_multi_turn so the multi-turn resume
coverage is discoverable and each case can fail independently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
…failures

Address code-review findings on the Deep Agents adapter:

- Gate delegated subagents with the config.tools allow-list so tools routed
  through the `task` tool cannot run ungated.
- Pass virtual_mode=True to FilesystemBackend so absolute paths and `..` cannot
  escape the workspace root (and to not rely on the deprecated 0.5 default).
- Run preflight, model construction, and resume-state load inside the guarded
  scope so a missing credential or absent package returns a normalized failure
  instead of a raw traceback.
- Raise AdapterConfigError for a non-list `tools` value or a misconfigured MCP
  server instead of silently disabling gating / dropping the server.
- Default api_key_env per provider (NVIDIA_API_KEY / OPENAI_API_KEY) and require
  it explicitly for other providers, so a key is never sent to the wrong endpoint.
- Stream with subgraphs=True and fold subagent message deltas (deduped by id)
  into usage so usage/cost is accurate for delegating runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
…r-provider defaults

Follow-ups to the per-provider credential change:

- README no longer claims api_key_env defaults to NVIDIA_API_KEY
  unconditionally; it now states the per-provider defaults in one place.
- Document that `openai-compatible` (and any init_chat_model backend) must set
  api_key_env explicitly — a deliberate behavior change that was undocumented.
- Reword the preflight error so it no longer says the credential env var "is
  defined in the configuration" when it may have only been defaulted per provider.
- Add a regression test that `openai-compatible` without api_key_env is a
  normalized configuration failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>

@AjayThorve AjayThorve left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I have two questions about the SDK extension boundary before we establish this adapter contract.

Comment thread adapters/deepagents/README.md Outdated
Comment thread adapters/deepagents/src/nemo_fabric_adapters/deepagents/adapter.py Outdated
@AjayThorve

AjayThorve commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@yczhang-nv I think we should keep subagent support narrow for this PR:

  • Deep Agents can delegate through its built-in task tool.
  • Subagents inherit the parent’s model, tools, skills, workspace, telemetry, and permissions.
  • The parent config.tools policy applies to delegated execution, so subagents cannot broaden capabilities.
  • We do not expose independently configured subagent tools, skills, models, MCP servers, middleware, or permissions through the Fabric SDK yet.
  • harness.settings.deepagents supports only documented, JSON-serializable options; it is not a general Python-object escape hatch.
  • Fabric-owned arguments such as model, tools, backend, and skills cannot be overridden through passthrough settings.
  • We add one real delegation test to verify the inherited restrictions.

@AnuradhaKaruppiah curious to know your thoughts

…t subagent scope

Address review of the harness.settings.deepagents boundary:

- Reserve Fabric-owned keys (model, tools, backend, skills, system_prompt,
  middleware, checkpointer): the passthrough can no longer override them, which
  previously let a config silently defeat the normalized model config, MCP tool
  resolution, workspace confinement, and tool gating.
- Restrict the passthrough to a documented, JSON-serializable allow-list
  (subagents, interrupt_on) and fail clearly on unknown keys instead of silently
  dropping them via _supported_kwargs.
- Rewrite the README: it is a JSON-only passthrough, not a Python-object escape
  hatch; document the subagent inheritance model (delegated subagents inherit the
  parent's tools/skills/workspace/permissions and the config.tools policy, so they
  cannot broaden capabilities) and that independently-configured subagents are
  future work.
- Add an opt-in real delegation e2e test plus unit tests for passthrough
  validation (reserved-key override, unknown key, supported option forwarded).

Deterministic verification of delegated tool-gating remains mock-based pending a
subagents SDK update; tracked as a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Yuchen Zhang <yuchenz@nvidia.com>
@yczhang-nv

Copy link
Copy Markdown
Contributor Author

@yczhang-nv I think we should keep subagent support narrow for this PR:

  • Deep Agents can delegate through its built-in task tool.
  • Subagents inherit the parent’s model, tools, skills, workspace, telemetry, and permissions.
  • The parent config.tools policy applies to delegated execution, so subagents cannot broaden capabilities.
  • We do not expose independently configured subagent tools, skills, models, MCP servers, middleware, or permissions through the Fabric SDK yet.
  • harness.settings.deepagents supports only documented, JSON-serializable options; it is not a general Python-object escape hatch.
  • Fabric-owned arguments such as model, tools, backend, and skills cannot be overridden through passthrough settings.
  • We add one real delegation test to verify the inherited restrictions.

@AnuradhaKaruppiah curious to know your thoughts

@AjayThorve Thanks for the clarification! I updated the code in 1b7f7ad to match the scope you mentioned. Specifically,

  • Subagents delegate through the built-in task tool and inherit the parent's model, tools, skills, workspace, telemetry, and permissions. The parent config.tools allow-list is applied to delegated execution (gating middleware is attached to each subagent), so subagents cannot broaden capabilities.
  • No independently-configured subagent tools/skills/models/MCP servers/middleware/permissions are exposed through the Fabric SDK.
  • harness.settings.deepagents accepts only documented, JSON-serializable options; Fabric-owned arguments (model, tools, backend, skills, system_prompt, middleware, checkpointer) cannot be overridden.
  • Added one real delegation test (opt-in e2e); deterministic gating-inheritance verification remains mock-based pending a subagents SDK update.

@AnuradhaKaruppiah LMK if you have any comments on those as well

@AjayThorve
AjayThorve self-requested a review July 10, 2026 22:52

@AjayThorve AjayThorve left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Looks good to me

@yczhang-nv

Copy link
Copy Markdown
Contributor Author

/merge

@rapids-bot
rapids-bot Bot merged commit 9d7fb35 into NVIDIA:main Jul 10, 2026
11 checks passed
@yczhang-nv
yczhang-nv deleted the feat/deepagents-adapter branch July 10, 2026 23:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants