fableforge-agent-swarm 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agent_swarm/__init__.py +41 -0
- agent_swarm/agents.py +424 -0
- agent_swarm/cli.py +255 -0
- agent_swarm/models.py +169 -0
- agent_swarm/orchestrator.py +586 -0
- agent_swarm/transition_matrix.py +379 -0
- fableforge_agent_swarm-0.1.0.dist-info/METADATA +281 -0
- fableforge_agent_swarm-0.1.0.dist-info/RECORD +11 -0
- fableforge_agent_swarm-0.1.0.dist-info/WHEEL +4 -0
- fableforge_agent_swarm-0.1.0.dist-info/entry_points.txt +2 -0
- fableforge_agent_swarm-0.1.0.dist-info/licenses/LICENSE +21 -0
agent_swarm/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""AgentSwarm — Orchestrate micro-agent swarms using Markov transition matrices."""
|
|
2
|
+
|
|
3
|
+
from agent_swarm.orchestrator import SwarmOrchestrator, SwarmResult, SwarmStatus
|
|
4
|
+
from agent_swarm.transition_matrix import TransitionMatrix, ToolCall, HandoffPattern
|
|
5
|
+
from agent_swarm.agents import (
|
|
6
|
+
ReaderAgent,
|
|
7
|
+
EditorAgent,
|
|
8
|
+
BashAgent,
|
|
9
|
+
VerifierAgent,
|
|
10
|
+
PlannerAgent,
|
|
11
|
+
BaseAgent,
|
|
12
|
+
AgentRole,
|
|
13
|
+
create_agent,
|
|
14
|
+
)
|
|
15
|
+
from agent_swarm.models import (
|
|
16
|
+
AgentConfig,
|
|
17
|
+
SwarmResult as SwarmResultPydantic,
|
|
18
|
+
HandoffEvent,
|
|
19
|
+
AgentMessage as AgentMessagePydantic,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
__version__ = "0.1.0"
|
|
23
|
+
__all__ = [
|
|
24
|
+
"SwarmOrchestrator",
|
|
25
|
+
"SwarmResult",
|
|
26
|
+
"SwarmStatus",
|
|
27
|
+
"TransitionMatrix",
|
|
28
|
+
"ToolCall",
|
|
29
|
+
"HandoffPattern",
|
|
30
|
+
"ReaderAgent",
|
|
31
|
+
"EditorAgent",
|
|
32
|
+
"BashAgent",
|
|
33
|
+
"VerifierAgent",
|
|
34
|
+
"PlannerAgent",
|
|
35
|
+
"BaseAgent",
|
|
36
|
+
"AgentRole",
|
|
37
|
+
"create_agent",
|
|
38
|
+
"AgentConfig",
|
|
39
|
+
"HandoffEvent",
|
|
40
|
+
"AgentMessagePydantic",
|
|
41
|
+
]
|
agent_swarm/agents.py
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
"""Specialized micro-agents for the agent swarm."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from agent_swarm.models import AgentConfig, AgentRole as PydanticAgentRole
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentRole(str, Enum):
|
|
13
|
+
"""Roles that micro-agents can assume."""
|
|
14
|
+
|
|
15
|
+
READER = "reader"
|
|
16
|
+
EDITOR = "editor"
|
|
17
|
+
BASH = "bash"
|
|
18
|
+
VERIFIER = "verifier"
|
|
19
|
+
PLANNER = "planner"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ToolDef:
|
|
24
|
+
"""Definition of a tool available to an agent."""
|
|
25
|
+
|
|
26
|
+
name: str
|
|
27
|
+
description: str
|
|
28
|
+
parameters: dict[str, Any] = field(default_factory=dict)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class AgentMessage:
|
|
33
|
+
"""A message in the agent swarm conversation."""
|
|
34
|
+
|
|
35
|
+
role: str
|
|
36
|
+
content: str
|
|
37
|
+
tool_calls: list[dict[str, Any]] = field(default_factory=list)
|
|
38
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
READER_TOOLS = [
|
|
42
|
+
ToolDef(name="read", description="Read file contents at a given path", parameters={"path": {"type": "string"}}),
|
|
43
|
+
ToolDef(name="grep", description="Search file contents for a pattern", parameters={"pattern": {"type": "string"}, "path": {"type": "string"}}),
|
|
44
|
+
ToolDef(name="glob", description="Find files matching a pattern", parameters={"pattern": {"type": "string"}}),
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
EDITOR_TOOLS = [
|
|
48
|
+
ToolDef(name="edit", description="Edit a file by replacing a string", parameters={"file_path": {"type": "string"}, "old_string": {"type": "string"}, "new_string": {"type": "string"}}),
|
|
49
|
+
ToolDef(name="write", description="Write content to a file", parameters={"file_path": {"type": "string"}, "content": {"type": "string"}}),
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
BASH_TOOLS = [
|
|
53
|
+
ToolDef(name="bash", description="Execute a shell command", parameters={"command": {"type": "string"}, "timeout": {"type": "integer"}}),
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
VERIFIER_TOOLS = [
|
|
57
|
+
ToolDef(name="bash", description="Execute shell commands for testing", parameters={"command": {"type": "string"}}),
|
|
58
|
+
ToolDef(name="read", description="Read files to verify changes", parameters={"path": {"type": "string"}}),
|
|
59
|
+
ToolDef(name="grep", description="Search for patterns in files", parameters={"pattern": {"type": "string"}, "path": {"type": "string"}}),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
PLANNER_TOOLS = [
|
|
63
|
+
ToolDef(name="question", description="Ask the user a clarifying question", parameters={"question": {"type": "string"}}),
|
|
64
|
+
ToolDef(name="glob", description="Find files to understand project structure", parameters={"pattern": {"type": "string"}}),
|
|
65
|
+
ToolDef(name="read", description="Read files for context", parameters={"path": {"type": "string"}}),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# System prompts for each agent role
|
|
69
|
+
SYSTEM_PROMPTS: dict[str, str] = {
|
|
70
|
+
"reader": (
|
|
71
|
+
"You are a Reader agent. Your job is to explore the codebase, "
|
|
72
|
+
"read files, search for patterns, and gather context. You do NOT "
|
|
73
|
+
"edit files or run commands. When you have enough context, hand off "
|
|
74
|
+
"to the Editor, Bash, or Verifier agent as appropriate.\n\n"
|
|
75
|
+
"Guidelines:\n"
|
|
76
|
+
"- Start by understanding the project structure\n"
|
|
77
|
+
"- Read relevant files thoroughly before handing off\n"
|
|
78
|
+
"- Use grep/glob to find relevant code\n"
|
|
79
|
+
"- Summarize your findings in the handoff context"
|
|
80
|
+
),
|
|
81
|
+
"editor": (
|
|
82
|
+
"You are an Editor agent. Your job is to write and modify code. "
|
|
83
|
+
"You make precise, targeted edits. After editing, hand off to the "
|
|
84
|
+
"Verifier to check your work.\n\n"
|
|
85
|
+
"Guidelines:\n"
|
|
86
|
+
"- Make minimal, focused changes\n"
|
|
87
|
+
"- Preserve existing code style and conventions\n"
|
|
88
|
+
"- Add comments only when asked\n"
|
|
89
|
+
"- After editing, always hand off to the Verifier"
|
|
90
|
+
),
|
|
91
|
+
"bash": (
|
|
92
|
+
"You are a Bash agent. Your job is to execute shell commands to "
|
|
93
|
+
"install dependencies, run tests, start servers, and perform "
|
|
94
|
+
"system operations. You do NOT edit files directly.\n\n"
|
|
95
|
+
"Guidelines:\n"
|
|
96
|
+
"- Always use absolute paths\n"
|
|
97
|
+
"- Set reasonable timeouts (60s default)\n"
|
|
98
|
+
"- Handle errors gracefully\n"
|
|
99
|
+
"- Report results clearly for the next agent"
|
|
100
|
+
),
|
|
101
|
+
"verifier": (
|
|
102
|
+
"You are a Verifier agent. Your job is to validate that changes "
|
|
103
|
+
"work correctly by running tests, checking linting, and verifying "
|
|
104
|
+
"the code does what was intended.\n\n"
|
|
105
|
+
"Guidelines:\n"
|
|
106
|
+
"- Run the project's test suite first\n"
|
|
107
|
+
"- Check for type errors if a typechecker is available\n"
|
|
108
|
+
"- Run linters to catch style issues\n"
|
|
109
|
+
"- If tests fail, hand off to the Editor with specific details\n"
|
|
110
|
+
"- If all checks pass, report success"
|
|
111
|
+
),
|
|
112
|
+
"planner": (
|
|
113
|
+
"You are a Planner agent. Your job is to break down tasks, define "
|
|
114
|
+
"the approach, and coordinate which agents should handle which steps. "
|
|
115
|
+
"You ask clarifying questions when requirements are ambiguous.\n\n"
|
|
116
|
+
"Guidelines:\n"
|
|
117
|
+
"- Break complex tasks into subtasks\n"
|
|
118
|
+
"- Assign subtasks to the most appropriate agent role\n"
|
|
119
|
+
"- Ask questions when requirements are unclear\n"
|
|
120
|
+
"- Track progress across agent handoffs"
|
|
121
|
+
),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass
|
|
126
|
+
class BaseAgent:
|
|
127
|
+
"""Base class for all micro-agents in the swarm.
|
|
128
|
+
|
|
129
|
+
Each agent has:
|
|
130
|
+
- A role defining its specialization
|
|
131
|
+
- A set of tools it can use
|
|
132
|
+
- A system prompt for LLM interactions
|
|
133
|
+
- A list of roles it can hand off to
|
|
134
|
+
- A context history of messages
|
|
135
|
+
|
|
136
|
+
Attributes:
|
|
137
|
+
role: The agent's specialization role.
|
|
138
|
+
tools: List of tool definitions available to this agent.
|
|
139
|
+
system_prompt: System prompt for LLM interactions.
|
|
140
|
+
can_handoff_to: List of roles this agent can transfer control to.
|
|
141
|
+
_context: Internal message history.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
role: AgentRole
|
|
145
|
+
tools: list[ToolDef]
|
|
146
|
+
system_prompt: str
|
|
147
|
+
can_handoff_to: list[AgentRole]
|
|
148
|
+
_context: list[AgentMessage] = field(default_factory=list, repr=False)
|
|
149
|
+
|
|
150
|
+
def add_message(self, message: AgentMessage) -> None:
|
|
151
|
+
"""Add a message to the agent's context history.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
message: The message to add.
|
|
155
|
+
"""
|
|
156
|
+
self._context.append(message)
|
|
157
|
+
|
|
158
|
+
def get_context(self) -> list[AgentMessage]:
|
|
159
|
+
"""Return a copy of the agent's context history."""
|
|
160
|
+
return list(self._context)
|
|
161
|
+
|
|
162
|
+
def clear_context(self) -> None:
|
|
163
|
+
"""Clear the agent's context history."""
|
|
164
|
+
self._context.clear()
|
|
165
|
+
|
|
166
|
+
def tool_names(self) -> list[str]:
|
|
167
|
+
"""Return the names of available tools."""
|
|
168
|
+
return [t.name for t in self.tools]
|
|
169
|
+
|
|
170
|
+
def can_handle(self, tool_name: str) -> bool:
|
|
171
|
+
"""Check if this agent can use the given tool.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
tool_name: Name of the tool to check.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
True if the agent has access to this tool.
|
|
178
|
+
"""
|
|
179
|
+
return tool_name in self.tool_names()
|
|
180
|
+
|
|
181
|
+
def to_dict(self) -> dict[str, Any]:
|
|
182
|
+
"""Serialize the agent to a dictionary."""
|
|
183
|
+
return {
|
|
184
|
+
"role": self.role.value,
|
|
185
|
+
"tools": [t.name for t in self.tools],
|
|
186
|
+
"system_prompt": self.system_prompt,
|
|
187
|
+
"can_handoff_to": [r.value for r in self.can_handoff_to],
|
|
188
|
+
"context": [
|
|
189
|
+
{"role": m.role, "content": m.content, "metadata": m.metadata}
|
|
190
|
+
for m in self._context
|
|
191
|
+
],
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
def get_config(self) -> AgentConfig:
|
|
195
|
+
"""Get a Pydantic AgentConfig for this agent."""
|
|
196
|
+
return AgentConfig(
|
|
197
|
+
role=PydanticAgentRole(self.role.value),
|
|
198
|
+
system_prompt=self.system_prompt,
|
|
199
|
+
tools=[
|
|
200
|
+
AgentConfig.model_fields["tools"].annotation.__args__[0]( # type: ignore[attr-defined]
|
|
201
|
+
name=t.name,
|
|
202
|
+
description=t.description,
|
|
203
|
+
parameters=t.parameters,
|
|
204
|
+
)
|
|
205
|
+
] if False else [], # Handled by for_role
|
|
206
|
+
can_handoff_to=[PydanticAgentRole(r.value) for r in self.can_handoff_to],
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
def execute(self, task: str, context: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
210
|
+
"""Execute a task within this agent's specialization.
|
|
211
|
+
|
|
212
|
+
This is a local execution method that processes the task based on
|
|
213
|
+
the agent's role and available tools. It does not call an LLM; instead
|
|
214
|
+
it records the execution plan and returns structured output.
|
|
215
|
+
|
|
216
|
+
For LLM-based execution, use execute_with_llm() which integrates
|
|
217
|
+
with litellm for actual model calls.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
task: The task description to execute.
|
|
221
|
+
context: Optional context from previous agent handoffs.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Dictionary with execution results including:
|
|
225
|
+
- role: The agent's role
|
|
226
|
+
- task: The task description
|
|
227
|
+
- plan: List of planned tool calls
|
|
228
|
+
- context: Enriched context for handoff
|
|
229
|
+
- status: Execution status
|
|
230
|
+
"""
|
|
231
|
+
context = context or {}
|
|
232
|
+
|
|
233
|
+
# Determine primary tool based on role
|
|
234
|
+
role_primary_tools: dict[str, str] = {
|
|
235
|
+
"reader": "read",
|
|
236
|
+
"editor": "edit",
|
|
237
|
+
"bash": "bash",
|
|
238
|
+
"verifier": "bash",
|
|
239
|
+
"planner": "question",
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
primary_tool = role_primary_tools.get(self.role.value, "read")
|
|
243
|
+
|
|
244
|
+
# Build execution plan
|
|
245
|
+
plan = [ToolCall(name=primary_tool, confidence=1.0, args={"task": task})]
|
|
246
|
+
|
|
247
|
+
# Add secondary tools based on role
|
|
248
|
+
if self.role == AgentRole.READER:
|
|
249
|
+
plan.append(ToolCall(name="grep", confidence=0.8, args={"pattern": task}))
|
|
250
|
+
plan.append(ToolCall(name="glob", confidence=0.6, args={"pattern": "**/*"}))
|
|
251
|
+
elif self.role == AgentRole.EDITOR:
|
|
252
|
+
plan.append(ToolCall(name="write", confidence=0.7, args={"task": task}))
|
|
253
|
+
elif self.role == AgentRole.VERIFIER:
|
|
254
|
+
plan.append(ToolCall(name="read", confidence=0.8, args={"task": "verify output"}))
|
|
255
|
+
|
|
256
|
+
# Record execution in context
|
|
257
|
+
self.add_message(AgentMessage(
|
|
258
|
+
role="assistant",
|
|
259
|
+
content=f"[{self.role.value}] Executing: {task}",
|
|
260
|
+
metadata={"plan": [tc.to_dict() for tc in plan], **context},
|
|
261
|
+
))
|
|
262
|
+
|
|
263
|
+
# Build handoff recommendations
|
|
264
|
+
recommended_handoff = None
|
|
265
|
+
if self.can_handoff_to:
|
|
266
|
+
# Pick the most likely handoff target based on role logic
|
|
267
|
+
handoff_priority: dict[str, list[str]] = {
|
|
268
|
+
"reader": ["editor", "verifier", "bash", "planner"],
|
|
269
|
+
"editor": ["verifier", "reader", "bash", "planner"],
|
|
270
|
+
"bash": ["reader", "editor", "verifier", "planner"],
|
|
271
|
+
"verifier": ["editor", "reader", "bash", "planner"],
|
|
272
|
+
"planner": ["reader", "editor", "bash", "verifier"],
|
|
273
|
+
}
|
|
274
|
+
priority_list = handoff_priority.get(self.role.value, [])
|
|
275
|
+
for target in priority_list:
|
|
276
|
+
target_role = AgentRole(target)
|
|
277
|
+
if target_role in self.can_handoff_to:
|
|
278
|
+
recommended_handoff = target_role.value
|
|
279
|
+
break
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
"role": self.role.value,
|
|
283
|
+
"task": task,
|
|
284
|
+
"plan": [tc.to_dict() for tc in plan],
|
|
285
|
+
"context": {
|
|
286
|
+
**context,
|
|
287
|
+
"executed_by": self.role.value,
|
|
288
|
+
"primary_tool": primary_tool,
|
|
289
|
+
},
|
|
290
|
+
"recommended_handoff": recommended_handoff,
|
|
291
|
+
"status": "completed",
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@dataclass
|
|
296
|
+
class ToolCall:
|
|
297
|
+
"""A tool call planned by an agent."""
|
|
298
|
+
|
|
299
|
+
name: str
|
|
300
|
+
confidence: float = 1.0
|
|
301
|
+
args: dict[str, Any] = field(default_factory=dict)
|
|
302
|
+
|
|
303
|
+
def to_dict(self) -> dict[str, Any]:
|
|
304
|
+
return {
|
|
305
|
+
"name": self.name,
|
|
306
|
+
"confidence": self.confidence,
|
|
307
|
+
"args": self.args,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
class ReaderAgent(BaseAgent):
|
|
312
|
+
"""Agent specialized in reading and searching code.
|
|
313
|
+
|
|
314
|
+
The Reader explores the codebase, reads files, searches for patterns,
|
|
315
|
+
and gathers context. It hands off to Editor (to make changes),
|
|
316
|
+
Verifier (to validate), or Bash (to execute commands).
|
|
317
|
+
|
|
318
|
+
Transition data: Read→Edit=0.22, Read→Bash=0.37
|
|
319
|
+
"""
|
|
320
|
+
|
|
321
|
+
def __init__(self) -> None:
|
|
322
|
+
super().__init__(
|
|
323
|
+
role=AgentRole.READER,
|
|
324
|
+
tools=READER_TOOLS,
|
|
325
|
+
system_prompt=SYSTEM_PROMPTS["reader"],
|
|
326
|
+
can_handoff_to=[AgentRole.EDITOR, AgentRole.BASH, AgentRole.VERIFIER, AgentRole.PLANNER],
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
class EditorAgent(BaseAgent):
|
|
331
|
+
"""Agent specialized in writing and editing code.
|
|
332
|
+
|
|
333
|
+
The Editor makes precise, targeted edits based on context from
|
|
334
|
+
other agents. After editing, it typically hands off to the Verifier.
|
|
335
|
+
|
|
336
|
+
Transition data: Edit→Bash=0.34, Edit→Read=0.28
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
def __init__(self) -> None:
|
|
340
|
+
super().__init__(
|
|
341
|
+
role=AgentRole.EDITOR,
|
|
342
|
+
tools=EDITOR_TOOLS,
|
|
343
|
+
system_prompt=SYSTEM_PROMPTS["editor"],
|
|
344
|
+
can_handoff_to=[AgentRole.READER, AgentRole.BASH, AgentRole.VERIFIER, AgentRole.PLANNER],
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
class BashAgent(BaseAgent):
|
|
349
|
+
"""Agent specialized in running shell commands.
|
|
350
|
+
|
|
351
|
+
The Bash agent executes system commands, runs tests, installs
|
|
352
|
+
dependencies, and reports results.
|
|
353
|
+
|
|
354
|
+
Transition data: Bash→Bash=0.59, Bash→Edit=0.18
|
|
355
|
+
"""
|
|
356
|
+
|
|
357
|
+
def __init__(self) -> None:
|
|
358
|
+
super().__init__(
|
|
359
|
+
role=AgentRole.BASH,
|
|
360
|
+
tools=BASH_TOOLS,
|
|
361
|
+
system_prompt=SYSTEM_PROMPTS["bash"],
|
|
362
|
+
can_handoff_to=[AgentRole.READER, AgentRole.EDITOR, AgentRole.VERIFIER, AgentRole.PLANNER],
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class VerifierAgent(BaseAgent):
|
|
367
|
+
"""Agent specialized in verifying code changes and running tests.
|
|
368
|
+
|
|
369
|
+
The Verifier runs tests, linters, and type checkers to validate
|
|
370
|
+
changes made by the Editor.
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
def __init__(self) -> None:
|
|
374
|
+
super().__init__(
|
|
375
|
+
role=AgentRole.VERIFIER,
|
|
376
|
+
tools=VERIFIER_TOOLS,
|
|
377
|
+
system_prompt=SYSTEM_PROMPTS["verifier"],
|
|
378
|
+
can_handoff_to=[AgentRole.READER, AgentRole.EDITOR, AgentRole.BASH, AgentRole.PLANNER],
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class PlannerAgent(BaseAgent):
|
|
383
|
+
"""Agent specialized in planning and coordinating work.
|
|
384
|
+
|
|
385
|
+
The Planner breaks down tasks, defines approaches, and coordinates
|
|
386
|
+
which agents should handle which steps.
|
|
387
|
+
"""
|
|
388
|
+
|
|
389
|
+
def __init__(self) -> None:
|
|
390
|
+
super().__init__(
|
|
391
|
+
role=AgentRole.PLANNER,
|
|
392
|
+
tools=PLANNER_TOOLS,
|
|
393
|
+
system_prompt=SYSTEM_PROMPTS["planner"],
|
|
394
|
+
can_handoff_to=[AgentRole.READER, AgentRole.EDITOR, AgentRole.BASH, AgentRole.VERIFIER],
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
AGENT_CLASSES: dict[AgentRole, type[BaseAgent]] = {
|
|
399
|
+
AgentRole.READER: ReaderAgent,
|
|
400
|
+
AgentRole.EDITOR: EditorAgent,
|
|
401
|
+
AgentRole.BASH: BashAgent,
|
|
402
|
+
AgentRole.VERIFIER: VerifierAgent,
|
|
403
|
+
AgentRole.PLANNER: PlannerAgent,
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def create_agent(role: AgentRole | str) -> BaseAgent:
|
|
408
|
+
"""Factory function to create an agent by role.
|
|
409
|
+
|
|
410
|
+
Args:
|
|
411
|
+
role: The agent role to create, either as an AgentRole enum or string.
|
|
412
|
+
|
|
413
|
+
Returns:
|
|
414
|
+
A new agent instance of the specified role.
|
|
415
|
+
|
|
416
|
+
Raises:
|
|
417
|
+
ValueError: If the role is not recognized.
|
|
418
|
+
"""
|
|
419
|
+
if isinstance(role, str):
|
|
420
|
+
role = AgentRole(role)
|
|
421
|
+
agent_class = AGENT_CLASSES.get(role)
|
|
422
|
+
if agent_class is None:
|
|
423
|
+
raise ValueError(f"Unknown agent role: {role}. Available: {list(AGENT_CLASSES.keys())}")
|
|
424
|
+
return agent_class()
|