ai-agentswarm 0.1.1__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 (33) hide show
  1. ai_agentswarm-0.1.1/LICENSE +21 -0
  2. ai_agentswarm-0.1.1/MANIFEST.in +2 -0
  3. ai_agentswarm-0.1.1/PKG-INFO +105 -0
  4. ai_agentswarm-0.1.1/README.md +46 -0
  5. ai_agentswarm-0.1.1/pyproject.toml +56 -0
  6. ai_agentswarm-0.1.1/setup.cfg +4 -0
  7. ai_agentswarm-0.1.1/src/agentswarm/__init__.py +0 -0
  8. ai_agentswarm-0.1.1/src/agentswarm/agents/__init__.py +26 -0
  9. ai_agentswarm-0.1.1/src/agentswarm/agents/base_agent.py +53 -0
  10. ai_agentswarm-0.1.1/src/agentswarm/agents/gathering_agent.py +25 -0
  11. ai_agentswarm-0.1.1/src/agentswarm/agents/map_reduce_agent.py +60 -0
  12. ai_agentswarm-0.1.1/src/agentswarm/agents/mcp_agent.py +99 -0
  13. ai_agentswarm-0.1.1/src/agentswarm/agents/merge_agent.py +25 -0
  14. ai_agentswarm-0.1.1/src/agentswarm/agents/react_agent.py +189 -0
  15. ai_agentswarm-0.1.1/src/agentswarm/agents/thinking_agent.py +27 -0
  16. ai_agentswarm-0.1.1/src/agentswarm/agents/transformer_agent.py +47 -0
  17. ai_agentswarm-0.1.1/src/agentswarm/datamodels/__init__.py +16 -0
  18. ai_agentswarm-0.1.1/src/agentswarm/datamodels/context.py +139 -0
  19. ai_agentswarm-0.1.1/src/agentswarm/datamodels/local_store.py +21 -0
  20. ai_agentswarm-0.1.1/src/agentswarm/datamodels/message.py +10 -0
  21. ai_agentswarm-0.1.1/src/agentswarm/datamodels/responses.py +17 -0
  22. ai_agentswarm-0.1.1/src/agentswarm/datamodels/store.py +34 -0
  23. ai_agentswarm-0.1.1/src/agentswarm/llms/__init__.py +11 -0
  24. ai_agentswarm-0.1.1/src/agentswarm/llms/gemini.py +94 -0
  25. ai_agentswarm-0.1.1/src/agentswarm/llms/llm.py +31 -0
  26. ai_agentswarm-0.1.1/src/agentswarm/utils/__init__.py +3 -0
  27. ai_agentswarm-0.1.1/src/agentswarm/utils/trace_view.py +907 -0
  28. ai_agentswarm-0.1.1/src/agentswarm/utils/tracing.py +135 -0
  29. ai_agentswarm-0.1.1/src/ai_agentswarm.egg-info/PKG-INFO +105 -0
  30. ai_agentswarm-0.1.1/src/ai_agentswarm.egg-info/SOURCES.txt +31 -0
  31. ai_agentswarm-0.1.1/src/ai_agentswarm.egg-info/dependency_links.txt +1 -0
  32. ai_agentswarm-0.1.1/src/ai_agentswarm.egg-info/requires.txt +19 -0
  33. ai_agentswarm-0.1.1/src/ai_agentswarm.egg-info/top_level.txt +1 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Luca Roverelli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ include README.md
