strands-swarms 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.
@@ -0,0 +1,265 @@
1
+ """Orchestrator agent that plans, creates sub-agents, assigns tasks, and synthesizes completion."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from strands import Agent, tool
8
+ from strands.hooks import HookRegistry
9
+
10
+ from .events import AgentInfo, AgentSpawnedEvent, PlanningCompletedEvent, TaskCreatedEvent, TaskInfo
11
+
12
+ if TYPE_CHECKING:
13
+ from strands.models import Model
14
+
15
+ from .swarm import SwarmConfig
16
+
17
+
18
+ # --- System Prompt ---
19
+
20
+ ORCHESTRATOR_SYSTEM_PROMPT = """\
21
+ You are a workflow orchestrator that plans, creates sub-agents, assigns tasks, and synthesizes completion.
22
+
23
+ ## Your Four Responsibilities
24
+
25
+ ### 1. Planning
26
+ Analyze the request and break it into logical steps. Determine what specialized agents are needed.
27
+
28
+ ### 2. Creating Sub-agents
29
+ Spawn specialized agents with appropriate tools and models. Each agent should have a focused role.
30
+
31
+ ### 3. Assigning Tasks
32
+ Create tasks and assign them to agents. Define dependencies when one task needs results from another.
33
+
34
+ ### 4. Synthesizing Completion
35
+ After all tasks complete, synthesize outputs into a cohesive final response. Be direct - deliver
36
+ the result as if you completed the task yourself without mentioning sub-agents or the process.
37
+
38
+ ## Your Process
39
+
40
+ Think through the problem step-by-step:
41
+
42
+ 1. **Analyze the request** - What needs to be done? Break it into logical steps.
43
+ 2. **Design agents** - What specialized agents are needed? What tools does each need?
44
+ 3. **Plan dependencies** - Which tasks must wait for others? Draw the dependency graph.
45
+ 4. **Finalize** - Call finalize_plan when done creating agents and tasks.
46
+
47
+ ## Output Format
48
+
49
+ Structure your response clearly:
50
+
51
+ ```
52
+ ### Analysis
53
+ [Brief analysis of what's needed]
54
+
55
+ ### Agents Needed
56
+ [List agents you'll create and why]
57
+
58
+ ### Workflow Dependencies
59
+ [Show which tasks depend on which, e.g., "write_report waits for research to complete"]
60
+
61
+ ### Building Workflow
62
+ [Then call the tools]
63
+ ```
64
+
65
+ ## Tools
66
+
67
+ **spawn_agent(name, role, tools, model)**
68
+ - Creates an agent with specific capabilities
69
+
70
+ **create_task(name, agent_name, description, depends_on)**
71
+ - Creates a task assigned to an agent
72
+ - depends_on: list of task names that must finish BEFORE this task starts
73
+
74
+ **finalize_plan()**
75
+ - Signals that planning is complete
76
+ - Call this after you've created all agents and tasks
77
+
78
+ ## Example
79
+
80
+ For "Research AI and write a report":
81
+
82
+ ### Analysis
83
+ Need to: 1) Research AI trends, 2) Write a report based on research.
84
+ The report cannot be written until research is done.
85
+
86
+ ### Agents Needed
87
+ - researcher: Gathers information (needs search_web)
88
+ - writer: Creates reports (needs write_file)
89
+
90
+ ### Workflow Dependencies
91
+ research_task → write_task (writer waits for researcher)
92
+
93
+ ### Building Workflow
94
+ [spawn agents, create tasks with depends_on, finalize_plan]
95
+
96
+ Now analyze the request and build the workflow.
97
+ """
98
+
99
+
100
+ # --- Swarm Config Management ---
101
+
102
+ _swarm_config: SwarmConfig | None = None
103
+ _hook_registry: HookRegistry | None = None
104
+
105
+
106
+ def set_swarm_config(config: SwarmConfig | None, hook_registry: HookRegistry | None = None) -> None:
107
+ """Set swarm config and hook registry. Called by DynamicSwarm before orchestration."""
108
+ global _swarm_config, _hook_registry
109
+ _swarm_config = config
110
+ _hook_registry = hook_registry
111
+
112
+
113
+ def get_swarm_config() -> SwarmConfig:
114
+ """Get swarm config, raising RuntimeError if not set."""
115
+ if _swarm_config is None:
116
+ raise RuntimeError("No swarm config set. Orchestrator tools must be used within DynamicSwarm.")
117
+ return _swarm_config
118
+
119
+
120
+ def _emit(event: Any) -> None:
121
+ if _hook_registry and _hook_registry.has_callbacks():
122
+ _hook_registry.invoke_callbacks(event)
123
+
124
+
125
+ def _build_agents_info(config: SwarmConfig) -> list[AgentInfo]:
126
+ return [
127
+ AgentInfo(
128
+ name=a.name, role=a.role, tools=a.tools, model=a.model, color=a.color
129
+ )
130
+ for a in config.agents.values()
131
+ ]
132
+
133
+
134
+ def _build_tasks_info(config: SwarmConfig) -> list[TaskInfo]:
135
+ return [
136
+ TaskInfo(
137
+ name=t.name, agent=t.agent, description=t.description, depends_on=t.depends_on
138
+ )
139
+ for t in config.tasks.values()
140
+ ]
141
+
142
+
143
+ # --- Orchestrator Tools ---
144
+
145
+
146
+ @tool
147
+ def list_available_tools() -> str:
148
+ """List all tools available for spawned agents."""
149
+ tools = get_swarm_config().available_tool_names
150
+ return "Available tools:\n" + "\n".join(f" - {t}" for t in tools) if tools else "No tools available."
151
+
152
+
153
+ @tool
154
+ def list_available_models() -> str:
155
+ """List all models available for spawned agents."""
156
+ models = get_swarm_config().available_model_names
157
+ return "Available models:\n" + "\n".join(f" - {m}" for m in models) if models else "No models configured."
158
+
159
+
160
+ @tool
161
+ def spawn_agent(
162
+ name: str,
163
+ role: str,
164
+ instructions: str = "",
165
+ tools: list[str] | None = None,
166
+ model: str | None = None,
167
+ ) -> str:
168
+ """Spawn a new sub-agent with specific capabilities."""
169
+ from .swarm import AgentDefinition
170
+
171
+ config = get_swarm_config()
172
+ definition = AgentDefinition(
173
+ name=name,
174
+ role=role,
175
+ instructions=instructions or None,
176
+ tools=tools or [],
177
+ model=model,
178
+ )
179
+
180
+ try:
181
+ config.register_agent(definition)
182
+ agent_def = config.agents.get(name)
183
+ _emit(AgentSpawnedEvent(
184
+ name=name,
185
+ role=role,
186
+ instructions=instructions or None,
187
+ tools=tools or [],
188
+ model=model,
189
+ color=agent_def.color if agent_def else None,
190
+ ))
191
+ return f"✓ Spawned '{name}' ({role})"
192
+ except ValueError as e:
193
+ return f"✗ Failed: {e}"
194
+
195
+
196
+ @tool
197
+ def create_task(
198
+ name: str,
199
+ agent_name: str,
200
+ description: str = "",
201
+ depends_on: list[str] | None = None,
202
+ ) -> str:
203
+ """Create a task and assign it to a spawned agent."""
204
+ from .swarm import TaskDefinition
205
+
206
+ config = get_swarm_config()
207
+ definition = TaskDefinition(
208
+ name=name,
209
+ agent=agent_name,
210
+ description=description or None,
211
+ depends_on=depends_on or [],
212
+ )
213
+
214
+ try:
215
+ config.register_task(definition)
216
+ _emit(TaskCreatedEvent(
217
+ name=name,
218
+ agent=agent_name,
219
+ description=description or None,
220
+ depends_on=depends_on or [],
221
+ ))
222
+ return f"✓ Created '{name}' -> '{agent_name}'"
223
+ except ValueError as e:
224
+ return f"✗ Failed: {e}"
225
+
226
+
227
+ @tool
228
+ def get_planning_status() -> str:
229
+ """Get current planning status - agents and tasks defined so far."""
230
+ return get_swarm_config().get_summary()
231
+
232
+
233
+ @tool
234
+ def finalize_plan() -> str:
235
+ """Signal that planning is complete. Call after creating all agents and tasks."""
236
+ config = get_swarm_config()
237
+
238
+ _emit(PlanningCompletedEvent(
239
+ entry_task=None,
240
+ agents=_build_agents_info(config),
241
+ tasks=_build_tasks_info(config),
242
+ ))
243
+ return f"✓ Planning complete.\n\n{config.get_summary()}"
244
+
245
+
246
+ # --- Agent Factory ---
247
+
248
+ ORCHESTRATOR_TOOLS = [
249
+ list_available_tools,
250
+ list_available_models,
251
+ spawn_agent,
252
+ create_task,
253
+ get_planning_status,
254
+ finalize_plan,
255
+ ]
256
+
257
+
258
+ def create_orchestrator_agent(model: Model | None = None) -> Agent:
259
+ """Create orchestrator agent that plans, creates sub-agents, assigns tasks, and synthesizes completion."""
260
+ return Agent(
261
+ name="orchestrator",
262
+ system_prompt=ORCHESTRATOR_SYSTEM_PROMPT,
263
+ model=model,
264
+ tools=ORCHESTRATOR_TOOLS,
265
+ )
File without changes