Repository navigation
feat: Add Deep Agents integration #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rapids-bot
merged 41 commits into
NVIDIA:release/0.2
from
dagardner-nv:david-deepagents-int-p2
May 14, 2026
Merged
Changes from 23 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
e1abda3
Add deepagents extra
dagardner-nv 92ca845
Import improvement
dagardner-nv d8912c9
WIP: First pass at deep agents integration
dagardner-nv 3ce8bc7
Use a nim model, remove unnecessary helper method
dagardner-nv 649e9f8
Rename helper method
dagardner-nv 2e2b9f0
Document constants, remove unneeded truncation
dagardner-nv 561eb93
Add protocol type hints
dagardner-nv 4d4435c
Cleanups to callbacks class, distinguish between hitl and other graph…
dagardner-nv 6a67371
Nitpicky formatting
dagardner-nv bc51450
Update attribs
dagardner-nv 601e771
Move the subscribed_events fixture to be shared
dagardner-nv 822bab7
WIP: [skip ci]
dagardner-nv 445bdff
Formatting [skip ci]
dagardner-nv aa3b66e
Fix linting errors
dagardner-nv a1eecc3
Add deepagents in ci
dagardner-nv e609300
Add return type hint per code rabbit suggestion
dagardner-nv bdbe385
Install dependencies in the CI check stage needed for ty tool
dagardner-nv 92e3908
Install OpenSSL via vcpkg for Windows-arm64
dagardner-nv 59e01c8
Merge branch 'main' of github.com:NVIDIA/NeMo-Flow into david-deepage…
dagardner-nv c6f0eff
Add more tests
dagardner-nv 9948131
Formatting
dagardner-nv 47b225e
Merge branch 'main' of github.com:NVIDIA/NeMo-Flow into david-deepage…
dagardner-nv 9b7e117
Apply suggestions from coderabbit
dagardner-nv b3aba0d
Merge branch 'main' of github.com:NVIDIA/NeMo-Flow into david-deepage…
dagardner-nv dd2e39c
Merge branch 'release/0.2' of github.com:NVIDIA/NeMo-Flow into david-…
dagardner-nv 05ac673
Record skills as tool scopes not marks
dagardner-nv e5faedb
Replace tool marks with scopes
dagardner-nv 5fe997f
Remove redundant backend.py
dagardner-nv cb3ef49
Remove overloads of wrap_tool_call and awrap_tool_call and therefore …
dagardner-nv 0606d75
WIP
dagardner-nv e175f04
Set to warning not debug, failing to pop due to a failure in _prepare…
dagardner-nv 9c49a65
Handle pydantic classes in _serialize_value
dagardner-nv 95bf464
Handle langgraph command and send types in prepare_outputs
dagardner-nv ab8a14b
Update lock
dagardner-nv cd2ec54
Remove unused methods
dagardner-nv 7016a3c
Formatting
dagardner-nv 869edaf
Expand e2e test to include a subagent
dagardner-nv 08870f7
Improve event validation
dagardner-nv 85577cc
Merge branch 'release/0.2' of github.com:NVIDIA/NeMo-Flow into david-…
dagardner-nv 342b367
Move documentation
dagardner-nv c478f91
Forgot to check in moved file
dagardner-nv File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| <!-- | ||
| SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| SPDX-License-Identifier: Apache-2.0 | ||
| --> | ||
|
|
||
| # NeMo Flow Deep Agents Integration | ||
|
|
||
| This directory contains the `nemo_flow.integrations.deepagents` package, which | ||
| adds Deep Agents-specific observability on top of the LangChain and LangGraph | ||
| integrations. | ||
|
|
||
| Deep Agents is built on LangChain `AgentMiddleware` and the LangGraph runtime. | ||
| The integration therefore composes the existing NeMo Flow LangChain and | ||
| LangGraph hooks, then emits stable mark events for Deep Agents concepts: | ||
| subagents, async subagents, human-in-the-loop interrupts and resumes, skills, | ||
| filesystem operations, and sandbox execution. | ||
|
|
||
| ## Setup | ||
|
|
||
| ```bash | ||
| uv sync --all-groups --extra deepagents | ||
| just build-python | ||
| ``` | ||
|
|
||
| ## Usage Example | ||
|
|
||
| ```python | ||
| import nemo_flow | ||
| from deepagents import create_deep_agent | ||
| from nemo_flow.integrations.deepagents import ( | ||
| NemoFlowDeepAgentsCallbackHandler, | ||
| add_nemo_flow_integration, | ||
| ) | ||
|
|
||
| agent = create_deep_agent( | ||
| **add_nemo_flow_integration( | ||
| model="nvidia:nvidia/nemotron-3-nano-30b-a3b", | ||
| tools=[], | ||
| skills=["/skills/research/"], | ||
| name="main-agent", | ||
| ) | ||
| ) | ||
|
|
||
| with nemo_flow.scope.scope("deepagents-request", nemo_flow.ScopeType.Agent): | ||
| result = agent.invoke( | ||
| {"messages": [{"role": "user", "content": "Research recent GPU news"}]}, | ||
| config={"callbacks": [NemoFlowDeepAgentsCallbackHandler()]}, | ||
| ) | ||
| ``` | ||
|
|
||
| ## What Is Captured | ||
|
|
||
| - LangChain model and tool calls through NeMo Flow managed execution. | ||
| - LangGraph run scopes through callbacks. | ||
| - Human-in-the-loop interrupt and resume marks. | ||
| - Deep Agents `task` delegation marks for synchronous subagents. | ||
| - Deep Agents async subagent tool lifecycle marks. | ||
| - Filesystem tool marks for `ls`, `read_file`, `write_file`, `edit_file`, | ||
| `glob`, and `grep`. | ||
| - Sandbox/local shell `execute` marks. | ||
| - Configured skills and subagent summaries at agent-run start. | ||
| - Direct backend method calls when the backend is wrapped with | ||
| `observe_backend()` or via `add_nemo_flow_integration(..., backend=...)`. | ||
|
|
||
| Remote async subagents still need NeMo Flow instrumentation in the remote graph | ||
| or process to capture their internal model and tool calls. Supervisor-side | ||
| observability captures the task lifecycle and returned status. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| # SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| """NeMo Flow integrations for Deep Agents.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| from collections.abc import Mapping, Sequence | ||
| from typing import Any | ||
|
|
||
| from nemo_flow.integrations.deepagents.backend import NemoFlowDeepAgentsBackend, observe_backend | ||
| from nemo_flow.integrations.deepagents.callbacks import NemoFlowDeepAgentsCallbackHandler | ||
| from nemo_flow.integrations.deepagents.middleware import NemoFlowDeepAgentsMiddleware | ||
|
|
||
|
|
||
| def add_nemo_flow_integration( | ||
| kwargs: Mapping[str, Any] | None = None, | ||
| *, | ||
| instrument_subagents: bool = True, | ||
| instrument_backend: bool = True, | ||
| **overrides: Any, | ||
| ) -> dict[str, Any]: | ||
| """ | ||
| Receives the keyword arguments for ``create_deep_agent`` and returns them with NeMo Flow observability attached. | ||
|
|
||
| Use this helper as ``create_deep_agent(**add_nemo_flow_integration(...))``. | ||
| It injects Deep Agents-aware middleware at the top level, adds the same | ||
| middleware to dictionary-style custom subagents that do not inherit parent | ||
| middleware, and optionally wraps the backend. | ||
| """ | ||
| observed = dict(kwargs or {}) | ||
| observed.update(overrides) | ||
|
|
||
| skills = _string_sequence(observed.get("skills")) | ||
| subagents = list(observed.get("subagents") or ()) | ||
| subagent_summaries = [_subagent_summary(subagent) for subagent in subagents] | ||
| backend = observed.get("backend") | ||
|
|
||
| if isinstance(backend, NemoFlowDeepAgentsBackend): | ||
| observed["backend"] = backend | ||
| backend_name = type(backend.__wrapped__).__name__ | ||
| elif instrument_backend and backend is not None: | ||
| observed["backend"] = observe_backend(backend) | ||
| backend_name = type(backend).__name__ | ||
| elif backend is not None: | ||
| backend_name = type(backend).__name__ | ||
| else: | ||
| backend_name = None | ||
|
|
||
| middleware = list(observed.get("middleware") or ()) | ||
| _append_middleware( | ||
| middleware, | ||
| NemoFlowDeepAgentsMiddleware( | ||
| agent_name=observed.get("name"), | ||
| skills=skills, | ||
| subagents=subagent_summaries, | ||
| backend_name=backend_name, | ||
| ), | ||
| ) | ||
| observed["middleware"] = middleware | ||
|
|
||
| if instrument_subagents and subagents: | ||
| observed["subagents"] = [_instrument_subagent(subagent) for subagent in subagents] | ||
|
|
||
| return observed | ||
|
|
||
|
|
||
| def _append_middleware(middleware: list[Any], new_middleware: NemoFlowDeepAgentsMiddleware) -> None: | ||
| if any(isinstance(item, NemoFlowDeepAgentsMiddleware) for item in middleware): | ||
| return | ||
| middleware.append(new_middleware) | ||
|
|
||
|
|
||
| def _instrument_subagent(subagent: Any) -> Any: | ||
| if not isinstance(subagent, dict): | ||
| return subagent | ||
|
|
||
| observed = dict(subagent) | ||
| middleware = list(observed.get("middleware") or ()) | ||
| _append_middleware( | ||
| middleware, | ||
| NemoFlowDeepAgentsMiddleware( | ||
| agent_name=observed.get("name"), | ||
| skills=_string_sequence(observed.get("skills")), | ||
| subagents=None, | ||
| ), | ||
| ) | ||
| observed["middleware"] = middleware | ||
| return observed | ||
|
|
||
|
|
||
| def _subagent_summary(subagent: Any) -> Mapping[str, Any]: | ||
| if isinstance(subagent, Mapping): | ||
| summary: dict[str, Any] = {"type": "subagent"} | ||
| for key in ("name", "description", "model", "graph_id", "url"): | ||
| value = subagent.get(key) | ||
| if value is not None: | ||
| summary[key] = value | ||
| if "skills" in subagent: | ||
| summary["skills"] = _string_sequence(subagent.get("skills")) | ||
| return summary | ||
|
|
||
| summary = {"type": type(subagent).__name__} | ||
| for attr in ("name", "description", "graph_id", "url"): | ||
| value = getattr(subagent, attr, None) | ||
| if value is not None: | ||
| summary[attr] = value | ||
| return summary | ||
|
|
||
|
|
||
| def _string_sequence(value: Any) -> Sequence[str] | None: | ||
| if value is None: | ||
| return None | ||
| if isinstance(value, str): | ||
| return [value] | ||
| if isinstance(value, Sequence): | ||
| return [str(item) for item in value] | ||
| return [str(value)] | ||
|
|
||
|
|
||
| __all__ = [ | ||
| "NemoFlowDeepAgentsBackend", | ||
| "NemoFlowDeepAgentsCallbackHandler", | ||
| "NemoFlowDeepAgentsMiddleware", | ||
| "add_nemo_flow_integration", | ||
| "observe_backend", | ||
| ] |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.