@evolvingmachines/evolve 0.0.55-project-sable.20260916.db0bc0bb → 0.0.56

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.
Files changed (73) hide show
  1. package/dist/cli/index.cjs +32 -32
  2. package/dist/cli/index.js +27 -27
  3. package/package.json +4 -6
  4. package/skills/evolve/SKILL.md +4 -3
  5. package/skills/evolve-evals/SKILL.md +112 -0
  6. package/{docs-evals → skills/evolve-evals/references}/cli-reference/run.mdx +1 -1
  7. package/{docs-evals → skills/evolve-evals/references}/cli-reference/skills.mdx +4 -4
  8. package/{docs-evals → skills/evolve-evals/references}/core-concepts/agents.mdx +2 -2
  9. package/{docs-evals → skills/evolve-evals/references}/core-concepts/analyze.mdx +1 -1
  10. package/{docs-evals → skills/evolve-evals/references}/core-concepts/jobs.mdx +3 -3
  11. package/{docs-evals → skills/evolve-evals/references}/core-concepts/models.mdx +1 -1
  12. package/{docs-evals → skills/evolve-evals/references}/core-concepts/sandboxes.mdx +2 -2
  13. package/{docs-evals → skills/evolve-evals/references}/core-concepts/secrets.mdx +1 -1
  14. package/{docs-evals → skills/evolve-evals/references}/core-concepts/trial-outputs.mdx +1 -1
  15. package/{docs-evals → skills/evolve-evals/references}/getting-started/quick-start.mdx +1 -1
  16. package/{docs-evals → skills/evolve-evals/references}/index.mdx +1 -1
  17. package/{docs-evals → skills/evolve-evals/references}/sdk/python.mdx +1 -1
  18. package/{docs-evals → skills/evolve-evals/references}/sdk/typescript.mdx +1 -1
  19. package/{docs-evals → skills/evolve-evals/references}/sdk-reference/errors.mdx +2 -2
  20. package/{docs-evals → skills/evolve-evals/references}/sdk-reference/jobs.mdx +2 -2
  21. package/docs-agents/SKILL.md +0 -138
  22. package/docs-agents/index.md +0 -81
  23. package/docs-agents/python/01-getting-started.md +0 -511
  24. package/docs-agents/python/02-configuration.md +0 -855
  25. package/docs-agents/python/03-runtime.md +0 -899
  26. package/docs-agents/python/04-streaming.md +0 -451
  27. package/docs-agents/python/05-swarm-pipeline.md +0 -953
  28. package/docs-agents/python/index.md +0 -122
  29. package/docs-agents/typescript/01-getting-started.md +0 -494
  30. package/docs-agents/typescript/02-configuration.md +0 -841
  31. package/docs-agents/typescript/03-runtime.md +0 -864
  32. package/docs-agents/typescript/04-streaming.md +0 -478
  33. package/docs-agents/typescript/05-swarm-pipeline.md +0 -957
  34. package/docs-agents/typescript/index.md +0 -121
  35. package/docs-evals/SKILL.md +0 -112
  36. package/docs-evals/docs.json +0 -180
  37. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/agent.mdx +0 -0
  38. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/analysis.mdx +0 -0
  39. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/analyze.mdx +0 -0
  40. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/auth.mdx +0 -0
  41. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/check.mdx +0 -0
  42. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/dataset.mdx +0 -0
  43. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/index.mdx +0 -0
  44. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/job.mdx +0 -0
  45. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/secrets.mdx +0 -0
  46. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/skill.mdx +0 -0
  47. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/trial.mdx +0 -0
  48. /package/{docs-evals → skills/evolve-evals/references}/cli-reference/upload.mdx +0 -0
  49. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/check.mdx +0 -0
  50. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/datasets.mdx +0 -0
  51. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/index.mdx +0 -0
  52. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/skills.mdx +0 -0
  53. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/tasks.mdx +0 -0
  54. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/trials.mdx +0 -0
  55. /package/{docs-evals → skills/evolve-evals/references}/core-concepts/upload.mdx +0 -0
  56. /package/{docs-evals → skills/evolve-evals/references}/dashboard/checks.mdx +0 -0
  57. /package/{docs-evals → skills/evolve-evals/references}/dashboard/datasets.mdx +0 -0
  58. /package/{docs-evals → skills/evolve-evals/references}/dashboard/jobs.mdx +0 -0
  59. /package/{docs-evals → skills/evolve-evals/references}/dashboard/traces.mdx +0 -0
  60. /package/{docs-evals → skills/evolve-evals/references}/dashboard/trial-viewer.mdx +0 -0
  61. /package/{docs-evals → skills/evolve-evals/references}/getting-started/installation.mdx +0 -0
  62. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/agents.mdx +0 -0
  63. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/analyses.mdx +0 -0
  64. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/auth.mdx +0 -0
  65. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/checks.mdx +0 -0
  66. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/datasets.mdx +0 -0
  67. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/index.mdx +0 -0
  68. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/meta.mdx +0 -0
  69. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/skills.mdx +0 -0
  70. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/trials.mdx +0 -0
  71. /package/{docs-evals → skills/evolve-evals/references}/sdk-reference/types.mdx +0 -0
  72. /package/{docs-evals → skills/evolve-evals/references}/snippets/global-options.mdx +0 -0
  73. /package/{docs-evals → skills/evolve-evals/references}/snippets/list-flags.mdx +0 -0
