agentensemble 0.1.0__tar.gz → 0.3.0__tar.gz

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. agentensemble-0.3.0/PKG-INFO +649 -0
  2. agentensemble-0.3.0/README.md +596 -0
  3. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/__init__.py +36 -1
  4. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/agents/__init__.py +3 -0
  5. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/agents/base.py +31 -19
  6. agentensemble-0.3.0/agentensemble/agents/hybrid_agent.py +265 -0
  7. agentensemble-0.3.0/agentensemble/agents/react_agent.py +412 -0
  8. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/agents/structured_agent.py +9 -1
  9. agentensemble-0.3.0/agentensemble/core/__init__.py +9 -0
  10. agentensemble-0.3.0/agentensemble/core/protocol.py +36 -0
  11. agentensemble-0.3.0/agentensemble/graph/__init__.py +9 -0
  12. agentensemble-0.3.0/agentensemble/graph/workflow.py +91 -0
  13. agentensemble-0.3.0/agentensemble/llm/__init__.py +34 -0
  14. agentensemble-0.3.0/agentensemble/llm/interface.py +142 -0
  15. agentensemble-0.3.0/agentensemble/llm/mistral_provider.py +229 -0
  16. agentensemble-0.3.0/agentensemble/memory/__init__.py +15 -0
  17. agentensemble-0.3.0/agentensemble/memory/session.py +54 -0
  18. agentensemble-0.3.0/agentensemble/memory/sqlite_session.py +104 -0
  19. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/orchestration/__init__.py +4 -4
  20. agentensemble-0.3.0/agentensemble/orchestration/debate.py +102 -0
  21. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/orchestration/ensemble.py +73 -40
  22. agentensemble-0.3.0/agentensemble/planner/__init__.py +9 -0
  23. agentensemble-0.3.0/agentensemble/planner/planner_agent.py +78 -0
  24. agentensemble-0.3.0/agentensemble/router/__init__.py +10 -0
  25. agentensemble-0.3.0/agentensemble/router/router_agent.py +175 -0
  26. agentensemble-0.3.0/agentensemble/runner.py +131 -0
  27. agentensemble-0.3.0/agentensemble/testing/comparison.py +117 -0
  28. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/tools/__init__.py +7 -0
  29. agentensemble-0.3.0/agentensemble/tools/adapters.py +118 -0
  30. agentensemble-0.3.0/agentensemble/tools/function_tool.py +82 -0
  31. agentensemble-0.3.0/agentensemble/tools/protocol.py +115 -0
  32. agentensemble-0.3.0/agentensemble/tools/validator.py +120 -0
  33. agentensemble-0.3.0/agentensemble/tracing/__init__.py +10 -0
  34. agentensemble-0.3.0/agentensemble/tracing/hooks.py +121 -0
  35. agentensemble-0.3.0/agentensemble.egg-info/PKG-INFO +649 -0
  36. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble.egg-info/SOURCES.txt +30 -1
  37. {agentensemble-0.1.0 → agentensemble-0.3.0}/pyproject.toml +2 -2
  38. {agentensemble-0.1.0 → agentensemble-0.3.0}/setup.py +1 -4
  39. agentensemble-0.3.0/tests/test_agents.py +115 -0
  40. agentensemble-0.3.0/tests/test_comparison.py +107 -0
  41. agentensemble-0.3.0/tests/test_memory.py +65 -0
  42. agentensemble-0.3.0/tests/test_orchestration.py +99 -0
  43. agentensemble-0.3.0/tests/test_react_session.py +34 -0
  44. agentensemble-0.3.0/tests/test_runner.py +103 -0
  45. agentensemble-0.3.0/tests/test_tools.py +81 -0
  46. agentensemble-0.3.0/tests/test_tracing.py +49 -0
  47. agentensemble-0.1.0/PKG-INFO +0 -407
  48. agentensemble-0.1.0/README.md +0 -354
  49. agentensemble-0.1.0/agentensemble/agents/hybrid_agent.py +0 -147
  50. agentensemble-0.1.0/agentensemble/agents/react_agent.py +0 -118
  51. agentensemble-0.1.0/agentensemble/testing/comparison.py +0 -82
  52. agentensemble-0.1.0/agentensemble/tools/validator.py +0 -51
  53. agentensemble-0.1.0/agentensemble.egg-info/PKG-INFO +0 -407
  54. {agentensemble-0.1.0 → agentensemble-0.3.0}/MANIFEST.in +0 -0
  55. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/agents/rag_agent.py +0 -0
  56. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/agents/stategraph_agent.py +0 -0
  57. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/orchestration/pipeline.py +0 -0
  58. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/orchestration/supervisor.py +0 -0
  59. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/orchestration/swarm.py +0 -0
  60. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/py.typed +0 -0
  61. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/testing/__init__.py +0 -0
  62. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/testing/benchmark.py +0 -0
  63. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/testing/metrics.py +0 -0
  64. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/tools/rag.py +0 -0
  65. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/tools/registry.py +0 -0
  66. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/tools/scraper.py +0 -0
  67. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/tools/search.py +0 -0
  68. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/utils/__init__.py +0 -0
  69. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble/utils/llm.py +0 -0
  70. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble.egg-info/dependency_links.txt +0 -0
  71. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble.egg-info/requires.txt +0 -0
  72. {agentensemble-0.1.0 → agentensemble-0.3.0}/agentensemble.egg-info/top_level.txt +0 -0
  73. {agentensemble-0.1.0 → agentensemble-0.3.0}/setup.cfg +0 -0
