Skip to content

Commit 30dfa57

Browse files
dylan-savageclaude
authored andcommitted
feat(agent): crewai orchestrator (#608)
* Feat: new control interface for crew, added crew orchestrator node * orchestrator wip * feat(agent_crewai): expose expert config fields for both node types Add goal, backstory, expected_output, max_iter to agent_crewai (CrewDriver) and goal, backstory, max_iter to agent_crewai_orchestrator (OrchestratorDriver). All fields fall back to existing defaults when blank/zero. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * chore: cleanup modified crew code * Chore: Delete Crew readme * chore: rename 'expert' mode to 'advanced' mode * chore: remove dead code found by code rabbit * Fix: Rename orchestrator to Manager; remove tool invoke from manager node * fix: propagate sub-agent advanced config through crewai.describe() goal, backstory, expected_output, and instructions were all loaded from connConfig but not included in DescribeResponse, causing the manager to silently fall back to hardcoded defaults for every sub-agent. - Add goal, backstory, expected_output, instructions to DescribeResponse - Populate them in CrewDriver.describe() - Use d.goal/backstory/expected_output in ManagerDriver sub-agent build - Append instructions to sub-agent backstory (substitutes for the run_agent() path that is bypassed in manager mode) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1702b7f commit 30dfa57

7 files changed

Lines changed: 546 additions & 61 deletions

File tree

nodes/src/nodes/agent_crewai/IGlobal.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@
2626
import os
2727
from typing import Any
2828

29-
from rocketlib import IGlobalBase, OPEN_MODE
29+
from rocketlib import IGlobalBase, IJson, OPEN_MODE
3030

3131

3232
class IGlobal(IGlobalBase):
3333
process: Any = None
3434
agent: Any = None
35+
role: str = 'Assistant'
36+
task_description: str = ''
37+
goal: str = ''
38+
backstory: str = ''
39+
expected_output: str = ''
3540

3641
def beginGlobal(self) -> None:
3742
if self.IEndpoint.endpoint.openMode == OPEN_MODE.CONFIG:
@@ -46,10 +51,28 @@ def beginGlobal(self) -> None:
4651

4752
self.process = Process.sequential
4853

49-
from .crewai import CrewDriver
54+
conn_config = IJson.toDict(self.glb.connConfig) if self.glb.connConfig else {}
5055

51-
self.agent = CrewDriver(self, process=self.process)
56+
self.goal = str(conn_config.get('goal') or '').strip()
57+
self.backstory = str(conn_config.get('backstory') or '').strip()
58+
59+
if self.glb.logicalType == 'agent_crewai_manager':
60+
from .crewai import ManagerDriver
61+
62+
self.agent = ManagerDriver(self)
63+
else:
64+
self.role = str(conn_config.get('role') or 'Assistant').strip() or 'Assistant'
65+
self.task_description = str(conn_config.get('task_description') or '').strip()
66+
self.expected_output = str(conn_config.get('expected_output') or '').strip()
67+
from .crewai import CrewDriver
68+
69+
self.agent = CrewDriver(self, process=self.process, role=self.role, task_description=self.task_description, goal=self.goal, backstory=self.backstory, expected_output=self.expected_output)
5270

5371
def endGlobal(self) -> None:
5472
self.agent = None
5573
self.process = None
74+
self.role = 'Assistant'
75+
self.task_description = ''
76+
self.goal = ''
77+
self.backstory = ''
78+
self.expected_output = ''

nodes/src/nodes/agent_crewai/IInstance.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,24 @@ def writeQuestions(self, question: Question):
4242
self.IGlobal.agent.run_agent(self, question, emit_answers_lane=True)
4343

4444
def invoke(self, param: Any) -> Any: # noqa: ANN401
45-
# Only intercept tool.* control-plane operations; otherwise fall back.
4645
op = param.get('op') if isinstance(param, dict) else getattr(param, 'op', None)
46+
47+
# crewai.describe fan-out: only sub-agents (CrewDriver) respond — guarded by hasattr.
48+
# ManagerDriver has no describe() so it silently falls through.
49+
if isinstance(op, str) and op == 'crewai.describe' and hasattr(self.IGlobal.agent, 'describe'):
50+
descriptor = self.IGlobal.agent.describe(self)
51+
existing = getattr(param, 'agents', None)
52+
if isinstance(existing, list):
53+
existing.append(descriptor)
54+
try:
55+
param.agents = existing
56+
except Exception:
57+
pass
58+
return param
59+
return [descriptor]
60+
61+
# tool.* control-plane operations for agent-as-tool.
4762
if isinstance(op, str) and op.startswith('tool.'):
4863
return self.IGlobal.agent.handle_invoke(self, param)
64+
4965
return super().invoke(param)

0 commit comments

Comments
 (0)