swarmai 1.0.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.
swarmai-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: swarmai
3
+ Version: 1.0.0
4
+ Summary: A highly flexible multi-agent ReAct framework powered by Google Gemini
5
+ Author-email: ProgVM <progvminc@gmail.com>
6
+ Project-URL: Homepage, https://github.com/ProgVM
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: google-genai
13
+ Requires-Dist: duckduckgo-search
14
+
15
+ # 🐝 Swarm
16
+
17
+ **Swarm** is an advanced, open-source multi-agent intelligence framework designed for autonomous collaboration. Powered by Google's **Gemini 3.1 Flash Lite**, Swarm allows multiple agents to interact, use tools, execute code, and perform web searches in a shared environment.
18
+
19
+ ## 🚀 Key Features
20
+
21
+ - **N-Agent Collaboration**: Run any number of agents in a single session.
22
+ - **Autonomous Tool Usage**: Agents can perform Google searches and execute Shell commands.
23
+ - **Agent Sandboxing**: Per-agent blacklists for terminal commands and file paths.
24
+ - **Turn Management**: Agents can autonomously pass turns to specific peers using `pass_turn`.
25
+ - **ReAct Loop**: High-level reasoning before acting, supporting multi-step tool execution.
26
+ - **Files API Support**: Agents can upload and analyze PDF, images, and logs via Google Files API.
27
+ - **Persistent Sessions**: Save and load full agent histories and environment states.
28
+
29
+ ## 🛠 Installation
30
+
31
+ ```bash
32
+ # Clone the repository
33
+ git clone https://github.com/ProgVM/swarm
34
+ cd swarm
35
+
36
+ # Install dependencies
37
+ pip install .
38
+ ```
39
+
40
+ Or:
41
+ ```bash
42
+ pip install swarmai
43
+ ```
44
+
45
+ ## 💻 CLI Usage
46
+
47
+ Start a basic conversation with two agents:
48
+ ```bash
49
+ swarm --keys YOUR_API_KEY --agents_count 2
50
+ ```
51
+
52
+ Start an autonomous coding session:
53
+ ```bash
54
+ swarm --first_msg "Write a web scraper in Python and test it" \
55
+ --sys1 "You are an Expert Coder" \
56
+ --sys2 "You are a Security Auditor" \
57
+ --cmd_timeout 600
58
+ ```
59
+
60
+ ### Advanced Configuration
61
+ Swarm supports individual parameters for every agent using `aiN_` prefixes:
62
+ - `--ai1_name "Architect"`
63
+ - `--ai1_model "gemini-2.0-flash"`
64
+ - `--ai2_name "Tester"`
65
+ - `--ai2_temp 0.2`
66
+
67
+ ## ⚙️ Configuration File
68
+ You can use a JSON config to manage complex Swarms:
69
+ ```json
70
+ {
71
+ "agents_count": 3,
72
+ "model": "gemini-3.1-flash-lite",
73
+ "cmd_blacklist": ["rm -rf", "format"],
74
+ "no_pause": true
75
+ }
76
+ ```
77
+ Run with: `swarm --config my_swarm.json`
78
+
79
+ ## ⌨️ Command Center (Interactive Menu)
80
+ Press `Ctrl+C` at any time to:
81
+ - Rotate API Keys.
82
+ - Toggle reading pauses.
83
+ - Save the current state.
84
+ - Change logging levels (DEBUG, INFO, WARNING).
85
+ - Inject manual directives into agents' minds.
86
+
87
+ ## 🤝 Contributing
88
+ Developed by **ProgVM**.
89
+ - Email: progvminc@gmail.com
90
+ - GitHub: [https://github.com/ProgVM](https://github.com/ProgVM)
91
+
92
+ License: MIT
@@ -0,0 +1,78 @@
1
+ # 🐝 Swarm
2
+
3
+ **Swarm** is an advanced, open-source multi-agent intelligence framework designed for autonomous collaboration. Powered by Google's **Gemini 3.1 Flash Lite**, Swarm allows multiple agents to interact, use tools, execute code, and perform web searches in a shared environment.
4
+
5
+ ## 🚀 Key Features
6
+
7
+ - **N-Agent Collaboration**: Run any number of agents in a single session.
8
+ - **Autonomous Tool Usage**: Agents can perform Google searches and execute Shell commands.
9
+ - **Agent Sandboxing**: Per-agent blacklists for terminal commands and file paths.
10
+ - **Turn Management**: Agents can autonomously pass turns to specific peers using `pass_turn`.
11
+ - **ReAct Loop**: High-level reasoning before acting, supporting multi-step tool execution.
12
+ - **Files API Support**: Agents can upload and analyze PDF, images, and logs via Google Files API.
13
+ - **Persistent Sessions**: Save and load full agent histories and environment states.
14
+
15
+ ## 🛠 Installation
16
+
17
+ ```bash
18
+ # Clone the repository
19
+ git clone https://github.com/ProgVM/swarm
20
+ cd swarm
21
+
22
+ # Install dependencies
23
+ pip install .
24
+ ```
25
+
26
+ Or:
27
+ ```bash
28
+ pip install swarmai
29
+ ```
30
+
31
+ ## 💻 CLI Usage
32
+
33
+ Start a basic conversation with two agents:
34
+ ```bash
35
+ swarm --keys YOUR_API_KEY --agents_count 2
36
+ ```
37
+
38
+ Start an autonomous coding session:
39
+ ```bash
40
+ swarm --first_msg "Write a web scraper in Python and test it" \
41
+ --sys1 "You are an Expert Coder" \
42
+ --sys2 "You are a Security Auditor" \
43
+ --cmd_timeout 600
44
+ ```
45
+
46
+ ### Advanced Configuration
47
+ Swarm supports individual parameters for every agent using `aiN_` prefixes:
48
+ - `--ai1_name "Architect"`
49
+ - `--ai1_model "gemini-2.0-flash"`
50
+ - `--ai2_name "Tester"`
51
+ - `--ai2_temp 0.2`
52
+
53
+ ## ⚙️ Configuration File
54
+ You can use a JSON config to manage complex Swarms:
55
+ ```json
56
+ {
57
+ "agents_count": 3,
58
+ "model": "gemini-3.1-flash-lite",
59
+ "cmd_blacklist": ["rm -rf", "format"],
60
+ "no_pause": true
61
+ }
62
+ ```
63
+ Run with: `swarm --config my_swarm.json`
64
+
65
+ ## ⌨️ Command Center (Interactive Menu)
66
+ Press `Ctrl+C` at any time to:
67
+ - Rotate API Keys.
68
+ - Toggle reading pauses.
69
+ - Save the current state.
70
+ - Change logging levels (DEBUG, INFO, WARNING).
71
+ - Inject manual directives into agents' minds.
72
+
73
+ ## 🤝 Contributing
74
+ Developed by **ProgVM**.
75
+ - Email: progvminc@gmail.com
76
+ - GitHub: [https://github.com/ProgVM](https://github.com/ProgVM)
77
+
78
+ License: MIT
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "swarmai"
7
+ version = "1.0.0"
8
+ authors = [
9
+ { name="ProgVM", email="progvminc@gmail.com" },
10
+ ]
11
+ description = "A highly flexible multi-agent ReAct framework powered by Google Gemini"
12
+ readme = "README.md"
13
+ requires-python = ">=3.9"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ dependencies = [
20
+ "google-genai",
21
+ "duckduckgo-search",
22
+ ]
23
+
24
+ [project.urls]
25
+ "Homepage" = "https://github.com/ProgVM"
26
+
27
+ [project.scripts]
28
+ swarm = "swarm.cli:run"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .core import SwarmSession, Agent
2
+ from .tools import ToolRegistry
3
+ from .utils import Serializer, setup_logger
4
+
5
+ __version__ = "1.0.0"
6
+ __author__ = "ProgVM"
@@ -0,0 +1,119 @@
1
+ import argparse
2
+ import os
3
+ import json
4
+ import logging
5
+ from .utils import Colors, setup_logger, smart_sleep, Serializer
6
+ from .core import SwarmSession
7
+
8
+ def parse_args():
9
+ parser = argparse.ArgumentParser(description="Swarm Framework")
10
+ parser.add_argument("--agents_count", type=int, default=2)
11
+ parser.add_argument("--model", default="gemini-3.1-flash-lite")
12
+ parser.add_argument("--first_msg", default="Initialize Swarm and greet the user.")
13
+ parser.add_argument("--keys", nargs="+")
14
+ parser.add_argument("--config", help="Path to config JSON")
15
+ parser.add_argument("--load", help="Load session JSON")
16
+ parser.add_argument("--log_level", default="INFO")
17
+ parser.add_argument("--no_pause", action="store_true")
18
+ parser.add_argument("--save_file", default="swarm_session.json")
19
+
20
+ # Global tool settings
21
+ parser.add_argument("--max_results", type=int, default=5)
22
+ parser.add_argument("--cmd_timeout", type=int, default=300)
23
+ parser.add_argument("--cmd_blacklist", nargs="*", default=[])
24
+ parser.add_argument("--file_blacklist", nargs="*", default=[])
25
+
26
+ # Defaults
27
+ parser.add_argument("--sys1", default="You are Agent 1.")
28
+ parser.add_argument("--sys2", default="You are Agent 2.")
29
+ parser.add_argument("--temp", type=float, default=0.7)
30
+
31
+ return parser.parse_known_args()
32
+
33
+ def run():
34
+ args, unknown = parse_args()
35
+ logger = setup_logger(args.log_level)
36
+
37
+ # 1. Load config if exists (Overrides defaults)
38
+ if args.config and os.path.exists(args.config):
39
+ with open(args.config, 'r') as f:
40
+ cdata = json.load(f)
41
+ for k, v in cdata.items(): setattr(args, k, v)
42
+
43
+ # 2. Collect API Keys
44
+ keys = args.keys or [os.getenv("GOOGLE_API_KEY")]
45
+ if not keys or not keys[0]:
46
+ print(f"{Colors.ERR}Error: API Keys not found. Set GOOGLE_API_KEY environment variable or use --keys.{Colors.RESET}")
47
+ return
48
+
49
+ session = SwarmSession(args, keys)
50
+
51
+ # 3. Handle loading (Arguments take priority after load)
52
+ if args.load and os.path.exists(args.load):
53
+ with open(args.load, 'r') as f:
54
+ sd = json.load(f)
55
+ session.current_agent_idx = sd.get("current_agent", 0)
56
+ session.last_interaction = sd.get("last_interaction", args.first_msg)
57
+ for i, ah in enumerate(sd.get("histories", [])):
58
+ if i < len(session.agents):
59
+ session.agents[i].history = Serializer.deserialize_history(ah)
60
+ print(f"{Colors.SYS}Session loaded. Overriding with current CLI parameters...{Colors.RESET}")
61
+
62
+ print(f"{Colors.SYS}{Colors.BOLD}>>> SWARM STARTING. CTRL+C FOR COMMAND CENTER.{Colors.RESET}")
63
+
64
+ while True:
65
+ try:
66
+ agent = session.agents[session.current_agent_idx]
67
+ color = Colors.AI_COLORS[session.current_agent_idx % len(Colors.AI_COLORS)]
68
+
69
+ # 1-Agent User Input Mode
70
+ if args.agents_count == 1 and len(agent.history) > 0:
71
+ user_msg = input(f"{Colors.BOLD}User: {Colors.RESET}")
72
+ session.last_interaction = user_msg
73
+
74
+ # Agent Thinking
75
+ response, reports = session.execute_react_step()
76
+
77
+ # Print and Share Reports
78
+ for rep in reports:
79
+ print(f"{Colors.REPORT}{rep}{Colors.RESET}")
80
+ for i, other in enumerate(session.agents):
81
+ if i != session.current_agent_idx:
82
+ other.history.append(types.Content(role="user", parts=[types.Part(text=rep)]))
83
+
84
+ # Final Output
85
+ print(f"{color}{Colors.BOLD}{agent.name}:{Colors.RESET} {response}")
86
+
87
+ # Turn Management
88
+ if not session.turn_passed_manually and args.agents_count > 1:
89
+ session.current_agent_idx = (session.current_agent_idx + 1) % args.agents_count
90
+
91
+ # Timing
92
+ wait = (len(response) / 10) * 1.5
93
+ smart_sleep(max(3, min(wait, 30)), enabled=session.enable_pauses)
94
+
95
+ except KeyboardInterrupt:
96
+ print(f"\n{Colors.MENU}{Colors.BOLD}=== SWARM COMMAND CENTER ==={Colors.RESET}")
97
+ print("1. Change Keys\n2. Toggle Pauses\n3. Save State\n4. Change Log Level\n5. Exit Swarm")
98
+ choice = input("Choice: ")
99
+
100
+ if choice == '1':
101
+ nk = input("Enter keys (space separated): ").split()
102
+ if nk: session.keys = nk; session.key_idx = 0
103
+ elif choice == '2':
104
+ session.enable_pauses = not session.enable_pauses
105
+ print(f"Pauses enabled: {session.enable_pauses}")
106
+ elif choice == '3':
107
+ path = input(f"Save filename [{args.save_file}]: ") or args.save_file
108
+ save_data = {
109
+ "current_agent": session.current_agent_idx,
110
+ "last_interaction": session.last_interaction,
111
+ "histories": [Serializer.serialize_history(a.history) for a in session.agents]
112
+ }
113
+ with open(path, 'w') as f: json.dump(save_data, f, indent=2)
114
+ print(f"State saved to {path}")
115
+ elif choice == '4':
116
+ lv = input("Enter Level (DEBUG, INFO, WARNING): ").upper()
117
+ logger.setLevel(getattr(logging, lv, logging.INFO))
118
+ elif choice == '5':
119
+ break
@@ -0,0 +1,167 @@
1
+ import logging
2
+ from google import genai
3
+ from google.genai import types
4
+ from .utils import Colors, Serializer
5
+ from .tools import ToolRegistry
6
+
7
+ logger = logging.getLogger("Swarm.Core")
8
+
9
+ class Agent:
10
+ """Individual Agent state and configuration."""
11
+ def __init__(self, agent_id, name, description, config):
12
+ self.id = agent_id
13
+ self.name = name
14
+ self.description = description
15
+ self.model = config.get('model', 'gemini-3.1-flash-lite')
16
+ self.sys_prompt = config.get('sys_prompt', 'You are a helpful agent.')
17
+ self.temperature = config.get('temp', 0.7)
18
+ self.history = []
19
+
20
+ # Tool specifics for this agent
21
+ self.tools_enabled = config.get('tools', ["web_search", "shell_exec", "upload_file", "pass_turn"])
22
+ self.max_search = config.get('max_search', 5)
23
+ self.cmd_timeout = config.get('cmd_timeout', 300)
24
+ self.cmd_blacklist = config.get('cmd_blacklist', [])
25
+ self.file_blacklist = config.get('file_blacklist', [])
26
+
27
+ class SwarmSession:
28
+ """Manager of the multi-agent ReAct loop."""
29
+ def __init__(self, args, keys):
30
+ self.args = args
31
+ self.keys = keys
32
+ self.key_idx = 0
33
+ self.client = genai.Client(api_key=self.keys[self.key_idx])
34
+ self.agents = []
35
+ self.current_agent_idx = 0
36
+ self.last_interaction = args.first_msg
37
+ self.enable_pauses = not args.no_pause
38
+ self.turn_passed_manually = False
39
+ self._init_agents(args)
40
+
41
+ def _init_agents(self, args):
42
+ for i in range(args.agents_count):
43
+ p = f"ai{i+1}_"
44
+ # Get values with fallbacks to global args
45
+ config = {
46
+ 'model': getattr(args, f"{p}model", args.model),
47
+ 'sys_prompt': getattr(args, f"{p}sys", args.sys1 if i == 0 else args.sys2),
48
+ 'temp': getattr(args, f"{p}temp", args.temp),
49
+ 'tools': getattr(args, f"{p}tools", ["web_search", "shell_exec", "upload_file", "pass_turn"]),
50
+ 'max_search': getattr(args, f"{p}max_search", args.max_results),
51
+ 'cmd_timeout': getattr(args, f"{p}cmd_timeout", args.cmd_timeout),
52
+ 'cmd_blacklist': getattr(args, f"{p}cmd_blacklist", args.cmd_blacklist),
53
+ 'file_blacklist': getattr(args, f"{p}file_blacklist", args.file_blacklist),
54
+ }
55
+ name = getattr(args, f"{p}name", f"Agent_{i+1}")
56
+ desc = getattr(args, f"{p}desc", "A Swarm Agent.")
57
+ self.agents.append(Agent(i+1, name, desc, config))
58
+
59
+ def rotate_key(self):
60
+ if len(self.keys) > 1:
61
+ self.key_idx = (self.key_idx + 1) % len(self.keys)
62
+ self.client = genai.Client(api_key=self.keys[self.key_idx])
63
+ logger.info(f"API key rotated to index {self.key_idx}")
64
+ return True
65
+ return False
66
+
67
+ def build_system_instruction(self, agent):
68
+ """Constructs the root prompt for the agent."""
69
+ swarm_map = "\n".join([f"- {a.name} (ID: {a.id}): {a.description}" for a in self.agents])
70
+ root = (
71
+ f"{agent.sys_prompt}\n\n"
72
+ f"FRAMEWORK: SWARM\n"
73
+ f"YOUR IDENTITY: {agent.name}\n"
74
+ f"ALL AGENTS IN SWARM:\n{swarm_map}\n\n"
75
+ f"GUIDELINES:\n"
76
+ f"1. You are autonomous. Use tools as needed.\n"
77
+ )
78
+ if len(self.agents) > 1:
79
+ root += "2. You can use 'pass_turn' to give control to another agent by their name.\n"
80
+ else:
81
+ root += "2. You are talking directly to the User.\n"
82
+ return root
83
+
84
+ def get_tool_definitions(self, agent):
85
+ decls = []
86
+ if "web_search" in agent.tools_enabled:
87
+ decls.append({"name": "web_search", "description": "Search the web.", "parameters": {"type": "OBJECT", "properties": {"query": {"type": "STRING"}}, "required": ["query"]}})
88
+ if "shell_exec" in agent.tools_enabled:
89
+ decls.append({"name": "shell_exec", "description": "Execute terminal commands.", "parameters": {"type": "OBJECT", "properties": {"command": {"type": "STRING"}}, "required": ["command"]}})
90
+ if "upload_file" in agent.tools_enabled:
91
+ decls.append({"name": "upload_file", "description": "Upload a local file for analysis.", "parameters": {"type": "OBJECT", "properties": {"path": {"type": "STRING"}}, "required": ["path"]}})
92
+ if "pass_turn" in agent.tools_enabled and len(self.agents) > 1:
93
+ decls.append({"name": "pass_turn", "description": "Transfer turn to another agent.", "parameters": {"type": "OBJECT", "properties": {"agent_name": {"type": "STRING"}}, "required": ["agent_name"]}})
94
+ return [{"function_declarations": decls}] if decls else None
95
+
96
+ def execute_react_step(self):
97
+ """Runs the ReAct loop for the current agent."""
98
+ agent = self.agents[self.current_agent_idx]
99
+ agent.history.append(types.Content(role="user", parts=[types.Part(text=self.last_interaction)]))
100
+
101
+ full_text_output = ""
102
+ reports = []
103
+ self.turn_passed_manually = False
104
+
105
+ while True:
106
+ try:
107
+ config = types.GenerateContentConfig(
108
+ system_instruction=self.build_system_instruction(agent),
109
+ tools=self.get_tool_definitions(agent),
110
+ temperature=agent.temperature,
111
+ safety_settings=[types.SafetySetting(category=c, threshold="BLOCK_NONE") for c in [
112
+ "HARM_CATEGORY_HARASSMENT", "HARM_CATEGORY_HATE_SPEECH",
113
+ "HARM_CATEGORY_SEXUALLY_EXPLICIT", "HARM_CATEGORY_DANGEROUS_CONTENT"]]
114
+ )
115
+
116
+ response = self.client.models.generate_content(model=agent.model, config=config, contents=agent.history)
117
+
118
+ if not response.candidates:
119
+ return "[Blocked by Safety Filters]", []
120
+
121
+ candidate = response.candidates[0]
122
+ chunk_text = "".join([p.text for p in candidate.content.parts if p.text])
123
+ full_text_output += chunk_text
124
+
125
+ calls = [p.function_call for p in candidate.content.parts if p.function_call]
126
+
127
+ if not calls:
128
+ agent.history.append(candidate.content)
129
+ self.last_interaction = full_text_output
130
+ return full_text_output, reports
131
+
132
+ agent.history.append(candidate.content)
133
+ res_parts = []
134
+
135
+ for call in calls:
136
+ fn, args = call.name, call.args
137
+ logger.info(f"Agent {agent.name} calls tool: {fn}")
138
+
139
+ if fn == "pass_turn":
140
+ target = args.get("agent_name")
141
+ for i, a in enumerate(self.agents):
142
+ if a.name == target:
143
+ self.current_agent_idx = i
144
+ self.turn_passed_manually = True
145
+ res = f"Turn successfully passed to {target}."
146
+ break
147
+ else: res = f"Error: Agent '{target}' not found in Swarm."
148
+ elif fn == "web_search":
149
+ res = ToolRegistry.web_search(args.get("query"), max_results=agent.max_search)
150
+ elif fn == "shell_exec":
151
+ res = ToolRegistry.shell_exec(args.get("command"), timeout=agent.cmd_timeout, blacklist=agent.cmd_blacklist)
152
+ elif fn == "upload_file":
153
+ up = ToolRegistry.upload_file(self.client, args.get("path"), blacklist=agent.file_blacklist)
154
+ if "uri" in up:
155
+ res = f"File {args.get('path')} uploaded to {up['uri']}"
156
+ agent.history.append(types.Content(role="user", parts=[types.Part(file_data=types.FileData(file_uri=up['uri'], mime_type=up['mime']))]))
157
+ else: res = f"File Upload Error: {up.get('error')}"
158
+
159
+ res_parts.append(types.Part.from_function_response(name=fn, response={"result": res}))
160
+ reports.append(f"Tool Report: {agent.name} -> {fn}. Result: {str(res)[:150]}...")
161
+
162
+ agent.history.append(types.Content(role="tool", parts=res_parts))
163
+
164
+ except Exception as e:
165
+ if "429" in str(e) and self.rotate_key(): continue
166
+ logger.error(f"Cycle Exception: {e}")
167
+ return f"Error during agent cycle: {e}", []
@@ -0,0 +1,76 @@
1
+ import subprocess
2
+ import json
3
+ import re
4
+ import time
5
+ import logging
6
+ from ddgs import DDGS
7
+
8
+ logger = logging.getLogger("Swarm.Tools")
9
+
10
+ class ToolRegistry:
11
+ """Static container for all tool logic with built-in security and limits."""
12
+
13
+ @staticmethod
14
+ def web_search(query, max_results=5):
15
+ """Standard web search tool."""
16
+ logger.debug(f"Searching for: {query} (limit: {max_results})")
17
+ try:
18
+ with DDGS() as ddgs:
19
+ results = [r for r in ddgs.text(query, max_results=max_results)]
20
+ if not results:
21
+ return "No search results found."
22
+ return json.dumps(results, ensure_ascii=False, indent=2)
23
+ except Exception as e:
24
+ logger.error(f"DDGS Error: {e}")
25
+ return f"Search failed: {str(e)}"
26
+
27
+ @staticmethod
28
+ def shell_exec(command, timeout=300, blacklist=None):
29
+ """Secure shell execution tool."""
30
+ logger.debug(f"Executing: {command} (timeout: {timeout})")
31
+ if blacklist:
32
+ for pattern in blacklist:
33
+ if re.search(pattern, command, re.IGNORECASE):
34
+ logger.warning(f"Blocked command: {command} (matches {pattern})")
35
+ return f"Security Exception: Command matches blacklist pattern '{pattern}'."
36
+
37
+ try:
38
+ res = subprocess.run(
39
+ command, shell=True, capture_output=True, text=True, timeout=timeout
40
+ )
41
+ output = f"STDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"
42
+ return output if output.strip() else "Executed successfully with no output."
43
+ except subprocess.TimeoutExpired:
44
+ return f"Timeout Error: Command exceeded {timeout} seconds."
45
+ except Exception as e:
46
+ return f"Execution failed: {str(e)}"
47
+
48
+ @staticmethod
49
+ def upload_file(client, path, blacklist=None):
50
+ """Google Files API wrapper."""
51
+ if not os.path.exists(path):
52
+ return {"error": f"Path '{path}' does not exist."}
53
+
54
+ if blacklist:
55
+ for pattern in blacklist:
56
+ if re.search(pattern, path, re.IGNORECASE):
57
+ return {"error": f"Access to '{path}' is denied by security policy."}
58
+
59
+ logger.info(f"Uploading file: {path}")
60
+ try:
61
+ file_obj = client.files.upload(path=path)
62
+ while file_obj.state.name == "PROCESSING":
63
+ time.sleep(2)
64
+ file_obj = client.files.get(name=file_obj.name)
65
+
66
+ if file_obj.state.name == "FAILED":
67
+ return {"error": "Server-side file processing failed."}
68
+
69
+ return {
70
+ "uri": file_obj.uri,
71
+ "mime": file_obj.mime_type,
72
+ "name": file_obj.name
73
+ }
74
+ except Exception as e:
75
+ logger.error(f"Upload error: {e}")
76
+ return {"error": str(e)}
@@ -0,0 +1,82 @@
1
+ import os
2
+ import sys
3
+ import json
4
+ import time
5
+ import select
6
+ import logging
7
+ from google.genai import types
8
+
9
+ class Colors:
10
+ """ANSI Escape sequences for professional terminal output."""
11
+ AI_COLORS = ['\033[94m', '\033[92m', '\033[96m', '\033[95m', '\033[91m', '\033[33m']
12
+ SYS = '\033[93m'
13
+ ERR = '\033[91m'
14
+ TOOL = '\033[36m'
15
+ REPORT = '\033[90m'
16
+ MENU = '\033[95m'
17
+ BOLD = '\033[1m'
18
+ RESET = '\033[0m'
19
+
20
+ def setup_logger(level_name="INFO"):
21
+ """Initializes the logging system with configurable levels."""
22
+ level = getattr(logging, level_name.upper(), logging.INFO)
23
+ logging.basicConfig(
24
+ level=level,
25
+ format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
26
+ handlers=[logging.StreamHandler(sys.stdout)]
27
+ )
28
+ return logging.getLogger("Swarm")
29
+
30
+ def smart_sleep(timeout, enabled=True):
31
+ """Provides a toggleable pause that can be skipped with the Enter key."""
32
+ if not enabled or timeout <= 0:
33
+ return
34
+ print(f"{Colors.REPORT}[Pause: {timeout:.1f}s | Press Enter to skip]{Colors.RESET}", end="", flush=True)
35
+ # Use select for non-blocking stdin check
36
+ ready, _, _ = select.select([sys.stdin], [], [], timeout)
37
+ if ready:
38
+ sys.stdin.readline()
39
+ print(f"\r{Colors.SYS}[User Skipped Pause]{' ' * 30}{Colors.RESET}")
40
+ else:
41
+ # Clear the line after pause expires
42
+ print(f"\r{' ' * 50}\r", end="", flush=True)
43
+
44
+ class Serializer:
45
+ """Serializes and deserializes Google GenAI objects for persistent storage."""
46
+ @staticmethod
47
+ def serialize_history(history):
48
+ data = []
49
+ for content in history:
50
+ parts_list = []
51
+ for p in content.parts:
52
+ if p.text:
53
+ parts_list.append({"text": p.text})
54
+ elif p.function_call:
55
+ parts_list.append({"function_call": {"name": p.function_call.name, "args": p.function_call.args}})
56
+ elif p.function_response:
57
+ parts_list.append({"function_response": {"name": p.function_response.name, "response": p.function_response.response}})
58
+ elif p.file_data:
59
+ parts_list.append({"file_data": {"file_uri": p.file_data.file_uri, "mime_type": p.file_data.mime_type}})
60
+ data.append({"role": content.role, "parts": parts_list})
61
+ return data
62
+
63
+ @staticmethod
64
+ def deserialize_history(data):
65
+ history = []
66
+ for item in data:
67
+ parts = []
68
+ for p in item.get('parts', []):
69
+ if "text" in p:
70
+ parts.append(types.Part(text=p["text"]))
71
+ elif "function_call" in p:
72
+ fc = p["function_call"]
73
+ parts.append(types.Part(function_call=types.FunctionCall(name=fc["name"], args=fc["args"])))
74
+ elif "function_response" in p:
75
+ fr = p["function_response"]
76
+ parts.append(types.Part.from_function_response(name=fr["name"], response=fr["response"]))
77
+ elif "file_data" in p:
78
+ fd = p["file_data"]
79
+ parts.append(types.Part(file_data=types.FileData(file_uri=fd["file_uri"], mime_type=fd["mime_type"])))
80
+ if parts:
81
+ history.append(types.Content(role=item["role"], parts=parts))
82
+ return history
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.4
2
+ Name: swarmai
3
+ Version: 1.0.0
4
+ Summary: A highly flexible multi-agent ReAct framework powered by Google Gemini
5
+ Author-email: ProgVM <progvminc@gmail.com>
6
+ Project-URL: Homepage, https://github.com/ProgVM
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: google-genai
13
+ Requires-Dist: duckduckgo-search
14
+
15
+ # 🐝 Swarm
16
+
17
+ **Swarm** is an advanced, open-source multi-agent intelligence framework designed for autonomous collaboration. Powered by Google's **Gemini 3.1 Flash Lite**, Swarm allows multiple agents to interact, use tools, execute code, and perform web searches in a shared environment.
18
+
19
+ ## 🚀 Key Features
20
+
21
+ - **N-Agent Collaboration**: Run any number of agents in a single session.
22
+ - **Autonomous Tool Usage**: Agents can perform Google searches and execute Shell commands.
23
+ - **Agent Sandboxing**: Per-agent blacklists for terminal commands and file paths.
24
+ - **Turn Management**: Agents can autonomously pass turns to specific peers using `pass_turn`.
25
+ - **ReAct Loop**: High-level reasoning before acting, supporting multi-step tool execution.
26
+ - **Files API Support**: Agents can upload and analyze PDF, images, and logs via Google Files API.
27
+ - **Persistent Sessions**: Save and load full agent histories and environment states.
28
+
29
+ ## 🛠 Installation
30
+
31
+ ```bash
32
+ # Clone the repository
33
+ git clone https://github.com/ProgVM/swarm
34
+ cd swarm
35
+
36
+ # Install dependencies
37
+ pip install .
38
+ ```
39
+
40
+ Or:
41
+ ```bash
42
+ pip install swarmai
43
+ ```
44
+
45
+ ## 💻 CLI Usage
46
+
47
+ Start a basic conversation with two agents:
48
+ ```bash
49
+ swarm --keys YOUR_API_KEY --agents_count 2
50
+ ```
51
+
52
+ Start an autonomous coding session:
53
+ ```bash
54
+ swarm --first_msg "Write a web scraper in Python and test it" \
55
+ --sys1 "You are an Expert Coder" \
56
+ --sys2 "You are a Security Auditor" \
57
+ --cmd_timeout 600
58
+ ```
59
+
60
+ ### Advanced Configuration
61
+ Swarm supports individual parameters for every agent using `aiN_` prefixes:
62
+ - `--ai1_name "Architect"`
63
+ - `--ai1_model "gemini-2.0-flash"`
64
+ - `--ai2_name "Tester"`
65
+ - `--ai2_temp 0.2`
66
+
67
+ ## ⚙️ Configuration File
68
+ You can use a JSON config to manage complex Swarms:
69
+ ```json
70
+ {
71
+ "agents_count": 3,
72
+ "model": "gemini-3.1-flash-lite",
73
+ "cmd_blacklist": ["rm -rf", "format"],
74
+ "no_pause": true
75
+ }
76
+ ```
77
+ Run with: `swarm --config my_swarm.json`
78
+
79
+ ## ⌨️ Command Center (Interactive Menu)
80
+ Press `Ctrl+C` at any time to:
81
+ - Rotate API Keys.
82
+ - Toggle reading pauses.
83
+ - Save the current state.
84
+ - Change logging levels (DEBUG, INFO, WARNING).
85
+ - Inject manual directives into agents' minds.
86
+
87
+ ## 🤝 Contributing
88
+ Developed by **ProgVM**.
89
+ - Email: progvminc@gmail.com
90
+ - GitHub: [https://github.com/ProgVM](https://github.com/ProgVM)
91
+
92
+ License: MIT
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ swarm/__init__.py
4
+ swarm/cli.py
5
+ swarm/core.py
6
+ swarm/tools.py
7
+ swarm/utils.py
8
+ swarmai.egg-info/PKG-INFO
9
+ swarmai.egg-info/SOURCES.txt
10
+ swarmai.egg-info/dependency_links.txt
11
+ swarmai.egg-info/entry_points.txt
12
+ swarmai.egg-info/requires.txt
13
+ swarmai.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ swarm = swarm.cli:run
@@ -0,0 +1,2 @@
1
+ google-genai
2
+ duckduckgo-search
@@ -0,0 +1 @@
1
+ swarm