@@ -0,0 +1,649 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentensemble
3
+ Version: 0.3.0
4
+ Summary: Orchestrate AI agents in perfect harmony - A comprehensive Python framework for building, testing, and orchestrating multi-agent AI systems
5
+ Author-email: Irfan Ali <irfanali29@hotmail.com>
6
+ Maintainer-email: Irfan Ali <irfanali29@hotmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/irfanalidv/AgentEnsemble
9
+ Project-URL: Documentation, https://github.com/irfanalidv/AgentEnsemble#readme
10
+ Project-URL: Repository, https://github.com/irfanalidv/AgentEnsemble
11
+ Project-URL: Issues, https://github.com/irfanalidv/AgentEnsemble/issues
12
+ Keywords: ai-agents,multi-agent,orchestration,langgraph,langchain,rag,agent-testing,agentic-ai,state-graph,react-agent
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ Requires-Dist: langchain>=0.1.0
25
+ Requires-Dist: langchain-core>=0.1.0
26
+ Requires-Dist: langchain-community>=0.0.20
27
+ Requires-Dist: langchain-mistralai>=0.1.0
28
+ Requires-Dist: langgraph>=0.0.20
29
+ Requires-Dist: pydantic>=2.0.0
30
+ Requires-Dist: typing-extensions>=4.5.0
31
+ Requires-Dist: python-dotenv>=1.0.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
35
+ Requires-Dist: black>=23.0.0; extra == "dev"
36
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
37
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
38
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
39
+ Provides-Extra: observability
40
+ Requires-Dist: langsmith>=0.1.0; extra == "observability"
41
+ Requires-Dist: opentelemetry-api>=1.20.0; extra == "observability"
42
+ Provides-Extra: rag
43
+ Requires-Dist: chromadb>=0.4.0; extra == "rag"
44
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "rag"
45
+ Requires-Dist: langchain-text-splitters>=0.0.1; extra == "rag"
46
+ Requires-Dist: beautifulsoup4>=4.12.0; extra == "rag"
47
+ Requires-Dist: langchain-mistralai>=0.1.0; extra == "rag"
48
+ Provides-Extra: search
49
+ Requires-Dist: duckduckgo-search>=4.0.0; extra == "search"
50
+ Requires-Dist: google-search-results>=2.4.2; extra == "search"
51
+ Provides-Extra: all
52
+ Requires-Dist: agentensemble[dev,observability,rag,search]; extra == "all"
53
+
54
+ # AgentEnsemble
55
+
56
+ **Production-ready multi-agent orchestration for Python**
57
+
58
+ AgentEnsemble is a modern framework for building and orchestrating AI agent systems. Designed for clarity, extensibility, and production use—comparable to LangGraph, CrewAI, and AutoGen.
59
+
60
+ [![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
61
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
62
+ [![PyPI version](https://img.shields.io/pypi/v/agentensemble)](https://pypi.org/project/agentensemble/)
63
+ [![PyPI Downloads](https://static.pepy.tech/badge/agentensemble)](https://pepy.tech/projects/agentensemble)
64
+
65
+ ---
66
+
67
+ ## Design Philosophy
68
+
69
+ - **Modular** — Agents, tools, orchestration, and memory are decoupled
70
+ - **Async-first** — `run()` and `arun()` on all agents; parallel execution via `asyncio.gather`
71
+ - **Provider-agnostic** — LLM interface supports Mistral (OpenAI/Anthropic-ready)
72
+ - **Production patterns** — Runner, retries, hooks, tracing, session memory
73
+
74
+ ---
75
+
76
+ ## Architecture
77
+
78
+ <p align="center">
79
+ <img src="docs/agentensemble-architecture.png" alt="AgentEnsemble Architecture" width="900"/>
80
+ </p>
81
+
82
+ ### Framework layers
83
+
84
+ ```
85
+ ┌──────────────────────────────────────────────────────────────────────────────────────────┐
86
+ │ AgentEnsemble — Layered Architecture │
87
+ ├─────────────────┬─────────────────────┬─────────────────────┬────────────────────────────┤
88
+ │ AGENTS │ ORCHESTRATION │ TOOLS │ MEMORY & RUNNER │
89
+ ├─────────────────┼─────────────────────┼─────────────────────┼────────────────────────────┤
90
+ │ ReActAgent │ Supervisor │ SearchTool │ Session (InMemory, SQLite) │
91
+ │ StateGraphAgent │ Swarm (parallel) │ RAGTool │ RunConfig, RunHooks │
92
+ │ RAGAgent │ Pipeline (seq) │ @function_tool │ TraceHooks (usage, cost) │
93
+ │ HybridAgent │ Debate (consensus) │ ValidationTool │ interrupt_before_tools │
94
+ │ RouterAgent │ Ensemble + Router │ ToolRegistry │ retries, session memory │
95
+ │ PlannerAgent │ WorkflowGraph │ │ │
96
+ └─────────────────┴─────────────────────┴─────────────────────┴────────────────────────────┘
97
+ │ │ │ │
98
+ └────────────────────┴────────────────────┴─────────────────────────┘
99
+ │
100
+ ┌───────────▼───────────┐
101
+ │ Use Case Spectrum │
102
+ └───────────────────────┘
103
+ ```
104
+
105
+ ### Use case spectrum
106
+
107
+ | Layer | Use cases |
108
+ |-------|-----------|
109
+ | **Research & fact-checking** | ReActAgent + SearchTool; Swarm for parallel verification |
110
+ | **Document Q&A** | RAGAgent + RAGTool; index URLs, ask questions |
111
+ | **Multi-agent coordination** | Supervisor, Swarm, Pipeline; RouterAgent for LLM-based selection |
112
+ | **Debate & consensus** | DebateOrchestrator; solvers propose → aggregator votes |
113
+ | **Custom workflows** | WorkflowGraph (DAG), StateGraphAgent (nodes), PlannerAgent (decompose) |
114
+ | **Human-in-loop** | `interrupt_before_tools` — pause before sensitive tools, resume with approval |
115
+ | **Production pipelines** | Runner + RunConfig, retries, TraceHooks (usage/cost), Session memory |
116
+
117
+ ---
118
+
119
+ ## Quick Start
120
+
121
+ ```bash
122
+ pip install agentensemble
123
+ # Optional: pip install agentensemble[search,rag]
124
+ ```
125
+
126
+ ```python
127
+ from agentensemble import ReActAgent, SearchTool, Runner
128
+
129
+ agent = ReActAgent(name="research", tools=[SearchTool()], max_iterations=5)
130
+ result = Runner.run(agent, "What are the latest AI breakthroughs in 2024?")
131
+ print(result["result"])
132
+ ```
133
+
134
+ **Setup:** Copy `.env.example` to `.env` and add your keys. Use `KEY=value` format (no spaces around `=`).
135
+
136
+ ---
137
+
138
+ ## Examples — Code + Output
139
+
140
+ Each example is runnable. Requires `.env` with `MISTRAL_API_KEY` and `SERPER_API_KEY` (see `.env.example` for format).
141
+
142
+ ---
143
+
144
+ ### 1. ReActAgent — LLM decides when to search
145
+
146
+ ```python
147
+ from agentensemble import ReActAgent, SearchTool
148
+
149
+ agent = ReActAgent(name="research", tools=[SearchTool()], max_iterations=5)
150
+ result = agent.run("Who won the 2024 Nobel Prize in Physics?")
151
+ print(result["result"])
152
+ ```
153
+
154
+ **Output:**
155
+
156
+ ```
157
+ The 2024 Nobel Prize in Physics was awarded jointly to John J. Hopfield and
158
+ Geoffrey E. Hinton for their foundational discoveries and inventions that
159
+ enable machine learning with artificial neural networks.
160
+ ```
161
+
162
+ ---
163
+
164
+ ### 2. Custom @function_tool — Add your own tools
165
+
166
+ ```python
167
+ from agentensemble import ReActAgent, SearchTool, function_tool
168
+
169
+ @function_tool(description="Get current weather for a city")
170
+ def get_weather(city: str) -> str:
171
+ return f"Weather in {city}: 72°F, partly cloudy"
172
+
173
+ agent = ReActAgent(tools=[SearchTool(), get_weather], max_iterations=4)
174
+ result = agent.run("What is the weather in Paris?")
175
+ print(result["result"])
176
+ ```
177
+
178
+ **Output:**
179
+
180
+ ```
181
+ The current weather in Paris is 72°F (22°C) with partly cloudy conditions.
182
+ ```
183
+
184
+ ---
185
+
186
+ ### 3. HybridAgent — LLM routes SEARCH → RAG → VALIDATE → ANSWER
187
+
188
+ ```python
189
+ from agentensemble import HybridAgent, SearchTool
190
+
191
+ agent = HybridAgent(tools=[SearchTool()], max_iterations=5)
192
+ result = agent.run("What are the key trends in AI agents in 2024?")
193
+ print(result["result"][:300], "...")
194
+ print("Actions:", result["metadata"]["actions_taken"])
195
+ ```
196
+
197
+ **Output:**
198
+
199
+ ```
200
+ 1. Multi-Modal AI Models Redefined Possibilities · 2. AI Agents Stepping Into
201
+ the Spotlight · 3. AI's Bold Move into Video Creation · 4. Rise of Open-Source AI...
202
+ Actions: ['SEARCH', 'SEARCH', 'RAG', 'VALIDATE', 'ANSWER']
203
+ ```
204
+
205
+ ---
206
+
207
+ ### 4. Swarm — Parallel agents (multiple perspectives)
208
+
209
+ ```python
210
+ from agentensemble import ReActAgent, SwarmOrchestrator, SearchTool
211
+
212
+ researcher = ReActAgent(name="researcher", tools=[SearchTool()], max_iterations=2)
213
+ fact_checker = ReActAgent(name="fact_checker", tools=[SearchTool()], max_iterations=2)
214
+
215
+ swarm = SwarmOrchestrator(agents={"researcher": researcher, "fact_checker": fact_checker})
216
+ result = swarm.perform("What is the capital of France?")
217
+ for name, r in result["results"].items():
218
+ print(f"{name}: {r['result'][:80]}...")
219
+ ```
220
+
221
+ **Output:**
222
+
223
+ ```
224
+ researcher: The capital of France is Paris.
225
+ fact_checker: The capital of France is Paris.
226
+ ```
227
+
228
+ ---
229
+
230
+ ### 5. Pipeline — Sequential (search → summarize)
231
+
232
+ ```python
233
+ from agentensemble import ReActAgent, PipelineOrchestrator, SearchTool
234
+
235
+ searcher = ReActAgent(name="searcher", tools=[SearchTool()], max_iterations=2)
236
+ summarizer = ReActAgent(name="summarizer", tools=[], max_iterations=1)
237
+
238
+ pipeline = PipelineOrchestrator(agents={"searcher": searcher, "summarizer": summarizer})
239
+ result = pipeline.perform("Summarize the top 3 AI breakthroughs in 2024")
240
+ print(result["final_result"]["previous_result"][:250], "...")
241
+ ```
242
+
243
+ **Output:**
244
+
245
+ ```
246
+ 1. Multimodal AI Advancements (GPT-4o, Gemini 1.5) · 2. AI Agents Stepping Into
247
+ the Spotlight · 3. AI's Bold Move into Video Creation...
248
+ ```
249
+
250
+ ---
251
+
252
+ ### 6. Debate — Multi-agent consensus for reasoning
253
+
254
+ ```python
255
+ from agentensemble import ReActAgent, DebateOrchestrator
256
+
257
+ solver1 = ReActAgent(name="solver_1", tools=[], max_iterations=1)
258
+ solver2 = ReActAgent(name="solver_2", tools=[], max_iterations=1)
259
+ aggregator = ReActAgent(name="aggregator", tools=[], max_iterations=1)
260
+
261
+ debate = DebateOrchestrator(solvers=[solver1, solver2], aggregator=aggregator, rounds=2)
262
+ result = debate.debate("What is 15 * 7?")
263
+ print("Proposals:", result["proposals"])
264
+ print("Final:", result["result"])
265
+ ```
266
+
267
+ **Output:**
268
+
269
+ ```
270
+ Proposals: ['15 × 7 = 105', '15 × 7 equals 105']
271
+ Final: 15 × 7 equals 105.
272
+ ```
273
+
274
+ ---
275
+
276
+ ### 7. Router + Ensemble — LLM picks the best agent
277
+
278
+ ```python
279
+ from agentensemble import ReActAgent, RouterAgent, Ensemble, SearchTool
280
+
281
+ researcher = ReActAgent(name="researcher", tools=[SearchTool()], max_iterations=2)
282
+ validator = ReActAgent(name="validator", tools=[SearchTool()], max_iterations=2)
283
+
284
+ router = RouterAgent(agents={"researcher": researcher, "validator": validator})
285
+ ensemble = Ensemble(conductor="supervisor", agents={"researcher": researcher, "validator": validator}, router=router)
286
+ result = ensemble.perform("Latest developments in quantum computing")
287
+ for name, r in result["results"].items():
288
+ print(f"Routed to {name}: {r['result'][:120]}...")
289
+ ```
290
+
291
+ **Output:**
292
+
293
+ ```
294
+ Routed to researcher: Here are the latest developments in quantum computing:
295
+ 1. Hardware Advances · 2. Fault-tolerant quantum computing...
296
+ ```
297
+
298
+ ---
299
+
300
+ ### 8. Planner — Decompose task → executor runs subtasks
301
+
302
+ ```python
303
+ from agentensemble import PlannerAgent, StateGraphAgent
304
+
305
+ def worker(state):
306
+ return {"result": f"Processed: {state.query}"}
307
+
308
+ executor = StateGraphAgent(name="worker", nodes={"start": worker})
309
+ executor._route = lambda s, c: "end"
310
+
311
+ planner = PlannerAgent(executor=executor, max_subtasks=3)
312
+ result = planner.run("Research and compare LangGraph vs CrewAI")
313
+ print("Subtasks:", result["metadata"]["subtasks"])
314
+ print("Result:", result["result"][:200], "...")
315
+ ```
316
+
317
+ **Output:**
318
+
319
+ ```
320
+ Subtasks: ['Search for information about: Research and compare LangGraph vs CrewAI',
321
+ 'Summarize key findings from: Research and compare LangGraph vs CrewAI']
322
+ Result: Processed: Search for information... --- Processed: Summarize key findings...
323
+ ```
324
+
325
+ ---
326
+
327
+ ### 9. WorkflowGraph — Custom DAG with nodes and edges
328
+
329
+ ```python
330
+ from agentensemble import WorkflowGraph
331
+
332
+ def analyze(s):
333
+ return {"context": {**s.get("context", {}), "done": True}, "current_node": "summarize"}
334
+
335
+ def summarize(s):
336
+ return {"result": f"Summary for: {s['query']}", "current_node": "end"}
337
+
338
+ graph = (
339
+ WorkflowGraph(entry="start", exit_nodes=["end"])
340
+ .add_node("start", lambda s: {"current_node": "analyze"})
341
+ .add_node("analyze", analyze)
342
+ .add_node("summarize", summarize)
343
+ .add_edge("start", "analyze")
344
+ .add_edge("analyze", "summarize")
345
+ .add_edge("summarize", "end")
346
+ )
347
+ result = graph.run("What is multi-agent AI?")
348
+ print(result["result"])
349
+ ```
350
+
351
+ **Output:**
352
+
353
+ ```
354
+ Summary for: What is multi-agent AI?
355
+ ```
356
+
357
+ ---
358
+
359
+ ### 10. Runner + RunHooks + InMemorySession — Production-ready execution
360
+
361
+ ```python
362
+ from agentensemble import ReActAgent, Runner, RunConfig, RunHooks, InMemorySession, SearchTool
363
+
364
+ session = InMemorySession(session_id="chat-1")
365
+ agent = ReActAgent(name="chatbot", tools=[SearchTool()], session=session, max_iterations=3)
366
+
367
+ def on_start(q, _): print(f"→ User: {q[:50]}...")
368
+ def on_end(r): print(f"← Agent: {r['result'][:80]}...\n")
369
+
370
+ config = RunConfig(hooks=RunHooks(on_start=on_start, on_end=on_end))
371
+
372
+ Runner.run(agent, "What is the capital of Japan?", config=config)
373
+ Runner.run(agent, "What about its population?", config=config) # Remembers context
374
+ ```
375
+
376
+ **Output:**
377
+
378
+ ```
379
+ → User: What is the capital of Japan?...
380
+ ← Agent: The capital of Japan is Tokyo.
381
+
382
+ → User: What about its population?...
383
+ ← Agent: Tokyo's population is approximately 14 million...
384
+ ```
385
+
386
+ ---
387
+
388
+ ### 11. TraceHooks — Observability, token usage, cost estimation
389
+
390
+ ```python
391
+ from agentensemble import ReActAgent, Runner, RunConfig, TraceHooks, SearchTool
392
+
393
+ agent = ReActAgent(name="research", tools=[SearchTool()], max_iterations=3)
394
+ hooks = TraceHooks()
395
+
396
+ config = RunConfig(trace_hooks=hooks)
397
+ result = Runner.run(agent, "Who won the 2024 Nobel Prize in Physics?", config=config)
398
+
399
+ print(result["result"][:100], "...")
400
+ print("Usage:", result["metadata"].get("total_usage"))
401
+ print("Cost (USD):", result["metadata"].get("cost_usd"))
402
+ print("Events:", [e.type.value for e in hooks.events])
403
+ ```
404
+
405
+ **Output:**
406
+
407
+ ```
408
+ The 2024 Nobel Prize in Physics was awarded jointly to John J. Hopfield and Geoffrey E. Hinton...
409
+ Usage: {'input_tokens': 1247, 'output_tokens': 89, 'total_tokens': 1336}
410
+ Cost (USD): 0.00293
411
+ Events: ['run_start', 'llm_start', 'llm_end', 'tool_start', 'tool_end', 'llm_start', 'llm_end', 'run_end']
412
+ ```
413
+
414
+ ---
415
+
416
+ ### 12. Human-in-loop — Pause before sensitive tools, resume with approval
417
+
418
+ ```python
419
+ from agentensemble import ReActAgent, Runner, RunConfig, function_tool
420
+
421
+ @function_tool(description="Execute a payment")
422
+ def execute_payment(amount: float, recipient: str) -> str:
423
+ return f"Paid ${amount} to {recipient}"
424
+
425
+ agent = ReActAgent(tools=[execute_payment], max_iterations=2)
426
+ config = RunConfig(interrupt_before_tools=["execute_payment"])
427
+
428
+ result = Runner.run(agent, "Pay $50 to Alice", config=config)
429
+
430
+ if result.get("__interrupted__"):
431
+ print("Paused for approval:", result["pending_tool"])
432
+ # Simulate human approval
433
+ result = Runner.run(agent, "...", config=config, resume=result["run_state"], resume_value="Paid $50 to Alice")
434
+ print(result["result"])
435
+ ```
436
+
437
+ **Output:**
438
+
439
+ ```
440
+ Paused for approval: {'name': 'execute_payment', 'arguments': {'amount': 50.0, 'recipient': 'Alice'}}
441
+ Paid $50 to Alice
442
+ ```
443
+
444
+ ---
445
+
446
+ ### 13. RAGAgent — Document Q&A (optional: `pip install agentensemble[rag]`)
447
+
448
+ ```python
449
+ from agentensemble import RAGAgent, RAGTool
450
+
451
+ rag_tool = RAGTool()
452
+ rag_tool.index_documents(["https://example.com/doc"]) # Index from URLs
453
+
454
+ agent = RAGAgent(name="doc_qa", tools=[rag_tool], fallback_strategies=2)
455
+ result = agent.run("What is task decomposition?")
456
+ print(result["result"][:200], "...")
457
+ ```
458
+
459
+ **Output:**
460
+ ```
461
+ Task decomposition is the process of breaking complex tasks into smaller,
462
+ manageable subtasks that can be executed by specialized agents...
463
+ ```
464
+
465
+ ---
466
+
467
+ ### 14. StructuredAgent — Pydantic output (optional: `langchain>=1.1`)
468
+
469
+ ```python
470
+ from pydantic import BaseModel, Field
471
+ from agentensemble import StructuredAgent
472
+
473
+ class ProductReview(BaseModel):
474
+ product: str = Field(description="Product name")
475
+ rating: int = Field(description="1-5 stars")
476
+ summary: str = Field(description="Brief summary")
477
+
478
+ agent = StructuredAgent(response_format=ProductReview)
479
+ result = agent.run("Review: iPhone 15 Pro - 5 stars, excellent camera and battery")
480
+ print(result["structured_response"])
481
+ ```
482
+
483
+ **Output:**
484
+ ```
485
+ ProductReview(product='iPhone 15 Pro', rating=5, summary='Excellent camera and battery')
486
+ ```
487
+
488
+ ---
489
+
490
+ ### Creative combo — Research pipeline (Planner + Swarm + Debate + Runner)
491
+
492
+ ```python
493
+ from agentensemble import (
494
+ ReActAgent, PlannerAgent, StateGraphAgent,
495
+ SwarmOrchestrator, DebateOrchestrator,
496
+ Runner, RunConfig, RunHooks, SearchTool,
497
+ )
498
+
499
+ # 1. Executor: ReActAgent with search
500
+ executor = ReActAgent(name="researcher", tools=[SearchTool()], max_iterations=3)
501
+
502
+ # 2. Planner decomposes → runs subtasks via executor
503
+ planner = PlannerAgent(executor=executor, max_subtasks=2)
504
+ plan_result = planner.run("Compare LangGraph vs CrewAI vs AutoGen")
505
+
506
+ # 3. Swarm: parallel research + fact-check
507
+ researcher = ReActAgent(name="r", tools=[SearchTool()], max_iterations=2)
508
+ checker = ReActAgent(name="c", tools=[SearchTool()], max_iterations=2)
509
+ swarm = SwarmOrchestrator(agents={"r": researcher, "c": checker})
510
+ swarm_result = swarm.perform("Top 3 AI agent frameworks 2024")
511
+
512
+ # 4. Debate: consensus on a fact
513
+ s1 = ReActAgent(name="s1", tools=[], max_iterations=1)
514
+ s2 = ReActAgent(name="s2", tools=[], max_iterations=1)
515
+ agg = ReActAgent(name="agg", tools=[], max_iterations=1)
516
+ debate = DebateOrchestrator(solvers=[s1, s2], aggregator=agg, rounds=2)
517
+ debate_result = debate.debate("What is 12 * 8?")
518
+
519
+ # 5. Runner with hooks
520
+ hooks = RunHooks(on_start=lambda q, _: print(f"Running: {q[:40]}..."))
521
+ result = Runner.run(executor, "Who won 2024 Nobel Physics?", RunConfig(hooks=hooks))
522
+
523
+ print("Planner:", plan_result["result"][:100], "...")
524
+ print("Swarm agents:", list(swarm_result["results"].keys()))
525
+ print("Debate answer:", debate_result["result"])
526
+ print("Runner:", result["result"][:80], "...")
527
+ ```
528
+
529
+ **Output:**
530
+
531
+ ```
532
+ Running: Who won 2024 Nobel Physics?...
533
+ Planner: Processed: Search for information... --- Processed: Summarize key findings...
534
+ Swarm agents: ['r', 'c']
535
+ Debate answer: 12 × 8 equals 96.
536
+ Runner: The 2024 Nobel Prize in Physics was awarded to John J. Hopfield and Geoffrey E. Hinton...
537
+ ```
538
+
539
+ ---
540
+
541
+ ## Run full showcase
542
+
543
+ ```bash
544
+ pip install agentensemble[search]
545
+ PYTHONPATH=. python examples/showcase_all.py
546
+ ```
547
+
548
+ Runs all demos with real API calls.
549
+
550
+ ---
551
+
552
+ ## Core Components
553
+
554
+ ### Agents
555
+
556
+ | Agent | Use Case | LLM |
557
+ | ------------------- | ----------------------------- | -------- |
558
+ | **ReActAgent** | Research, tool use, reasoning | Yes |
559
+ | **StateGraphAgent** | Custom node workflows | No |
560
+ | **RAGAgent** | Document Q&A | Optional |
561
+ | **HybridAgent** | Search → RAG → Validate | Optional |
562
+ | **RouterAgent** | Route to best agent | Optional |
563
+ | **PlannerAgent** | Task decomposition | No |
564
+
565
+ ### Orchestration
566
+
567
+ | Pattern | Description |
568
+ | -------------- | -------------------------------------------------------------------- |
569
+ | **Supervisor** | Central coordinator; optional `router` for LLM-based agent selection |
570
+ | **Swarm** | Parallel execution via `asyncio.gather` |
571
+ | **Pipeline** | Sequential agent workflow |
572
+ | **Debate** | Solvers propose → feedback → aggregator votes |
573
+
574
+ ### Tools
575
+
576
+ - `@function_tool` — Decorate functions for automatic schema
577
+ - `SearchTool` — Serper or DuckDuckGo
578
+ - `RAGTool` — Document indexing and retrieval
579
+ - `ValidationTool` — Quality checks
580
+ - `ToolRegistry` — Dynamic tool management
581
+
582
+ ### Memory & Runner
583
+
584
+ - **Session** — `InMemorySession`, `SQLiteSession` for multi-turn context
585
+ - **Runner** — `Runner.run()`, `Runner.arun()` with `RunConfig`, `RunHooks`, retries
586
+ - **TraceHooks** — Observability: run/LLM/tool events, token usage, cost estimation
587
+ - **Human-in-loop** — `interrupt_before_tools=["pay", "send_email"]` to pause for approval
588
+
589
+ ---
590
+
591
+ ## Project Structure
592
+
593
+ ```
594
+ agentensemble/
595
+ ├── agents/ # ReAct, StateGraph, RAG, Hybrid, Structured
596
+ ├── core/ # AgentProtocol, RunnableProtocol
597
+ ├── llm/ # LLMProvider, MistralLLMProvider
598
+ ├── memory/ # Session, InMemorySession, SQLiteSession
599
+ ├── orchestration/ # Ensemble, Supervisor, Swarm, Pipeline, Debate
600
+ ├── tools/ # Tool protocol, @function_tool, built-ins
601
+ ├── router/ # RouterAgent (LLM-based routing)
602
+ ├── planner/ # PlannerAgent (task decomposition)
603
+ ├── graph/ # WorkflowGraph (composable workflows)
604
+ ├── tracing/ # TraceHooks, TraceEvent
605
+ ├── testing/ # Benchmark, AgentComparison, Metrics
606
+ └── runner.py # Runner, RunConfig, RunHooks
607
+ ```
608
+
609
+ ---
610
+
611
+ ## Comparison
612
+
613
+ | Feature | AgentEnsemble | LangGraph | CrewAI | AutoGen |
614
+ | ------------------ | ------------------------- | ------------ | ------ | ------- |
615
+ | Multi-provider LLM | Mistral (extensible) | Any | Any | Any |
616
+ | Async API | `arun()` everywhere | Yes | Yes | Yes |
617
+ | Router agents | RouterAgent | Manual | Yes | Manual |
618
+ | Planner pattern | PlannerAgent | Manual | Yes | Manual |
619
+ | Graph workflows | WorkflowGraph, StateGraph | Native | No | No |
620
+ | Debate pattern | DebateOrchestrator | Manual | Yes | Yes |
621
+ | Session memory | InMemory, SQLite | Checkpointer | No | No |
622
+ | Observability | TraceHooks, usage, cost | LangSmith | No | No |
623
+ | Human-in-loop | interrupt_before_tools | interrupt() | No | No |
624
+
625
+ ---
626
+
627
+ ## Production Use
628
+
629
+ - **Retries**: `RunConfig(max_retries=2, retry_on=(RateLimitError, TimeoutError))`
630
+ - **Session**: `Runner.run(agent, query, config=RunConfig(session=SQLiteSession()))`
631
+ - **Tracing**: `RunConfig(trace_hooks=TraceHooks())` — captures run/LLM/tool events, usage, cost
632
+ - **Human-in-loop**: `RunConfig(interrupt_before_tools=["pay"])` — pause before sensitive tools; resume with `Runner.run(agent, ..., resume=run_state, resume_value="Approved")`
633
+ - **Benchmarks**: `AgentComparison`, `Benchmark.research_tasks()`, `Metrics`
634
+
635
+ ---
636
+
637
+ ## Documentation
638
+
639
+ - **[Showcase](examples/showcase_all.py)** — Run all 10 features with real API output
640
+ - [Examples](examples/) — `quickstart`, `react_llm`, `swarm`, `pipeline`, `streaming`, `debate`, `rag`, `research_agent`, `graph_workflow`, `runner`, `structured_output`
641
+ - [Roadmap](docs/ROADMAP_2025.md) — Planned improvements
642
+
643
+ > **Note:** Ensure `docs/agentensemble-architecture.png` is committed so the architecture diagram renders on GitHub.
644
+
645
+ ---
646
+
647
+ ## License
648
+
649
+ MIT — see [LICENSE](LICENSE).