2
+ include LICENSE
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: ai-agentswarm
3
+ Version: 0.1.1
4
+ Summary: A recursive, functional, and state-isolated Multi-Agent Framework.
5
+ Author-email: Lucas Roverelli <lucas.roverelli@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Luca Roverelli
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/ai-agentswarm/agentswarm
28
+ Project-URL: Bug Tracker, https://github.com/ai-agentswarm/agentswarm/issues
29
+ Project-URL: Documentation, https://github.com/ai-agentswarm/agentswarm#readme
30
+ Keywords: ai,agents,llm,framework,recursive,react
31
+ Classifier: Development Status :: 3 - Alpha
32
+ Classifier: Intended Audience :: Developers
33
+ Classifier: License :: OSI Approved :: MIT License
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
38
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
39
+ Requires-Python: >=3.10
40
+ Description-Content-Type: text/markdown
41
+ License-File: LICENSE
42
+ Requires-Dist: pydantic>=2.12.4
43
+ Requires-Dist: google-genai>=1.50.1
44
+ Requires-Dist: mcp
45
+ Provides-Extra: dev
46
+ Requires-Dist: pytest; extra == "dev"
47
+ Requires-Dist: twine; extra == "dev"
48
+ Requires-Dist: build; extra == "dev"
49
+ Requires-Dist: black; extra == "dev"
50
+ Requires-Dist: isort; extra == "dev"
51
+ Provides-Extra: examples
52
+ Requires-Dist: httpx; extra == "examples"
53
+ Requires-Dist: python-dotenv; extra == "examples"
54
+ Provides-Extra: docs
55
+ Requires-Dist: mkdocs; extra == "docs"
56
+ Requires-Dist: mkdocs-material; extra == "docs"
57
+ Requires-Dist: mkdocstrings[python]; extra == "docs"
58
+ Dynamic: license-file
59
+
60
+ # 🐝 AgentSwarm
61
+
62
+ **A Recursive, Functional, and Type-Safe Framework for Autonomous AI Agents.**
63
+
64
+ ![Logo](docs/media/logo_bg.png)
65
+
66
+ **AgentSwarm** is a next-generation agentic orchestration framework designed to solve the critical issues of **Context Pollution** and **Complex Orchestration** in multi-agent systems.
67
+
68
+ Unlike chat-based frameworks (like AutoGen) or static graph frameworks (like LangGraph), AgentSwarm treats agents as **Strongly Typed Asynchronous Functions** that operate in isolated environments ("Tabula Rasa"), natively supporting recursion and Map-Reduce patterns.
69
+
70
+ ## 🚀 Why AgentSwarm?
71
+
72
+ Current architectures suffer from two main problems:
73
+ 1. **Context Pollution:** Sub-agents inherit the parent's entire chat history, wasting tokens, increasing latency, and confusing the model.
74
+ 2. **Boilerplate Hell:** Defining dynamic recursive graphs requires complex configuration and rigid state definitions.
75
+
76
+ **AgentSwarm solves these problems:**
77
+
78
+ * **🧠 Tabula Rasa Execution:** Every invoked agent starts with a *clean* context. The sub-agent receives only the specific input required, executes the task, and returns the output. No noise, no context window overflow.
79
+ * **📦 Native Blackboard Pattern:** Agents don't pass massive raw text strings back and forth. They use a shared **Key-Value Store** to manipulate, transform, and merge data, exchanging only references (Keys).
80
+ * **🛡️ Type-Safe by Design:** Built on Pydantic and Python Generics. Agent inputs and outputs are validated at runtime, and JSON schemas for the LLM are generated automatically from type hints.
81
+ * **⚡ Implicit Parallelism:** The `ReActAgent` engine automatically handles parallel tool execution (Map) and result aggregation (Reduce) without complex graph definitions.
82
+
83
+ ## 🛠️ Architecture
84
+
85
+ AgentSwarm is built on three fundamental concepts:
86
+
87
+ ### 1. Agent-as-a-Function
88
+ Every agent is a class inheriting from `BaseAgent[Input, Output]`. There are no "nodes" or "edges" to manually define. Orchestration emerges naturally from the functional calls between agents.
89
+ Moreover, agents are **strongly typed** and **asynchronous** by design, making them easy to compose and debug. When errors occur, they are propagated as standard exceptions, that can be managed by agents at different levels.
90
+
91
+ ### 2. The "Store" (Shared Memory)
92
+ Instead of overloading the chat context, agents use the `Store` to manage information. We provide a set of agents to ease the management of the store:
93
+ * **GatheringAgent:** Retrieves data from the store for display or processing.
94
+ * **TransformerAgent:** Transforms data in the store (e.g., summarize, filter) using natural language commands and returns a new Key, keeping the context light.
95
+ * **MergeAgent:** Combines multiple data keys into a single entity.
96
+
97
+ ### 3. Recursive Map-Reduce
98
+ The Map-Reduce pattern is a powerful way to decompose complex tasks into smaller, parallelizable subtasks. It is used since decades in parallel computing. With AgentSwarm, we borrow this pattern to ease the orchestration of complex tasks, preventing context pullution or exosting, prefering to use the store as a shared memory.
99
+ A `MapReduceAgent` can decompose complex tasks, dynamically instantiate clones of itself or other agents, and execute work in parallel, avoiding deadlocks via instance isolation.
100
+
101
+ ### 4. Interoperability & Hybrid Execution
102
+ AgentSwarm is designed to be an open ecosystem, not a walled garden.
103
+ * **Protocol Agnostic:** It (will) supports the **Model Context Protocol (MCP)** and **Agent-to-Agent (A2A)** standards, allowing your agents to interact seamlessly with external tools and third-party agent networks. You can also easily wrap existing **LangChain** tools and run them within the Swarm.
104
+ * **Hybrid Runtime:** Each agent can be executed locally, or remotly, exploiting **AWS** or **Google Cloud** services. Also the Store can be a remote key-value store, like Redis.
105
+
@@ -0,0 +1,46 @@
1
+ # 🐝 AgentSwarm
2
+
3
+ **A Recursive, Functional, and Type-Safe Framework for Autonomous AI Agents.**
4
+
5
+ ![Logo](docs/media/logo_bg.png)
6
+
7
+ **AgentSwarm** is a next-generation agentic orchestration framework designed to solve the critical issues of **Context Pollution** and **Complex Orchestration** in multi-agent systems.
8
+
9
+ Unlike chat-based frameworks (like AutoGen) or static graph frameworks (like LangGraph), AgentSwarm treats agents as **Strongly Typed Asynchronous Functions** that operate in isolated environments ("Tabula Rasa"), natively supporting recursion and Map-Reduce patterns.
10
+
11
+ ## 🚀 Why AgentSwarm?
12
+
13
+ Current architectures suffer from two main problems:
14
+ 1. **Context Pollution:** Sub-agents inherit the parent's entire chat history, wasting tokens, increasing latency, and confusing the model.
15
+ 2. **Boilerplate Hell:** Defining dynamic recursive graphs requires complex configuration and rigid state definitions.
16
+
17
+ **AgentSwarm solves these problems:**
18
+
19
+ * **🧠 Tabula Rasa Execution:** Every invoked agent starts with a *clean* context. The sub-agent receives only the specific input required, executes the task, and returns the output. No noise, no context window overflow.
20
+ * **📦 Native Blackboard Pattern:** Agents don't pass massive raw text strings back and forth. They use a shared **Key-Value Store** to manipulate, transform, and merge data, exchanging only references (Keys).
21
+ * **🛡️ Type-Safe by Design:** Built on Pydantic and Python Generics. Agent inputs and outputs are validated at runtime, and JSON schemas for the LLM are generated automatically from type hints.
22
+ * **⚡ Implicit Parallelism:** The `ReActAgent` engine automatically handles parallel tool execution (Map) and result aggregation (Reduce) without complex graph definitions.
23
+
24
+ ## 🛠️ Architecture
25
+
26
+ AgentSwarm is built on three fundamental concepts:
27
+
28
+ ### 1. Agent-as-a-Function
29
+ Every agent is a class inheriting from `BaseAgent[Input, Output]`. There are no "nodes" or "edges" to manually define. Orchestration emerges naturally from the functional calls between agents.
30
+ Moreover, agents are **strongly typed** and **asynchronous** by design, making them easy to compose and debug. When errors occur, they are propagated as standard exceptions, that can be managed by agents at different levels.
31
+
32
+ ### 2. The "Store" (Shared Memory)
33
+ Instead of overloading the chat context, agents use the `Store` to manage information. We provide a set of agents to ease the management of the store:
34
+ * **GatheringAgent:** Retrieves data from the store for display or processing.
35
+ * **TransformerAgent:** Transforms data in the store (e.g., summarize, filter) using natural language commands and returns a new Key, keeping the context light.
36
+ * **MergeAgent:** Combines multiple data keys into a single entity.
37
+
38
+ ### 3. Recursive Map-Reduce
39
+ The Map-Reduce pattern is a powerful way to decompose complex tasks into smaller, parallelizable subtasks. It is used since decades in parallel computing. With AgentSwarm, we borrow this pattern to ease the orchestration of complex tasks, preventing context pullution or exosting, prefering to use the store as a shared memory.
40
+ A `MapReduceAgent` can decompose complex tasks, dynamically instantiate clones of itself or other agents, and execute work in parallel, avoiding deadlocks via instance isolation.
41
+
42
+ ### 4. Interoperability & Hybrid Execution
43
+ AgentSwarm is designed to be an open ecosystem, not a walled garden.
44
+ * **Protocol Agnostic:** It (will) supports the **Model Context Protocol (MCP)** and **Agent-to-Agent (A2A)** standards, allowing your agents to interact seamlessly with external tools and third-party agent networks. You can also easily wrap existing **LangChain** tools and run them within the Swarm.
45
+ * **Hybrid Runtime:** Each agent can be executed locally, or remotly, exploiting **AWS** or **Google Cloud** services. Also the Store can be a remote key-value store, like Redis.
46
+
@@ -0,0 +1,56 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "ai-agentswarm"
7
+ version = "0.1.1"
8
+ description = "A recursive, functional, and state-isolated Multi-Agent Framework."
9
+ readme = "README.md"
10
+ authors = [
11
+ { name = "Lucas Roverelli", email = "lucas.roverelli@gmail.com" },
12
+ ]
13
+ license = { file = "LICENSE" }
14
+ requires-python = ">=3.10"
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
23
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
24
+ ]
25
+ dependencies = [
26
+ "pydantic>=2.12.4",
27
+ "google-genai>=1.50.1",
28
+ "mcp"
29
+ ]
30
+ keywords = ["ai", "agents", "llm", "framework", "recursive", "react"]
31
+
32
+ [project.optional-dependencies]
33
+ dev = [
34
+ "pytest",
35
+ "twine",
36
+ "build",
37
+ "black",
38
+ "isort"
39
+ ]
40
+ examples = [
41
+ "httpx",
42
+ "python-dotenv"
43
+ ]
44
+ docs = [
45
+ "mkdocs",
46
+ "mkdocs-material",
47
+ "mkdocstrings[python]"
48
+ ]
49
+
50
+ [project.urls]
51
+ "Homepage" = "https://github.com/ai-agentswarm/agentswarm"
52
+ "Bug Tracker" = "https://github.com/ai-agentswarm/agentswarm/issues"
53
+ "Documentation" = "https://github.com/ai-agentswarm/agentswarm#readme"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,26 @@
1
+ from .react_agent import ReActAgent
2
+ from .map_reduce_agent import MapReduceAgent, MapReduceInput
3
+ from .gathering_agent import GatheringAgent, GatheringAgentInput
4
+ from .merge_agent import MergeAgent, MergeAgentInput
5
+ from .transformer_agent import TransformerAgent, TransformerAgentInput
6
+ from .thinking_agent import ThinkingAgent, ThinkingInput
7
+ from .base_agent import BaseAgent
8
+
9
+ from .mcp_agent import MCPBaseAgent, MCPToolAgent
10
+
11
+ __all__ = [
12
+ "ReActAgent",
13
+ "MapReduceAgent",
14
+ "MapReduceInput",
15
+ "GatheringAgent",
16
+ "GatheringAgentInput",
17
+ "MergeAgent",
18
+ "MergeAgentInput",
19
+ "TransformerAgent",
20
+ "TransformerAgentInput",
21
+ "ThinkingAgent",
22
+ "ThinkingInput",
23
+ "BaseAgent",
24
+ "MCPBaseAgent",
25
+ "MCPToolAgent",
26
+ ]
@@ -0,0 +1,53 @@
1
+ from abc import abstractmethod
2
+ from typing import TypeVar, Generic, get_args
3
+
4
+ from pydantic import BaseModel
5
+ from ..datamodels import Context
6
+
7
+
8
+ InputType = TypeVar('InputType', bound=BaseModel)
9
+ OutputType = TypeVar('OutputType', bound=BaseModel)
10
+
11
+
12
+ class BaseAgent(Generic[InputType, OutputType]):
13
+
14
+ @abstractmethod
15
+ def id(self) -> str:
16
+ pass
17
+
18
+ @abstractmethod
19
+ def description(self, user_id: str) -> str:
20
+ pass
21
+
22
+ @abstractmethod
23
+ async def execute(self, user_id: str, context: Context, input: InputType = None) -> OutputType:
24
+ pass
25
+
26
+ def _get_generic_type(self, index: int):
27
+ """
28
+ Obtains the concrete type of the generic at the specified index (0 for InputType, 1 for OutputType).
29
+ Works by inspecting __orig_bases__ of the class at runtime.
30
+ """
31
+ for base in getattr(self.__class__, "__orig_bases__", []):
32
+ origin = getattr(base, "__origin__", None)
33
+ if origin is BaseAgent or issubclass(origin, BaseAgent):
34
+ args = get_args(base)
35
+ if args and len(args) > index:
36
+ return args[index]
37
+ return None
38
+
39
+ def input_parameters(self) -> dict:
40
+ input_type = self._get_generic_type(0)
41
+ if input_type and hasattr(input_type, 'model_json_schema'):
42
+ schema = input_type.model_json_schema()
43
+ schema.pop('title', None)
44
+ return schema
45
+ return {}
46
+
47
+ def output_parameters(self) -> dict:
48
+ output_type = self._get_generic_type(1)
49
+ if output_type and hasattr(output_type, 'model_json_schema'):
50
+ schema = output_type.model_json_schema()
51
+ schema.pop('title', None)
52
+ return schema
53
+ return {}
@@ -0,0 +1,25 @@
1
+ from pydantic import BaseModel, Field
2
+ from .base_agent import BaseAgent
3
+ from ..datamodels import Context
4
+
5
+ class GatheringAgentInput(BaseModel):
6
+ key: str = Field(description="The key of the information to gather")
7
+
8
+ class GatheringAgent(BaseAgent[GatheringAgentInput, str]):
9
+ def id(self) -> str:
10
+ return "gathering-agent"
11
+
12
+ def description(self, user_id: str) -> str:
13
+ return """
14
+ I'm able to gather an information from the store and add to the current context.
15
+ USE THIS AGENT ONLY WHEN YOU NEED TO DISPLAY THE INFORMATION TO THE USER.
16
+ Whenever possible, if you need to filter or process the data, use the "transformer-agent".
17
+ """
18
+
19
+ async def execute(self, user_id: str, context: Context, input: GatheringAgentInput) -> str:
20
+ if not context.store.has(input.key):
21
+ raise Exception(f"Information from the store with key {input.key} not found")
22
+ value = context.store.get(input.key)
23
+ return value
24
+
25
+
@@ -0,0 +1,60 @@
1
+ import os
2
+ from typing import List
3
+
4
+ from pydantic import BaseModel, Field
5
+ from ..llms import LLM, GeminiLLM
6
+ from .base_agent import BaseAgent
7
+ from ..datamodels import Context, Message, KeyStoreResponse
8
+ from .react_agent import ReActAgent
9
+
10
+ class MapReduceInput(BaseModel):
11
+ task: str = Field(description="The task to solve, as a string")
12
+
13
+ class MapReduceAgent(ReActAgent[MapReduceInput, KeyStoreResponse]):
14
+ def __init__(self, max_iterations: int = 100, agents: List[BaseAgent] = []):
15
+ super().__init__(max_iterations)
16
+ self.agents = agents
17
+
18
+ def get_llm(self, user_id: str) -> LLM:
19
+ # TODO: Better LLM consiguration
20
+ return GeminiLLM(api_key=os.getenv("GEMINI_API_KEY"))
21
+
22
+ def id(self) -> str:
23
+ return "map-reduce"
24
+
25
+ def description(self, user_id: str) -> str:
26
+ return """USE THIS AGENT FOR:
27
+ 1. COMPLEX CONTENT GENERATION: Creating multi-part artifacts like software libraries, full books, or extensive reports where each chapter/module needs to be generated in parallel.
28
+ 2. RECURSIVE TASKS: Exploration of hierarchical structures (directories, org charts) or recursive problem solving.
29
+ 3. LARGE DATASETS: Processing data that exceeds context limits by splitting (Map) and summarizing (Reduce).
30
+ 4. PARALLEL EXECUTION: Any task requiring massive parallel execution of sub-tasks.
31
+
32
+ DO NOT USE FOR: Simple, linear tasks or single-file creation that fit in a single context window.
33
+ """
34
+
35
+ def prompt(self, user_id: str) -> str:
36
+ return """You are a recursive Map-Reduce agent. Your GOAL is to solve the given 'task' completely and return the FINAL RESULT.
37
+
38
+ ALGORITHM:
39
+ 1. ANALYZE: Understand the task (e.g., analyze a folder).
40
+ 2. MAP: Break it down into sub-tasks (e.g., read files in current dir, recursively call 'map-reduce' for sub-directories).
41
+ 3. EXECUTE: Run these sub-tasks using the available tools. Parallelize where possible.
42
+ 4. REDUCE: Collect ALL results from the sub-tasks.
43
+ 5. SYNTHESIZE: Combine them into a single, comprehensive final report.
44
+
45
+ CRITICAL RULES:
46
+ - ANTI-RECURSION: If your assigned task is T, DO NOT call 'map-reduce' with task T again. You must BREAK DOWN task T into smaller sub-tasks (t1, t2...) and delegate THOSE.
47
+ - You are the one responsible for executing the atomic actions for T (e.g., gathering data, listing items) before delegating sub-parts.
48
+ - Do NOT return "I am working on it". You must work on it UNTIL IT IS DONE.
49
+ - Do NOT return partial results unless you have hit a hard limit.
50
+ - Your final 'assistant' message MUST contain the complete answer/report.
51
+ """
52
+
53
+ def available_agents(self, user_id: str) -> List[BaseAgent]:
54
+ # IMPORTANT: Return a NEW instance of MapReduceAgent instead of self.
55
+ return [MapReduceAgent(max_iterations=self.max_iterations, agents=self.agents)] + self.agents
56
+
57
+ def generate_messages_context(self, user_id: str, context: Context, input: MapReduceInput = None) -> List[Message]:
58
+ msgs = super().generate_messages_context(user_id, context, input)
59
+ msgs.append(Message(type="user", content=f"CURRENT TASK: {input.task}\n\nWARNING: Do not call 'map-reduce' with this exact same task '{input.task}' again, as it would cause an infinite loop. decompose it into smaller sub-tasks."))
60
+ return msgs
@@ -0,0 +1,99 @@
1
+ import asyncio
2
+ from typing import List, Optional, Any, Dict
3
+ from abc import abstractmethod
4
+ from contextlib import asynccontextmanager
5
+
6
+ from mcp import ClientSession, StdioServerParameters
7
+ from mcp.client.stdio import stdio_client
8
+ from mcp.types import Tool
9
+
10
+ from .base_agent import BaseAgent
11
+ from ..datamodels import Context
12
+
13
+ class MCPToolAgent(BaseAgent[dict, Any]):
14
+ """
15
+ An agent that wraps a specific tool from an MCP server.
16
+ """
17
+ def __init__(self, session: ClientSession, tool: Tool):
18
+ self._session = session
19
+ self._tool = tool
20
+
21
+ def id(self) -> str:
22
+ return self._tool.name
23
+
24
+ def description(self, user_id: str) -> str:
25
+ return self._tool.description or f"Tool {self._tool.name} from MCP server"
26
+
27
+ def input_parameters(self) -> dict:
28
+ # MCP tools define inputSchema in a way compatible with JSON Schema
29
+ schema = self._tool.inputSchema
30
+ if schema:
31
+ # Ensure title is removed if present as per convention in other agents, though optional
32
+ schema.pop('title', None)
33
+ return schema
34
+ return {}
35
+
36
+ def output_parameters(self) -> dict:
37
+ # MCP tools return generic content lists (text, images, etc.)
38
+ # We don't have a strict schema for the output content structure here yet
39
+ return {"type": "object", "description": "The result of the tool execution"}
40
+
41
+ async def execute(self, user_id: str, context: Context, input: dict = None) -> Any:
42
+ if input is None:
43
+ input = {}
44
+
45
+ # Call the tool on the MCP server
46
+ result = await self._session.call_tool(self._tool.name, arguments=input)
47
+
48
+ # Process result.content which is a list of TextContent | ImageContent | EmbeddedResource
49
+ # For simplicity, we return the raw list or a simplified text representation
50
+ # depending on what the framework expects. BaseAgent expects output_type.
51
+ # Since we defined OutputType as Any, we return the result object or content.
52
+ return result.content
53
+
54
+ class MCPBaseAgent:
55
+ """
56
+ A base class for connecting to an MCP server and discovering tools.
57
+ This acts as a factory/manager for MCPToolAgents.
58
+ """
59
+
60
+ def __init__(self):
61
+ self._session: Optional[ClientSession] = None
62
+ self._exit_stack = None
63
+
64
+ @abstractmethod
65
+ def get_server_params(self) -> StdioServerParameters:
66
+ """
67
+ Define the connection parameters for the MCP server.
68
+ """
69
+ pass
70
+
71
+ @asynccontextmanager
72
+ async def connect(self):
73
+ """
74
+ Async context manager to establish connection to the MCP server.
75
+ """
76
+ server_params = self.get_server_params()
77
+
78
+ # We manually manage the nested context managers to keep the session alive appropriately
79
+ async with stdio_client(server_params) as (read, write):
80
+ async with ClientSession(read, write) as session:
81
+ self._session = session
82
+ await session.initialize()
83
+ yield self
84
+ self._session = None
85
+
86
+ async def get_agents(self) -> List[MCPToolAgent]:
87
+ """
88
+ Discovers tools on the connected MCP server and returns them as a list of MCPToolAgent.
89
+ Must be called within the 'connect' context.
90
+ """
91
+ if not self._session:
92
+ raise RuntimeError("MCP session is not active. Use 'async with agent.connect():'")
93
+
94
+ response = await self._session.list_tools()
95
+ agents = []
96
+ for tool in response.tools:
97
+ agents.append(MCPToolAgent(self._session, tool))
98
+
99
+ return agents
@@ -0,0 +1,25 @@
1
+ from typing import List
2
+ import uuid
3
+
4
+ from pydantic import BaseModel, Field
5
+ from .base_agent import BaseAgent
6
+ from ..datamodels import Message, Context, KeyStoreResponse
7
+
8
+ class MergeAgentInput(BaseModel):
9
+ keys: List[str] = Field(description="The list of keys to merge")
10
+
11
+ class MergeAgent(BaseAgent[MergeAgentInput, KeyStoreResponse]):
12
+ def id(self) -> str:
13
+ return "merge-agent"
14
+
15
+ def description(self, user_id: str) -> str:
16
+ return "I'm able to merge different informations into a single one."
17
+
18
+ async def execute(self, user_id: str, context: Context, input: MergeAgentInput) -> KeyStoreResponse:
19
+ values = [context.store.get(key) for key in input.keys]
20
+ value = "\n".join(values)
21
+ key = f"merged_{uuid.uuid4()}"
22
+ context.store.set(key, value)
23
+ return KeyStoreResponse(key=key, description=f"Merged information from keys {input.keys}")
24
+
25
+