Skip to content

Feat: deepagent subagents#665

Closed
dylan-savage wants to merge 3 commits into
feat/base-uifrom
feat/deepagents-subagents
Closed

Feat: deepagent subagents#665
dylan-savage wants to merge 3 commits into
feat/base-uifrom
feat/deepagents-subagents

Conversation

@dylan-savage
Copy link
Copy Markdown
Collaborator

@dylan-savage dylan-savage commented Apr 13, 2026

Summary

  • Adds a new DeepAgent Subagent node type so a top-level Deep Agent can hierarchically delegate work to specialized sub-agents over a deepagent invoke channel, each with its own LLM and tool roster.
  • Adds a CrewAI-style Advanced Mode checkbox to both nodes — Normal mode shows just Instructions (and Description on the subagent); Advanced mode swaps in Agent Description + System Prompt for full control.
  • Locks the subagent down to invoke-only (removed lanes/input, narrowed classType to ["deepagent"]) so it can't be mis-wired as a top-level pipeline agent.
  • Hardens the JSON tool-call envelope used by the LangChain wrapper: real JSON Schema for args_schema (so the LLM picks correct arg names like description instead of guessing prompt/task_description), \n{ stop-word + tolerant first-object parser to defeat duplicate/hallucinated JSON outputs.

Type

Testing

  • Tests added or updated
  • Tested locally
  • ./builder test passes

Checklist

  • Commit messages follow conventional commits
  • No secrets or credentials included
  • Wiki updated (if applicable)
  • Breaking changes documented (if applicable)

Linked Issue

Fixes #664

Summary by CodeRabbit

  • New Features

    • DeepAgent sub-agent support with orchestrator discovery
    • Configurable system_prompt field for finer agent behavior control
    • advanced_mode toggle to switch simplified vs advanced config
    • New DeepAgent Subagent node type for delegation workflows
  • Bug Fixes

    • More robust JSON extraction from model outputs to handle prose/code/trailing text

dylan-savage and others added 2 commits April 13, 2026 10:53
Introduces agent_deepagent_subagent node type so DeepAgent orchestrators
can delegate work to specialized sub-agents via the deepagent invoke channel,
with per-subagent tools and LLM routing through its own host services.

- services.subagent.json defines the new node type with description,
  system_prompt, and instructions config fields
- IInvokeDeepagent (Describe/DescribeResponse) added to rocketlib.types
  for the describe fan-out protocol
- DeepAgentDriver._collect_subagents builds deepagents.SubAgent entries
  from connected subagents, wiring each to its own LLM/tools host
- JSON-schema args serialization so the LLM sees actual field names
  for tools like task, not opaque <class 'X'> strings
- Stop word and tolerant first-object parser for the tool-call envelope
  to survive LLM outputs that emit duplicate or hallucinated JSON objects

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Both nodes get an Advanced Mode checkbox (crewai-style) that switches
  between Instructions and Agent Description + System Prompt
- Subagent is now invoke-only: removed questions/answers lanes and
  narrowed classType to ["deepagent"] so it can only be reached from a
  manager via the deepagent channel
- Driver cleanup: factor _compose_system_prompt helper used by both the
  manager and subagent paths, drop diagnostic logging, simplify parse and
  schema helpers

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added module:server C++ engine and server components module:nodes Python pipeline nodes labels Apr 13, 2026
@github-actions
Copy link
Copy Markdown

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR adds DeepAgent subagent support and an advanced-mode system prompt, selects between DeepAgent and DeepAgentSubagent drivers at startup, implements a deepagent.describe control-plane op for subagent discovery, improves LLM tool-call JSON extraction, and exposes new config/service schemas and Pydantic types for discovery.

Changes

Cohort / File(s) Summary
Driver selection & core drivers
nodes/src/nodes/agent_deepagent/IGlobal.py, nodes/src/nodes/agent_deepagent/deepagent.py
Conditional instantiation of DeepAgentDriver vs DeepAgentSubagentDriver based on logicalType; new DeepAgentSubagentDriver; driver now reads system_prompt/description; _run fans out via deepagent.describe and passes discovered subagents into create_deep_agent; improved LLM tool-call parsing extracting first balanced JSON object; added helpers _compose_system_prompt, _extract_first_json_object, _tool_args_schema.
Instance control-plane handling
nodes/src/nodes/agent_deepagent/IInstance.py
IInstance.invoke intercepts op == "deepagent.describe" and delegates to new _handle_deepagent_describe, which builds and appends IInvokeDeepagent.DescribeResponse entries populated from the driver and instance metadata.
Service manifests / UI schema
nodes/src/nodes/agent_deepagent/services.json, nodes/src/nodes/agent_deepagent/services.subagent.json
Added system_prompt and advanced_mode fields; replaced agent_deepagent.profile with advanced_mode conditional behavior; new subagent node definition agent_deepagent_subagent:// with preconfig, fields (description, system_prompt, instructions, advanced_mode) and invoke-only capability; added deepagent invoke capability.
Control-plane types & exports
packages/server/engine-lib/rocketlib-python/lib/rocketlib/types.py, packages/server/engine-lib/rocketlib-python/lib/rocketlib/__init__.py
Added IInvokeDeepagent Pydantic model with nested Describe and DescribeResponse types (op='deepagent.describe'); exported IInvokeDeepagent in package __all__.

Sequence Diagram(s)

sequenceDiagram
  participant Manager as Top-level DeepAgent Driver
  participant Orchestrator as IInstance / IGlobal
  participant SubagentNode as Subagent Node (agent_deepagent_subagent)
  participant Rocketlib as Control-plane (IInvokeDeepagent)

  Manager->>Orchestrator: send `deepagent.describe` (IInvokeDeepagent.Describe)
  Orchestrator->>SubagentNode: receive invoke, _handle_deepagent_describe
  SubagentNode-->>Orchestrator: return DescribeResponse (name, description, system_prompt, instructions, node_id, invoke)
  Orchestrator-->>Manager: aggregate responses, emit SSE/debug, create_deep_agent(..., subagents=...)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jmaionchi
  • Rod-Christensen
  • ryan-t-christensen

Poem

🐰 I hop and sniff the metaphorical breeze,
Subagents gather like leaves from the trees.
Prompts stitched fine and drivers decide,
Together they tackle the long-running stride.
Tiny paws, big orchestration—hooray! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Feat: deepagent subagents' directly and concisely summarizes the main change: adding subagent support to DeepAgent.
Linked Issues check ✅ Passed All primary objectives from #664 are met: subagent node added with deepagent invoke channel [#664], manager discovery via deepagent.describe implemented [#664], Normal/Advanced Mode toggle added to both nodes [#664], and changes in nodes/ai modules [#664].
Out of Scope Changes check ✅ Passed All changes align with PR objectives: subagent driver and service definitions, mode toggle in services.json/services.subagent.json, JSON parser hardening, and rocketlib type exports for deepagent.describe support are all required for hierarchical delegation.
Docstring Coverage ✅ Passed Docstring coverage is 89.47% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/deepagents-subagents

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@dylan-savage dylan-savage changed the title Feat/deepagents subagents Feat: deepagents subagents Apr 13, 2026
@dylan-savage dylan-savage changed the title Feat: deepagents subagents Feat: deepagent subagents Apr 13, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nodes/src/nodes/agent_deepagent/deepagent.py`:
- Around line 670-673: The except block swallowing errors from
self._invoker.instance.invoke('deepagent', req, nodeId=node_id) should be
changed to log the exception at debug level; catch Exception as e and call the
node's logger (e.g. self.logger.debug or a module logger) to record a
descriptive message including the sub-agent name 'deepagent', the node_id, any
relevant parts of req, and the exception details/stacktrace so
connectivity/protocol failures during sub-agent discovery are visible for
troubleshooting.

In `@nodes/src/nodes/agent_deepagent/IInstance.py`:
- Around line 82-84: The branch in the invoke method that checks `isinstance(op,
str) and op == 'deepagent.describe'` currently calls
`self._handle_deepagent_describe(param)` and falls through implicitly; update
that branch to explicitly return None (e.g., `return None`) after calling
`_handle_deepagent_describe` so `invoke`'s return is explicit and satisfies
linters and readers.
🪄 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: Pro

Run ID: 82dab710-4063-4f0f-b3ed-d850c8e719d2

📥 Commits

Reviewing files that changed from the base of the PR and between 824fc34 and 23a8051.

📒 Files selected for processing (7)
  • nodes/src/nodes/agent_deepagent/IGlobal.py
  • nodes/src/nodes/agent_deepagent/IInstance.py
  • nodes/src/nodes/agent_deepagent/deepagent.py
  • nodes/src/nodes/agent_deepagent/services.json
  • nodes/src/nodes/agent_deepagent/services.subagent.json
  • packages/server/engine-lib/rocketlib-python/lib/rocketlib/__init__.py
  • packages/server/engine-lib/rocketlib-python/lib/rocketlib/types.py

Comment thread nodes/src/nodes/agent_deepagent/deepagent.py
Comment thread nodes/src/nodes/agent_deepagent/IInstance.py
… parser

The first-object JSON parser handles every case the stop-word caught.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@dylan-savage dylan-savage changed the base branch from develop to feat/base-ui April 13, 2026 22:36
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@nodes/src/nodes/agent_deepagent/deepagent.py`:
- Around line 974-977: In the loop that iterates "for inst in instructions or
[]" avoid reassigning the loop variable "inst"; instead create a new name (e.g.,
inst_clean or inst_stripped) and use that in the if check and when appending to
"prompt" so replace "inst = inst.strip()" with a new variable and update the
conditional and f-string to use it, keeping the rest of the "prompt"
concatenation logic unchanged.
- Around line 519-521: Remove the redundant "from rocketlib import debug as
_debug" and update the debug call inside _run to use the already-imported debug;
specifically, delete the alias import and change the log expression that
references _debug (the f-string reporting tool_descriptors in function _run) to
call debug(...) so the module consistently uses the existing debug symbol (also
used elsewhere later in the file).
🪄 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: Pro

