Feat: deepagent subagents#665
Conversation
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>
|
No description provided. |
📝 WalkthroughWalkthroughThis PR adds DeepAgent subagent support and an advanced-mode system prompt, selects between DeepAgent and DeepAgentSubagent drivers at startup, implements a Changes
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=...)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (7)
nodes/src/nodes/agent_deepagent/IGlobal.pynodes/src/nodes/agent_deepagent/IInstance.pynodes/src/nodes/agent_deepagent/deepagent.pynodes/src/nodes/agent_deepagent/services.jsonnodes/src/nodes/agent_deepagent/services.subagent.jsonpackages/server/engine-lib/rocketlib-python/lib/rocketlib/__init__.pypackages/server/engine-lib/rocketlib-python/lib/rocketlib/types.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>
There was a problem hiding this comment.
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
📒 Files selected for processing (1)
nodes/src/nodes/agent_deepagent/deepagent.py
| 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]}') |
There was a problem hiding this comment.
🧹 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).
| for inst in instructions or []: | ||
| inst = inst.strip() | ||
| if inst: | ||
| prompt = f'{prompt}\n{inst}' |
There was a problem hiding this comment.
🧹 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.
| 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.
|
These changes are going to be merged into feat/base-ui instead of develop |
Summary
Type
Testing
./builder testpassesChecklist
Linked Issue
Fixes #664
Summary by CodeRabbit
New Features
Bug Fixes