@@ -1,953 +0,0 @@
1
- # Swarm & Pipeline
2
-
3
- Functional programming for AI agents: `map`, `filter`, `reduce`, `best_of`, `verify`.
4
-
5
- ```python
6
- from evolve import Swarm, SwarmConfig, AgentConfig, IntegrationsSetup
7
- from pydantic import BaseModel # Or use plain JSON Schema dicts instead
8
-
9
- agent = AgentConfig(type='claude')
10
-
11
- swarm = Swarm(SwarmConfig(
12
- agent=agent, # Default agent for all operations
13
- concurrency=4, # Max parallel sandboxes (default: 4)
14
- timeout_ms=3_600_000, # Default timeout per worker (default: 1 hour)
15
- tag='my-pipeline', # Tag prefix for observability
16
- skills=['anthropics/skills'], # Default skills (skills.sh / git / local references)
17
- integrations=IntegrationsSetup( # Default Integrations config for all workers
18
- user_id='root',
19
- apps=['gmail', 'notion'],
20
- ),
21
- mcp_servers={...}, # Default MCP servers for all workers
22
- retry=RetryConfig( # Default retry config for all operations
23
- max_attempts=3,
24
- backoff_ms=1000,
25
- backoff_multiplier=2,
26
- ),
27
- ))
28
- ```
29
-
30
- > **Defaults**: `agent`, `skills`, `integrations`, `mcp_servers`, `timeout_ms`, and `retry` set here are inherited by all operations (`map`, `filter`, `reduce`, `best_of`). Pass these options to individual operations to override.
31
-
32
- **SwarmConfig** — configuration for Swarm instance:
33
- ```python
34
- SwarmConfig(
35
- agent=AgentConfig,
36
- sandbox=SandboxProvider,
37
- skills=list[str],
38
- integrations=IntegrationsSetup,
39
- mcp_servers=dict[str, McpServerConfig],
40
- concurrency=int,
41
- timeout_ms=int,
42
- workspace_mode=str,
43
- tag=str,
44
- retry=RetryConfig,
45
- )
46
- ```
47
-
48
- | Option | Default | Notes |
49
- |--------|---------|-------|
50
- | `agent.type` | `'claude'` | Auto-resolved from env |
51
- | `agent.model` | per type | `'opus'` (claude), `'gpt-5.6-sol'` (codex), etc. |
52
- | `sandbox` | auto-resolved | Provider for every worker; falls back to env (`E2B_API_KEY`, `DAYTONA_API_KEY`, `MODAL_TOKEN_*`, `EVOLVE_API_KEY`) |
53
- | `skills` | `None` | Set here or per-operation |
54
- | `integrations` | `None` | Set here or per-operation |
55
- | `mcp_servers` | `None` | Set here or per-operation |
56
- | `concurrency` | `4` | Max parallel sandboxes |
57
- | `timeout_ms` | `3_600_000` | 1 hour per worker |
58
- | `workspace_mode` | `'knowledge'` | `'knowledge'` or `'swe'`; `'task'` is not a Swarm mode (the type does not stop you writing it — do not) |
59
- | `tag` | `'swarm'` | Observability prefix |
60
- | `retry` | `None` | Set here or per-operation |
61
-
62
- The `agent` here is the full `AgentConfig` — the same shape `Evolve(config=...)` takes, so `api_key`, `provider_api_key`, `oauth_token` and `max_context_size` all belong on it. Individual operations override this default with the same `AgentConfig` class, not a narrower one: `map(..., agent=AgentConfig(...))`, and `BestOfConfig(task_agents=[...], judge_agent=...)` take exactly what you pass here. (The TypeScript SDK narrows the per-operation form to an `AgentOverride`; Python does not.)
63
-
64
- **Minimal setup** — with `EVOLVE_API_KEY` set (see [Authentication](./01-getting-started.md#authentication)):
65
-
66
- ```python
67
- from dotenv import load_dotenv
68
- load_dotenv() # If using .env file
69
-
70
- from evolve import Swarm
71
-
72
- swarm = Swarm() # Auto-resolves agent (claude) and sandbox from env
73
- ```
74
-
75
- **RetryConfig** — auto-retry on error with exponential backoff:
76
- ```python
77
- RetryConfig(
78
- max_attempts=3,
79
- backoff_ms=1000,
80
- backoff_multiplier=2,
81
- retry_on=lambda r: r.status == 'error', # Custom condition
82
- on_item_retry=lambda idx, attempt, error: ..., # Callback
83
- )
84
- ```
85
-
86
- ## Input Types
87
-
88
- Swarm runs in **knowledge mode** by default—files are uploaded to `context/` in the sandbox.
89
-
90
- **FileMap structure:**
91
-
92
- ```python
93
- # FileMap: dict[path, content]
94
- # - path: str → file path in context/ folder
95
- # - content: str | bytes → file content
96
-
97
- FileMap = dict[str, str | bytes]
98
- ```
99
-
100
- ---
101
-
102
- **Case 1: One file per worker**
103
-
104
- ```python
105
- # 3 workers, each gets 1 file
106
- items: list[FileMap] = [
107
- {'report.txt': 'Q1 revenue...'}, # → Worker 0: context/report.txt
108
- {'report.txt': 'Q2 revenue...'}, # → Worker 1: context/report.txt
109
- {'report.txt': 'Q3 revenue...'}, # → Worker 2: context/report.txt
110
- ]
111
-
112
- results = await swarm.map(
113
- items=items,
114
- prompt='Summarize this report',
115
- )
116
- ```
117
-
118
- ---
119
-
120
- **Case 2: Multiple files per worker**
121
-
122
- ```python
123
- # 3 workers, each gets 2 files
124
- items: list[FileMap] = [
125
- { # → Worker 0:
126
- 'doc1.pdf': open('./doc1.pdf', 'rb').read(), # context/doc1.pdf
127
- 'doc2.pdf': open('./doc2.pdf', 'rb').read(), # context/doc2.pdf
128
- },
129
- { # → Worker 1:
130
- 'doc3.pdf': open('./doc3.pdf', 'rb').read(), # context/doc3.pdf
131
- 'doc4.pdf': open('./doc4.pdf', 'rb').read(), # context/doc4.pdf
132
- },
133
- { # → Worker 2:
134
- 'doc5.pdf': open('./doc5.pdf', 'rb').read(), # context/doc5.pdf
135
- 'doc6.pdf': open('./doc6.pdf', 'rb').read(), # context/doc6.pdf
136
- },
137
- ]
138
-
139
- results = await swarm.map(
140
- items=items,
141
- prompt='Compare these two documents',
142
- )
143
- ```
144
-
145
- ---
146
-
147
- **Case 3: Entire folder per worker**
148
-
149
- ```python
150
- from evolve import read_local_dir
151
-
152
- # read_local_dir(path, recursive) → returns FileMap with all files
153
- items: list[FileMap] = [
154
- read_local_dir('./project-a', recursive=True), # → Worker 0: all files from project-a
155
- read_local_dir('./project-b', recursive=True), # → Worker 1: all files from project-b
156
- read_local_dir('./project-c', recursive=True), # → Worker 2: all files from project-c
157
- ]
158
-
159
- results = await swarm.map(
160
- items=items,
161
- prompt='Review this codebase',
162
- )
163
- ```
164
-
165
- ## Abstractions
166
-
167
- Two types of operations:
168
-
169
- | Operation | Type | Description | Passes On |
170
- |-----------|------|-------------|-----------|
171
- | `best_of` | transform + select | `input` → `output` (best of N candidates) | winner output |
172
- | `map` | transform | `input` → `output` (agent produces new data) | agent output |
173
- | `filter` | gate | `input` → `input` (agent evaluates, condition decides) | original input + status (`success` \| `filtered`) |
174
- | `reduce` | transform | `inputs` → `output` (agent synthesizes) | agent output |
175
-
176
- **Transforms** produce new output files. **Filter** passes through original input files unchanged.
177
-
178
- **BestOfConfig** — run N candidates in parallel, judge picks the best:
179
- ```python
180
- BestOfConfig(
181
- n=int,
182
- judge_criteria=str,
183
- task_agents=list[AgentConfig],
184
- judge_agent=AgentConfig,
185
- skills=list[str],
186
- judge_skills=list[str],
187
- integrations=IntegrationsSetup,
188
- judge_integrations=IntegrationsSetup,
189
- mcp_servers=dict[str, McpServerConfig],
190
- judge_mcp_servers=dict[str, McpServerConfig],
191
- on_candidate_complete=Callable[[int, int, str], None],
192
- on_judge_complete=Callable[[int, int, str], None],
193
- )
194
- ```
195
-
196
- **VerifyConfig** — LLM-as-judge verifies output, retries with feedback if failed:
197
- ```python
198
- VerifyConfig(
199
- criteria=str,
200
- max_attempts=int,
201
- verifier_agent=AgentConfig,
202
- verifier_skills=list[str],
203
- verifier_integrations=IntegrationsSetup,
204
- verifier_mcp_servers=dict[str, McpServerConfig],
205
- on_worker_complete=Callable[[int, int, str], None],
206
- on_verifier_complete=Callable[[int, int, bool, str | None], None],
207
- )
208
- ```
209
-
210
- ### best_of
211
-
212
- Run N agents on the same `item` in parallel, then a judge picks the best. `Agent[i]` outputs `candidates[i]`, judge selects `winner`.
213
-
214
- ```
215
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
216
- │ Sandbox 0 │ │ Sandbox 1 │ │ Sandbox 2 │
217
- │ Agent 0 │ │ Agent 1 │ │ Agent 2 │
218
- │ │ │ │ │ │
219
- │ context/ │ │ context/ │ │ context/ │
220
- │ item │ │ item │ │ item │
221
- │ output/ │ │ output/ │ │ output/ │
222
- │ candidates[0]│ │ candidates[1]│ │ candidates[2]│
223
- └───────┬─────────┘ └───────┬─────────┘ └───────┬─────────┘
224
- │ │ │
225
- └───────────────────┼───────────────────┘
226
-
227
- ┌───────────────┐
228
- │ Judge │
229
- └───────┬───────┘
230
-
231
-
232
- winner
233
- ```
234
-
235
- ```python
236
- # Signature
237
- await swarm.best_of(
238
- item=FileMap | SwarmResult,
239
- prompt=str,
240
- config=BestOfConfig( # n?, judge_criteria, task_agents?, judge_agent?, callbacks
241
- judge_criteria='...',
242
- n=3,
243
- on_candidate_complete=lambda idx, cand_idx, status: ...,
244
- on_judge_complete=lambda idx, winner_idx, reasoning: ...,
245
- ),
246
- name=str, # Operation name for observability (appears in meta.operation_name)
247
- schema=PydanticModel | dict, # Optional
248
- system_prompt=str, # Optional
249
- retry=RetryConfig(...), # Per-candidate retry (judge uses default)
250
- timeout_ms=int, # Optional
251
- ) -> BestOfResult
252
- ```
253
-
254
- ```python
255
- input_item = {'task.txt': 'Complex problem...'}
256
-
257
- result = await swarm.best_of(
258
- item=input_item,
259
- prompt='Solve this problem',
260
- config=BestOfConfig(
261
- n=3,
262
- judge_criteria='Most accurate and well-explained solution',
263
- on_candidate_complete=lambda idx, cand_idx, status: print(f'Candidate {cand_idx}: {status}'),
264
- on_judge_complete=lambda idx, winner_idx, reasoning: print(f'Winner: {winner_idx}'),
265
- ),
266
- )
267
-
268
- print(result.winner) # Best SwarmResult
269
- print(result.winner_index) # 0, 1, or 2
270
- print(result.judge_reasoning) # Why this was chosen
271
- print(result.candidates) # All candidate results
272
- ```
273
-
274
- Use different agents per candidate:
275
-
276
- ```python
277
- claude_agent = AgentConfig(type='claude', model='opus')
278
- codex_agent = AgentConfig(type='codex', model='gpt-5.3-codex')
279
- gemini_agent = AgentConfig(type='gemini', model='gemini-3.5-flash')
280
-
281
- result = await swarm.best_of(
282
- item=input_item,
283
- prompt='Solve this',
284
- config=BestOfConfig(
285
- task_agents=[claude_agent, codex_agent, gemini_agent],
286
- judge_criteria='Best solution quality',
287
- judge_agent=claude_agent,
288
- mcp_servers={...}, # (optional) MCP servers for candidates
289
- judge_mcp_servers={...}, # (optional) MCP servers for judge
290
- skills=['anthropics/skills'], # (optional) Skills for candidates
291
- judge_skills=['anthropics/skills'], # (optional) Skills for judge
292
- integrations=IntegrationsSetup(...), # (optional) Integrations config for candidates
293
- judge_integrations=IntegrationsSetup(...), # (optional) Integrations config for judge
294
- ),
295
- )
296
- ```
297
-
298
- ### map
299
-
300
- Process items in parallel. `Agent[i]` sees `items[i]` and outputs `results[i]` (which includes `result.json` if `schema` provided).
301
-
302
- ```
303
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
304
- │ Sandbox 0 │ │ Sandbox 1 │ │ Sandbox 2 │
305
- │ Agent 0 │ │ Agent 1 │ │ Agent 2 │
306
- │ │ │ │ │ │
307
- │ context/ │ │ context/ │ │ context/ │
308
- │ items[0] │ │ items[1] │ │ items[2] │
309
- │ output/ │ │ output/ │ │ output/ │
310
- │ results[0] │ │ results[1] │ │ results[2] │
311
- └───────┬─────────┘ └───────┬─────────┘ └───────┬─────────┘
312
- │ │ │
313
- └───────────────────┼───────────────────┘
314
-
315
- [results[0], results[1], results[2]]
316
- ```
317
-
318
- ```python
319
- # Signature (schema accepts Pydantic model or JSON Schema dict)
320
- await swarm.map(
321
- items=list[FileMap] | list[SwarmResult],
322
- prompt=str | Callable[[FileMap, int], str],
323
- name=str, # Operation name for observability (appears in meta.operation_name)
324
- schema=PydanticModel | dict, # Optional
325
- system_prompt=str, # Optional
326
- agent=AgentConfig, # Optional override
327
- best_of=BestOfConfig, # N candidates + judge (mutually exclusive with verify)
328
- verify=VerifyConfig, # LLM-as-judge quality check with retry loop
329
- retry=RetryConfig, # Auto-retry on error with backoff
330
- mcp_servers=dict[str, McpServerConfig], # Optional
331
- skills=list[str], # Optional - e.g. ['anthropics/skills']
332
- integrations=IntegrationsSetup, # managed integrations config
333
- timeout_ms=int, # Optional
334
- ) -> SwarmResultList
335
- ```
336
-
337
- ```python
338
- # Basic
339
- results = await swarm.map(
340
- items=documents,
341
- prompt='Summarize this document',
342
- )
343
- ```
344
-
345
- When `schema` is provided, a structured output prompt is automatically embedded—instructing the agent to write `output/result.json` matching the schema.
346
-
347
- ```python
348
- # With Pydantic schema
349
- class SummarySchema(BaseModel):
350
- title: str
351
- key_points: list[str]
352
-
353
- results = await swarm.map(
354
- items=documents,
355
- prompt='Extract summary',
356
- schema=SummarySchema,
357
- )
358
-
359
- # Or with JSON Schema
360
- summary_json_schema = {
361
- 'type': 'object',
362
- 'properties': {
363
- 'title': {'type': 'string'},
364
- 'key_points': {'type': 'array', 'items': {'type': 'string'}},
365
- },
366
- 'required': ['title', 'key_points'],
367
- }
368
-
369
- results = await swarm.map(
370
- items=documents,
371
- prompt='Extract summary',
372
- schema=summary_json_schema,
373
- )
374
-
375
- # With dynamic prompt
376
- results = await swarm.map(
377
- items=documents,
378
- prompt=lambda files, index: f'Analyze document {index + 1}: focus on revenue',
379
- )
380
-
381
- # Access results
382
- for r in results:
383
- if r.status == 'success':
384
- print(r.data) # Parsed schema instance or FileMap
385
- print(r.files) # Output files from agent
386
- ```
387
-
388
- ### map + best_of
389
-
390
- Combine map parallelism with best_of quality:
391
-
392
- ```python
393
- class AnalysisSchema(BaseModel):
394
- findings: list[str]
395
- confidence: float
396
-
397
- # Each item gets N candidates, judge picks best per item
398
- results = await swarm.map(
399
- items=documents,
400
- prompt='Analyze thoroughly',
401
- schema=AnalysisSchema,
402
- best_of=BestOfConfig(
403
- n=3,
404
- judge_criteria='Most comprehensive analysis',
405
- # task_agents=[...], # Different agent per candidate
406
- # judge_agent=..., # Override judge agent
407
- # mcp_servers={...}, # MCP servers for candidates
408
- # judge_mcp_servers={...}, # MCP servers for judge
409
- # skills=[...], # Skills for candidates
410
- # judge_skills=[...], # Skills for judge
411
- ),
412
- )
413
-
414
- # Results contain only winners (one per input item)
415
- ```
416
-
417
- ### filter
418
-
419
- Two-step evaluation (`schema` and `condition` are required):
420
- 1. `Agent[i]` sees `items[i]`, assesses it, outputs `result.json` matching `schema`
421
- 2. SDK parses `result.json` → `data`, your `condition(data)` applies the threshold
422
- 3. Passing items forward their original input files, not agent output
423
-
424
- ```
425
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
426
- │ Sandbox 0 │ │ Sandbox 1 │ │ Sandbox 2 │
427
- │ Agent 0 │ │ Agent 1 │ │ Agent 2 │
428
- │ │ │ │ │ │
429
- │ context/ │ │ context/ │ │ context/ │
430
- │ items[0] │ │ items[1] │ │ items[2] │
431
- │ output/ │ │ output/ │ │ output/ │
432
- │ result.json │ │ result.json │ │ result.json │
433
- └───────┬─────────┘ └───────┬─────────┘ └───────┬─────────┘
434
- │ │ │
435
- └───────────────────┼───────────────────┘
436
-
437
- condition(data)
438
- ✓ ✗ ✓
439
- │ │
440
- ▼ ▼
441
- [items[0], items[2]]
442
- ```
443
-
444
- ```python
445
- # Signature (schema accepts Pydantic model or JSON Schema dict)
446
- await swarm.filter(
447
- items=list[FileMap] | list[SwarmResult],
448
- prompt=str, # Describe what to assess (agent outputs result.json)
449
- name=str, # Operation name for observability (appears in meta.operation_name)
450
- schema=PydanticModel | dict, # Required - defines evaluation output structure
451
- condition=Callable[[Any], bool], # Local function applies threshold
452
- system_prompt=str, # Optional
453
- agent=AgentConfig, # Optional override
454
- verify=VerifyConfig, # LLM-as-judge quality check with retry loop
455
- retry=RetryConfig, # Auto-retry on error with backoff
456
- mcp_servers=dict[str, McpServerConfig], # Optional
457
- skills=list[str], # Optional - e.g. ['anthropics/skills']
458
- integrations=IntegrationsSetup, # managed integrations config
459
- timeout_ms=int, # Optional
460
- ) -> SwarmResultList
461
- ```
462
-
463
- ```python
464
- class EvalSchema(BaseModel):
465
- severity: Literal['critical', 'warning', 'info']
466
- score: float
467
-
468
- results = await swarm.filter(
469
- items=documents,
470
- prompt='Assess the severity of issues in this document', # Agent evaluates
471
- schema=EvalSchema,
472
- condition=lambda data: data.severity == 'critical', # Code applies threshold
473
- )
474
-
475
- # Three possible statuses:
476
- results.success # Passed condition
477
- results.filtered # Evaluated but didn't pass
478
- results.error # Agent error
479
-
480
- # Chain to next step
481
- await swarm.reduce(
482
- items=results.success,
483
- prompt='Summarize critical issues',
484
- )
485
- ```
486
-
487
- ### reduce
488
-
489
- Synthesize many items into one. A single agent sees all `items` as `item_0/`, `item_1/`, etc. and outputs a unified `result` (which includes `result.json` if `schema` provided).
490
-
491
- ```
492
- ┌─────────────────────────┐
493
- │ Sandbox │
494
- │ Agent │
495
- │ │
496
- │ context/ │
497
- │ item_0/items[0] │
498
- │ item_1/items[1] │
499
- │ item_2/items[2] │
500
- │ output/ │
501
- │ result │
502
- └────────────┬────────────┘
503
-
504
-
505
- result
506
- ```
507
-
508
- ```python
509
- # Signature (schema accepts Pydantic model or JSON Schema dict)
510
- await swarm.reduce(
511
- items=list[FileMap] | list[SwarmResult],
512
- prompt=str,
513
- name=str, # Operation name for observability (appears in meta.operation_name)
514
- schema=PydanticModel | dict, # Optional
515
- system_prompt=str, # Optional
516
- agent=AgentConfig, # Optional override
517
- verify=VerifyConfig, # LLM-as-judge quality check with retry loop
518
- retry=RetryConfig, # Auto-retry on error with backoff
519
- mcp_servers=dict[str, McpServerConfig], # Optional
520
- skills=list[str], # Optional - e.g. ['anthropics/skills']
521
- integrations=IntegrationsSetup, # managed integrations config
522
- timeout_ms=int, # Optional
523
- ) -> ReduceResult
524
- ```
525
-
526
- ```python
527
- # Agent sees: item_0/, item_1/, item_2/, etc.
528
- report = await swarm.reduce(
529
- items=results.success,
530
- prompt='Create a unified report from all analyses',
531
- )
532
-
533
- if report.status == 'success':
534
- print(report.files) # Final output files
535
- print(report.data) # Parsed schema if provided
536
-
537
- # With schema
538
- class ReportSchema(BaseModel):
539
- summary: str
540
- recommendations: list[str]
541
-
542
- report = await swarm.reduce(
543
- items=items,
544
- prompt='Create report',
545
- schema=ReportSchema,
546
- )
547
- ```
548
-
549
- ### verify (quality gate)
550
-
551
- Add a verification loop to any operation (`map`, `filter`, `reduce`). A separate verifier agent checks if the worker's output meets your criteria. On failure, the worker retries with the verifier's feedback — up to `max_attempts` times.
552
-
553
- ```
554
- ┌───────────────┐ ┌──────────────┐
555
- │ Worker │─────▶│ Verifier │
556
- │ (attempt 1) │ │ │
557
- └───────────────┘ └──────┬───────┘
558
-
559
- pass? │ fail + feedback
560
- │ │ │
561
- ▼ │ ▼
562
- output │ ┌───────────────┐
563
- └─▶│ Worker │──▶ Verifier ──▶ ...
564
- │ (attempt 2) │ (up to max_attempts)
565
- └───────────────┘
566
- ```
567
-
568
- ```python
569
- results = await swarm.map(
570
- items=documents,
571
- prompt='Write a detailed analysis',
572
- schema=AnalysisSchema,
573
- verify=VerifyConfig(
574
- criteria='Analysis must include specific data points and cite sources',
575
- max_attempts=3, # Default: 3
576
- # verifier_agent=AgentConfig(type='claude', model='opus'), # Override verifier
577
- # verifier_skills=['anthropics/skills'], # Skills for verifier
578
- on_worker_complete=lambda idx, attempt, status: print(f'Item {idx}, attempt {attempt}: {status}'),
579
- on_verifier_complete=lambda idx, attempt, passed, feedback: print(f'Verify item {idx}: {"PASS" if passed else feedback}'),
580
- ),
581
- )
582
-
583
- # Check verification outcome
584
- for r in results:
585
- if r.verify:
586
- print(f'Passed: {r.verify.passed}, attempts: {r.verify.attempts}')
587
- print(f'Reasoning: {r.verify.reasoning}')
588
- ```
589
-
590
- On each failed attempt, the verifier's feedback is appended to the worker's prompt so it can address specific issues. The final result includes `verify: VerifyInfo` with the outcome.
591
-
592
- > **Note:** `verify` and `best_of` are mutually exclusive on `map`. Use one or the other per operation.
593
-
594
- ---
595
-
596
- ## Result Types
597
-
598
- ```python
599
- @dataclass
600
- class SwarmResult:
601
- """Result from map, filter, best_of candidates."""
602
- status: Literal['success', 'filtered', 'error']
603
- data: Any | None # Parsed schema, or None on error
604
- files: FileMap # Output files (map/best_of) or input files (filter)
605
- meta: IndexedMeta # operation_id, operation, tag, sandbox_id, item_index
606
- error: str | None # Error message if status == 'error'
607
- raw_data: str | None # Raw result.json when parse/validation failed
608
- best_of: BestOfInfo | None # Present when map used best_of option
609
- verify: VerifyInfo | None # Present when verify option was used
610
-
611
- # SwarmResultList - from map, filter (extends list)
612
- results.success # list[SwarmResult] with status 'success'
613
- results.filtered # list[SwarmResult] with status 'filtered'
614
- results.error # list[SwarmResult] with status 'error'
615
-
616
- @dataclass
617
- class ReduceResult:
618
- """Result from reduce."""
619
- status: Literal['success', 'error']
620
- data: Any | None
621
- files: FileMap
622
- meta: ReduceMeta # operation_id, operation, tag, sandbox_id, input_count, input_indices
623
- error: str | None
624
- raw_data: str | None
625
- verify: VerifyInfo | None
626
-
627
- @dataclass
628
- class VerifyInfo:
629
- """Verification outcome."""
630
- passed: bool # Final verification status
631
- reasoning: str # Verifier's reasoning
632
- verify_meta: VerifyMeta # operation_id, operation, tag, sandbox_id, attempts
633
- attempts: int # Total attempts made
634
-
635
- @dataclass
636
- class BestOfInfo:
637
- """Present when map used best_of option."""
638
- winner_index: int
639
- judge_reasoning: str
640
- judge_meta: JudgeMeta # operation_id, operation, tag, sandbox_id, candidate_count
641
- candidates: list[SwarmResult]
642
-
643
- @dataclass
644
- class BestOfResult:
645
- """Result from best_of."""
646
- winner: SwarmResult
647
- winner_index: int
648
- judge_reasoning: str
649
- judge_meta: JudgeMeta # operation_id, operation, tag, sandbox_id, candidate_count
650
- candidates: list[SwarmResult]
651
- ```
652
-
653
- ## Chaining Operations
654
-
655
- When chaining Swarm operations, `result.json` from a previous step is automatically renamed to `data.json`. This avoids confusion when the downstream agent writes its own `result.json`. This also applies to [Pipeline](#pipeline).
656
-
657
- **Example: map → reduce chain**
658
-
659
- ```
660
- ┌─────────────────────────────────────────────────────────────────────────────┐
661
- │ MAP (parallel) │
662
- │ │
663
- │ item_0 agent writes: item_1 agent writes: │
664
- │ output/ output/ │
665
- │ result.json ← schema result.json ← schema │
666
- │ │
667
- └─────────────────────────────────────────────────────────────────────────────┘
668
-
669
-
670
- ┌─────────────────────────────────────────────────────────────────────────────┐
671
- │ REDUCE (single agent) │
672
- │ │
673
- │ context/ │
674
- │ item_0/ │
675
- │ data.json ← renamed from result.json │
676
- │ item_1/ │
677
- │ data.json ← renamed from result.json │
678
- │ output/ │
679
- │ result.json ← reduce agent writes its own │
680
- │ │
681
- └─────────────────────────────────────────────────────────────────────────────┘
682
- ```
683
-
684
- ```python
685
- class AnalysisSchema(BaseModel):
686
- summary: str
687
-
688
- class SeveritySchema(BaseModel):
689
- severity: Literal['critical', 'warning', 'info']
690
-
691
- # Full pipeline: map → filter → reduce
692
- analyzed = await swarm.map(
693
- items=documents,
694
- prompt='Analyze',
695
- schema=AnalysisSchema,
696
- )
697
-
698
- critical = await swarm.filter(
699
- items=analyzed.success,
700
- prompt='Evaluate severity',
701
- schema=SeveritySchema,
702
- condition=lambda d: d.severity == 'critical',
703
- )
704
-
705
- report = await swarm.reduce(
706
- items=critical.success,
707
- prompt='Create summary report',
708
- )
709
-
710
- # Combine success and filtered
711
- all_evaluated = [*critical.success, *critical.filtered]
712
- await swarm.reduce(
713
- items=all_evaluated,
714
- prompt='Summarize all evaluated items',
715
- )
716
- ```
717
-
718
- ## AgentOverride
719
-
720
- Override the default agent for any operation. There is no separate override type in Python — you pass the same `AgentConfig` the Swarm itself takes, and `api_key` is inherited from the Swarm config when you leave it out. Only the fields worth overriding per operation are shown:
721
-
722
- ```python
723
- @dataclass
724
- class AgentConfig:
725
- type: Literal['claude', 'codex', 'gemini', 'qwen', 'kimi', 'opencode', 'droid']
726
- api_key: str | None = None
727
- model: str | None = None
728
- reasoning_effort: Literal['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'thinking', 'no-thinking'] | None = None
729
- ```
730
-
731
- ```python
732
- codex_agent = AgentConfig(
733
- type='codex',
734
- reasoning_effort='high',
735
- )
736
-
737
- results = await swarm.map(
738
- items=items,
739
- prompt='Analyze',
740
- agent=codex_agent,
741
- )
742
- ```
743
-
744
- ## Concurrency
745
-
746
- Global semaphore limits parallel sandboxes across all operations.
747
-
748
- ```python
749
- swarm = Swarm(SwarmConfig(
750
- agent=agent,
751
- concurrency=4, # Max 4 sandboxes at once (default: 4)
752
- ))
753
-
754
- # map(10) with best_of(5) = 60 agent calls, but only 4 run at any time
755
- ```
756
-
757
- **Ordering guarantees:**
758
- - `best_of`: Judge runs only after all candidates complete
759
- - `map` → `filter` → `reduce`: Each phase completes before next starts
760
- - Within a phase: Items run in parallel (up to concurrency limit)
761
-
762
- ---
763
-
764
- ## Pipeline
765
-
766
- Fluent wrapper over Swarm for chaining operations. **All Swarm features work in Pipeline steps** — `schema`, `best_of`, `verify`, `retry`, `agent`, `mcp_servers`, `skills`, `integrations`, dynamic prompts.
767
-
768
- ```python
769
- from dotenv import load_dotenv
770
- load_dotenv()
771
-
772
- from evolve import Swarm, Pipeline
773
-
774
- swarm = Swarm() # See Swarm Abstractions for full config
775
-
776
- pipeline = (
777
- Pipeline(swarm)
778
- .map(MapConfig(
779
- name='analyze',
780
- prompt='Analyze...',
781
- schema=AnalysisSchema,
782
- ))
783
- .filter(FilterConfig(
784
- name='critical',
785
- prompt='Rate...',
786
- schema=SeveritySchema,
787
- condition=lambda d: d.severity == 'critical',
788
- ))
789
- .reduce(ReduceConfig(
790
- name='report',
791
- prompt='Summarize...',
792
- ))
793
- )
794
-
795
- # Reusable — run with different data
796
- result1 = await pipeline.run(batch1)
797
- result2 = await pipeline.run(batch2)
798
- ```
799
-
800
- ### Step Configurations
801
-
802
- Each step accepts the same options as the corresponding Swarm method, plus `name` for observability:
803
-
804
- ```python
805
- # Map step — same as swarm.map() + name
806
- MapConfig(
807
- name=str, # Step name (appears in events)
808
- prompt=str | Callable[[FileMap, int], str],
809
- schema=PydanticModel | dict, # Optional
810
- best_of=BestOfConfig, # N candidates + judge
811
- verify=VerifyConfig, # LLM-as-judge quality check
812
- retry=RetryConfig, # Auto-retry on error
813
- agent=AgentConfig,
814
- mcp_servers=dict[str, McpServerConfig],
815
- skills=list[str], # Skills for workers
816
- integrations=IntegrationsSetup, # managed integrations config
817
- system_prompt=str,
818
- timeout_ms=int,
819
- )
820
-
821
- # Filter step — same as swarm.filter() + name + emit
822
- FilterConfig(
823
- name=str,
824
- prompt=str,
825
- schema=PydanticModel | dict, # Required
826
- condition=Callable[[Any], bool], # Required
827
- emit='success' | 'filtered' | 'all', # What passes to next step: 'success' (default), 'filtered', or 'all'
828
- verify=VerifyConfig,
829
- retry=RetryConfig,
830
- agent=AgentConfig,
831
- mcp_servers=dict[str, McpServerConfig],
832
- skills=list[str], # Skills for workers
833
- integrations=IntegrationsSetup, # managed integrations config
834
- system_prompt=str,
835
- timeout_ms=int,
836
- )
837
-
838
- # Reduce step — same as swarm.reduce() + name (terminal: no steps after)
839
- ReduceConfig(
840
- name=str,
841
- prompt=str,
842
- schema=PydanticModel | dict, # Optional
843
- verify=VerifyConfig,
844
- retry=RetryConfig,
845
- agent=AgentConfig,
846
- mcp_servers=dict[str, McpServerConfig],
847
- skills=list[str], # Skills for workers
848
- integrations=IntegrationsSetup, # managed integrations config
849
- system_prompt=str,
850
- timeout_ms=int,
851
- )
852
- ```
853
-
854
- ### Full Example
855
-
856
- ```python
857
- pipeline = (
858
- Pipeline(swarm)
859
-
860
- .map(MapConfig(
861
- name='analyze',
862
- prompt=lambda files, idx: f'Analyze document {idx + 1}',
863
- schema=AnalysisSchema,
864
- best_of=BestOfConfig(
865
- n=3,
866
- judge_criteria='Most thorough analysis',
867
- ),
868
- retry=RetryConfig(max_attempts=2),
869
- agent=AgentConfig(type='claude', model='opus'),
870
- ))
871
-
872
- .filter(FilterConfig(
873
- name='quality-gate',
874
- prompt='Rate the analysis quality',
875
- schema=QualitySchema, # Has score: float, reasoning: str
876
- condition=lambda d: d.score >= 8,
877
- emit='success', # Only high-quality pass through
878
- verify=VerifyConfig(
879
- criteria='Rating must be justified with specific examples',
880
- ),
881
- ))
882
-
883
- .reduce(ReduceConfig(
884
- name='synthesize',
885
- prompt='Create executive summary from all analyses',
886
- schema=ReportSchema,
887
- verify=VerifyConfig(
888
- criteria='Summary must cover all key findings',
889
- ),
890
- ))
891
-
892
- .on('step_complete', lambda e: print(f'{e.name}: {e.success_count}/{e.success_count + e.error_count}'))
893
- )
894
-
895
- result = await pipeline.run(documents)
896
- ```
897
-
898
- ### Events
899
-
900
- Pipeline unifies all Swarm callbacks at the pipeline level, adding `step_index` and `step_name`:
901
-
902
- ```python
903
- (
904
- pipeline
905
- .on('step_start', lambda e: print(f'Step {e.index} started with {e.item_count} items'))
906
- .on('step_complete', lambda e: print(f'Step {e.index} done in {e.duration_ms}ms'))
907
- .on('step_error', lambda e: print(f'Step {e.index} failed: {e.error}'))
908
- )
909
-
910
- # Or object style
911
- pipeline.on(PipelineEvents(
912
- on_step_complete=lambda e: print(f'{e.name}: {e.success_count} success'),
913
- on_item_retry=lambda e: print(f'Retry: step {e.step_index}, item {e.item_index}'),
914
- on_verifier_complete=lambda e: print(f"Verify: {'PASS' if e.passed else e.feedback}"),
915
- ))
916
- ```
917
-
918
- | Event | Fields |
919
- |-------|--------|
920
- | `step_start` | `type`, `index`, `name?`, `item_count` |
921
- | `step_complete` | `type`, `index`, `name?`, `duration_ms`, `success_count`, `error_count`, `filtered_count` |
922
- | `step_error` | `type`, `index`, `name?`, `error` |
923
- | `item_retry` | `step_index`, `step_name?`, `item_index`, `attempt`, `error` |
924
- | `worker_complete` | `step_index`, `step_name?`, `item_index`, `attempt`, `status` |
925
- | `verifier_complete` | `step_index`, `step_name?`, `item_index`, `attempt`, `passed`, `feedback?` |
926
- | `candidate_complete` | `step_index`, `step_name?`, `item_index`, `candidate_index`, `status` |
927
- | `judge_complete` | `step_index`, `step_name?`, `item_index`, `winner_index`, `reasoning` |
928
-
929
- ### Result
930
-
931
- ```python
932
- @dataclass
933
- class PipelineResult:
934
- pipeline_run_id: str
935
- steps: list[StepResult] # type, index, duration_ms, results
936
- output: list[SwarmResult] | ReduceResult
937
- total_duration_ms: int
938
-
939
- # Access step results
940
- for step in result.steps:
941
- print(f'{step.type} took {step.duration_ms}ms')
942
- ```
943
-
944
- ### Terminal Pipeline
945
-
946
- After `.reduce()`, no more steps can be added (returns `TerminalPipeline`):
947
-
948
- ```python
949
- terminal = pipeline.reduce(ReduceConfig(prompt='...'))
950
- terminal.map(MapConfig(prompt='...')) # Raises: "Cannot add steps after reduce"
951
- ```
952
-
953
- ---