Run ID: 6cc065b9-abbf-4964-bb7f-f7606e720749

📥 Commits

Reviewing files that changed from the base of the PR and between 23a8051 and 2c94cac.

📒 Files selected for processing (1)
  • nodes/src/nodes/agent_deepagent/deepagent.py

Comment on lines +519 to +521
from rocketlib import debug as _debug

_debug(f'deepagent _run discovered {len(tool_descriptors)} tools: {[td.get("name") if hasattr(td, "get") else str(td) for td in tool_descriptors]}')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove redundant debug import.

debug is already imported at line 34. This re-import as _debug is unnecessary and creates naming inconsistency—line 566 uses debug directly while line 521 uses _debug.

♻️ Proposed fix
-        from rocketlib import debug as _debug
-
-        _debug(f'deepagent _run discovered {len(tool_descriptors)} tools: {[td.get("name") if hasattr(td, "get") else str(td) for td in tool_descriptors]}')
+        debug(f'deepagent _run discovered {len(tool_descriptors)} tools: {[td.get("name") if hasattr(td, "get") else str(td) for td in tool_descriptors]}')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodes/src/nodes/agent_deepagent/deepagent.py` around lines 519 - 521, Remove
the redundant "from rocketlib import debug as _debug" and update the debug call
inside _run to use the already-imported debug; specifically, delete the alias
import and change the log expression that references _debug (the f-string
reporting tool_descriptors in function _run) to call debug(...) so the module
consistently uses the existing debug symbol (also used elsewhere later in the
file).

Comment on lines +974 to +977
for inst in instructions or []:
inst = inst.strip()
if inst:
prompt = f'{prompt}\n{inst}'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor: Avoid overwriting loop variable.

The assignment inst = inst.strip() shadows the loop variable, which can be confusing. Consider using a different name for clarity.

♻️ Proposed fix
     for inst in instructions or []:
-        inst = inst.strip()
-        if inst:
-            prompt = f'{prompt}\n{inst}'
+        stripped = inst.strip()
+        if stripped:
+            prompt = f'{prompt}\n{stripped}'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for inst in instructions or []:
inst = inst.strip()
if inst:
prompt = f'{prompt}\n{inst}'
for inst in instructions or []:
stripped = inst.strip()
if stripped:
prompt = f'{prompt}\n{stripped}'
🧰 Tools
🪛 Ruff (0.15.9)

[warning] 975-975: for loop variable inst overwritten by assignment target

(PLW2901)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@nodes/src/nodes/agent_deepagent/deepagent.py` around lines 974 - 977, In the
loop that iterates "for inst in instructions or []" avoid reassigning the loop
variable "inst"; instead create a new name (e.g., inst_clean or inst_stripped)
and use that in the if check and when appending to "prompt" so replace "inst =
inst.strip()" with a new variable and update the conditional and f-string to use
it, keeping the rest of the "prompt" concatenation logic unchanged.

@dylan-savage
Copy link
Copy Markdown
Collaborator Author

These changes are going to be merged into feat/base-ui instead of develop

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

module:nodes Python pipeline nodes module:server C++ engine and server components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add subagent support and advanced config mode to DeepAgent

1 participant