swarms 7.8.4__py3-none-any.whl → 7.8.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swarms/agents/ape_agent.py +5 -22
- swarms/agents/consistency_agent.py +1 -1
- swarms/agents/i_agent.py +1 -1
- swarms/agents/reasoning_agents.py +99 -3
- swarms/agents/reasoning_duo.py +1 -1
- swarms/cli/main.py +1 -1
- swarms/communication/__init__.py +1 -0
- swarms/communication/duckdb_wrap.py +32 -2
- swarms/communication/pulsar_struct.py +45 -19
- swarms/communication/redis_wrap.py +56 -11
- swarms/communication/supabase_wrap.py +1659 -0
- swarms/prompts/prompt.py +0 -3
- swarms/schemas/agent_completion_response.py +71 -0
- swarms/schemas/agent_rag_schema.py +7 -0
- swarms/schemas/conversation_schema.py +9 -0
- swarms/schemas/llm_agent_schema.py +99 -81
- swarms/schemas/swarms_api_schemas.py +164 -0
- swarms/structs/__init__.py +14 -11
- swarms/structs/agent.py +219 -199
- swarms/structs/agent_rag_handler.py +685 -0
- swarms/structs/base_swarm.py +2 -1
- swarms/structs/conversation.py +608 -87
- swarms/structs/csv_to_agent.py +153 -100
- swarms/structs/deep_research_swarm.py +197 -193
- swarms/structs/dynamic_conversational_swarm.py +18 -7
- swarms/structs/hiearchical_swarm.py +1 -1
- swarms/structs/hybrid_hiearchical_peer_swarm.py +2 -18
- swarms/structs/image_batch_processor.py +261 -0
- swarms/structs/interactive_groupchat.py +356 -0
- swarms/structs/ma_blocks.py +75 -0
- swarms/structs/majority_voting.py +1 -1
- swarms/structs/mixture_of_agents.py +1 -1
- swarms/structs/multi_agent_router.py +3 -2
- swarms/structs/rearrange.py +3 -3
- swarms/structs/sequential_workflow.py +3 -3
- swarms/structs/swarm_matcher.py +500 -411
- swarms/structs/swarm_router.py +15 -97
- swarms/structs/swarming_architectures.py +1 -1
- swarms/tools/mcp_client_call.py +3 -0
- swarms/utils/__init__.py +10 -2
- swarms/utils/check_all_model_max_tokens.py +43 -0
- swarms/utils/generate_keys.py +0 -27
- swarms/utils/history_output_formatter.py +5 -20
- swarms/utils/litellm_wrapper.py +208 -60
- swarms/utils/output_types.py +24 -0
- swarms/utils/vllm_wrapper.py +5 -6
- swarms/utils/xml_utils.py +37 -2
- {swarms-7.8.4.dist-info → swarms-7.8.7.dist-info}/METADATA +31 -55
- {swarms-7.8.4.dist-info → swarms-7.8.7.dist-info}/RECORD +53 -48
- swarms/structs/multi_agent_collab.py +0 -242
- swarms/structs/output_types.py +0 -6
- swarms/utils/markdown_message.py +0 -21
- swarms/utils/visualizer.py +0 -510
- swarms/utils/wrapper_clusterop.py +0 -127
- /swarms/{tools → schemas}/tool_schema_base_model.py +0 -0
- {swarms-7.8.4.dist-info → swarms-7.8.7.dist-info}/LICENSE +0 -0
- {swarms-7.8.4.dist-info → swarms-7.8.7.dist-info}/WHEEL +0 -0
- {swarms-7.8.4.dist-info → swarms-7.8.7.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,261 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import Any, Callable, Dict, List, Optional, Union
|
6
|
+
|
7
|
+
from loguru import logger
|
8
|
+
|
9
|
+
from swarms.structs import Agent
|
10
|
+
|
11
|
+
|
12
|
+
class ImageProcessingError(Exception):
|
13
|
+
"""Custom exception for image processing errors."""
|
14
|
+
|
15
|
+
pass
|
16
|
+
|
17
|
+
|
18
|
+
class InvalidAgentError(Exception):
|
19
|
+
"""Custom exception for invalid agent configurations."""
|
20
|
+
|
21
|
+
pass
|
22
|
+
|
23
|
+
|
24
|
+
class ImageAgentBatchProcessor:
|
25
|
+
"""
|
26
|
+
A class for processing multiple images in parallel using one or more agents.
|
27
|
+
|
28
|
+
This processor can:
|
29
|
+
- Handle multiple images from a directory
|
30
|
+
- Process images with single or multiple agents
|
31
|
+
- Execute tasks in parallel
|
32
|
+
- Provide detailed logging and error handling
|
33
|
+
|
34
|
+
Attributes:
|
35
|
+
agents (List[Agent]): List of agents to process images
|
36
|
+
max_workers (int): Maximum number of parallel workers
|
37
|
+
supported_formats (set): Set of supported image formats
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
agents: Union[Agent, List[Agent], Callable, List[Callable]],
|
43
|
+
max_workers: int = None,
|
44
|
+
supported_formats: Optional[List[str]] = None,
|
45
|
+
):
|
46
|
+
"""
|
47
|
+
Initialize the ImageBatchProcessor.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
agents: Single agent or list of agents to process images
|
51
|
+
max_workers: Maximum number of parallel workers (default: 4)
|
52
|
+
supported_formats: List of supported image formats (default: ['.jpg', '.jpeg', '.png'])
|
53
|
+
|
54
|
+
Raises:
|
55
|
+
InvalidAgentError: If agents parameter is invalid
|
56
|
+
"""
|
57
|
+
self.agents = agents
|
58
|
+
self.max_workers = max_workers
|
59
|
+
self.supported_formats = supported_formats
|
60
|
+
|
61
|
+
self.agents = (
|
62
|
+
[agents] if isinstance(agents, Agent) else agents
|
63
|
+
)
|
64
|
+
if not self.agents:
|
65
|
+
raise InvalidAgentError(
|
66
|
+
"At least one agent must be provided"
|
67
|
+
)
|
68
|
+
|
69
|
+
# Get 95% of the total number of cores
|
70
|
+
self.max_workers = int(os.cpu_count() * 0.95)
|
71
|
+
|
72
|
+
self.supported_formats = set(
|
73
|
+
supported_formats or [".jpg", ".jpeg", ".png"]
|
74
|
+
)
|
75
|
+
|
76
|
+
# Setup logging
|
77
|
+
logger.add(
|
78
|
+
"image_processor.log",
|
79
|
+
rotation="100 MB",
|
80
|
+
retention="10 days",
|
81
|
+
level="INFO",
|
82
|
+
)
|
83
|
+
|
84
|
+
def _validate_image_path(
|
85
|
+
self, image_path: Union[str, Path]
|
86
|
+
) -> Path:
|
87
|
+
"""
|
88
|
+
Validate if the image path exists and has supported format.
|
89
|
+
|
90
|
+
Args:
|
91
|
+
image_path: Path to the image file
|
92
|
+
|
93
|
+
Returns:
|
94
|
+
Path: Validated Path object
|
95
|
+
|
96
|
+
Raises:
|
97
|
+
ImageProcessingError: If path is invalid or format not supported
|
98
|
+
"""
|
99
|
+
path = Path(image_path)
|
100
|
+
if not path.exists():
|
101
|
+
raise ImageProcessingError(
|
102
|
+
f"Image path does not exist: {path}"
|
103
|
+
)
|
104
|
+
if path.suffix.lower() not in self.supported_formats:
|
105
|
+
raise ImageProcessingError(
|
106
|
+
f"Unsupported image format {path.suffix}. Supported formats: {self.supported_formats}"
|
107
|
+
)
|
108
|
+
return path
|
109
|
+
|
110
|
+
def _process_single_image(
|
111
|
+
self,
|
112
|
+
image_path: Path,
|
113
|
+
tasks: Union[str, List[str]],
|
114
|
+
agent: Agent,
|
115
|
+
) -> Dict[str, Any]:
|
116
|
+
"""
|
117
|
+
Process a single image with one agent and one or more tasks.
|
118
|
+
|
119
|
+
Args:
|
120
|
+
image_path: Path to the image
|
121
|
+
tasks: Single task or list of tasks to perform
|
122
|
+
agent: Agent to process the image
|
123
|
+
|
124
|
+
Returns:
|
125
|
+
Dict containing results for each task
|
126
|
+
"""
|
127
|
+
try:
|
128
|
+
tasks_list = [tasks] if isinstance(tasks, str) else tasks
|
129
|
+
results = {}
|
130
|
+
|
131
|
+
logger.info(
|
132
|
+
f"Processing image {image_path} with agent {agent.__class__.__name__}"
|
133
|
+
)
|
134
|
+
start_time = time.time()
|
135
|
+
|
136
|
+
for task in tasks_list:
|
137
|
+
try:
|
138
|
+
result = agent.run(task=task, img=str(image_path))
|
139
|
+
results[task] = result
|
140
|
+
except Exception as e:
|
141
|
+
logger.error(
|
142
|
+
f"Error processing task '{task}' for image {image_path}: {str(e)}"
|
143
|
+
)
|
144
|
+
results[task] = f"Error: {str(e)}"
|
145
|
+
|
146
|
+
processing_time = time.time() - start_time
|
147
|
+
logger.info(
|
148
|
+
f"Completed processing {image_path} in {processing_time:.2f} seconds"
|
149
|
+
)
|
150
|
+
|
151
|
+
return {
|
152
|
+
"image_path": str(image_path),
|
153
|
+
"results": results,
|
154
|
+
"processing_time": processing_time,
|
155
|
+
}
|
156
|
+
|
157
|
+
except Exception as e:
|
158
|
+
logger.error(
|
159
|
+
f"Failed to process image {image_path}: {str(e)}"
|
160
|
+
)
|
161
|
+
raise ImageProcessingError(
|
162
|
+
f"Failed to process image {image_path}: {str(e)}"
|
163
|
+
)
|
164
|
+
|
165
|
+
def run(
|
166
|
+
self,
|
167
|
+
image_paths: Union[str, List[str], Path],
|
168
|
+
tasks: Union[str, List[str]],
|
169
|
+
) -> List[Dict[str, Any]]:
|
170
|
+
"""
|
171
|
+
Process multiple images in parallel with the configured agents.
|
172
|
+
|
173
|
+
Args:
|
174
|
+
image_paths: Single image path or list of image paths or directory path
|
175
|
+
tasks: Single task or list of tasks to perform on each image
|
176
|
+
|
177
|
+
Returns:
|
178
|
+
List of dictionaries containing results for each image
|
179
|
+
|
180
|
+
Raises:
|
181
|
+
ImageProcessingError: If any image processing fails
|
182
|
+
"""
|
183
|
+
# Handle directory input
|
184
|
+
if (
|
185
|
+
isinstance(image_paths, (str, Path))
|
186
|
+
and Path(image_paths).is_dir()
|
187
|
+
):
|
188
|
+
image_paths = [
|
189
|
+
os.path.join(image_paths, f)
|
190
|
+
for f in os.listdir(image_paths)
|
191
|
+
if Path(os.path.join(image_paths, f)).suffix.lower()
|
192
|
+
in self.supported_formats
|
193
|
+
]
|
194
|
+
elif isinstance(image_paths, (str, Path)):
|
195
|
+
image_paths = [image_paths]
|
196
|
+
|
197
|
+
# Validate all paths
|
198
|
+
validated_paths = [
|
199
|
+
self._validate_image_path(path) for path in image_paths
|
200
|
+
]
|
201
|
+
|
202
|
+
if not validated_paths:
|
203
|
+
logger.warning("No valid images found to process")
|
204
|
+
return []
|
205
|
+
|
206
|
+
logger.info(
|
207
|
+
f"Starting batch processing of {len(validated_paths)} images"
|
208
|
+
)
|
209
|
+
results = []
|
210
|
+
|
211
|
+
with ThreadPoolExecutor(
|
212
|
+
max_workers=self.max_workers
|
213
|
+
) as executor:
|
214
|
+
future_to_path = {}
|
215
|
+
|
216
|
+
# Submit all tasks
|
217
|
+
for path in validated_paths:
|
218
|
+
for agent in self.agents:
|
219
|
+
future = executor.submit(
|
220
|
+
self._process_single_image, path, tasks, agent
|
221
|
+
)
|
222
|
+
future_to_path[future] = (path, agent)
|
223
|
+
|
224
|
+
# Collect results as they complete
|
225
|
+
for future in as_completed(future_to_path):
|
226
|
+
path, agent = future_to_path[future]
|
227
|
+
try:
|
228
|
+
result = future.result()
|
229
|
+
results.append(result)
|
230
|
+
except Exception as e:
|
231
|
+
logger.error(
|
232
|
+
f"Failed to process {path} with {agent.__class__.__name__}: {str(e)}"
|
233
|
+
)
|
234
|
+
results.append(
|
235
|
+
{
|
236
|
+
"image_path": str(path),
|
237
|
+
"error": str(e),
|
238
|
+
"agent": agent.__class__.__name__,
|
239
|
+
}
|
240
|
+
)
|
241
|
+
|
242
|
+
logger.info(
|
243
|
+
f"Completed batch processing of {len(validated_paths)} images"
|
244
|
+
)
|
245
|
+
return results
|
246
|
+
|
247
|
+
def __call__(self, *args, **kwargs):
|
248
|
+
"""
|
249
|
+
Make the ImageAgentBatchProcessor callable like a function.
|
250
|
+
|
251
|
+
This allows the processor to be used directly as a function, which will
|
252
|
+
call the run() method with the provided arguments.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
*args: Variable length argument list to pass to run()
|
256
|
+
**kwargs: Arbitrary keyword arguments to pass to run()
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
The result of calling run() with the provided arguments
|
260
|
+
"""
|
261
|
+
return self.run(*args, **kwargs)
|
@@ -0,0 +1,356 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Callable, List, Union
|
3
|
+
|
4
|
+
from loguru import logger
|
5
|
+
|
6
|
+
from swarms.structs.agent import Agent
|
7
|
+
from swarms.structs.conversation import Conversation
|
8
|
+
from swarms.utils.generate_keys import generate_api_key
|
9
|
+
from swarms.utils.history_output_formatter import (
|
10
|
+
history_output_formatter,
|
11
|
+
)
|
12
|
+
|
13
|
+
|
14
|
+
class InteractiveGroupChatError(Exception):
|
15
|
+
"""Base exception class for InteractiveGroupChat errors"""
|
16
|
+
|
17
|
+
pass
|
18
|
+
|
19
|
+
|
20
|
+
class AgentNotFoundError(InteractiveGroupChatError):
|
21
|
+
"""Raised when a mentioned agent is not found in the group"""
|
22
|
+
|
23
|
+
pass
|
24
|
+
|
25
|
+
|
26
|
+
class NoMentionedAgentsError(InteractiveGroupChatError):
|
27
|
+
"""Raised when no agents are mentioned in the task"""
|
28
|
+
|
29
|
+
pass
|
30
|
+
|
31
|
+
|
32
|
+
class InvalidTaskFormatError(InteractiveGroupChatError):
|
33
|
+
"""Raised when the task format is invalid"""
|
34
|
+
|
35
|
+
pass
|
36
|
+
|
37
|
+
|
38
|
+
class InteractiveGroupChat:
|
39
|
+
"""
|
40
|
+
An interactive group chat system that enables conversations with multiple agents using @mentions.
|
41
|
+
|
42
|
+
This class allows users to interact with multiple agents by mentioning them using @agent_name syntax.
|
43
|
+
When multiple agents are mentioned, they can see and respond to each other's tasks.
|
44
|
+
|
45
|
+
Attributes:
|
46
|
+
name (str): Name of the group chat
|
47
|
+
description (str): Description of the group chat's purpose
|
48
|
+
agents (List[Union[Agent, Callable]]): List of Agent instances or callable functions
|
49
|
+
max_loops (int): Maximum number of conversation turns
|
50
|
+
conversation (Conversation): Stores the chat history
|
51
|
+
agent_map (Dict[str, Union[Agent, Callable]]): Mapping of agent names to their instances
|
52
|
+
|
53
|
+
Args:
|
54
|
+
name (str, optional): Name of the group chat. Defaults to "InteractiveGroupChat".
|
55
|
+
description (str, optional): Description of the chat. Defaults to "An interactive group chat for multiple agents".
|
56
|
+
agents (List[Union[Agent, Callable]], optional): List of participating agents or callables. Defaults to empty list.
|
57
|
+
max_loops (int, optional): Maximum conversation turns. Defaults to 1.
|
58
|
+
output_type (str, optional): Type of output format. Defaults to "string".
|
59
|
+
interactive (bool, optional): Whether to enable interactive terminal mode. Defaults to False.
|
60
|
+
|
61
|
+
Raises:
|
62
|
+
ValueError: If invalid initialization parameters are provided
|
63
|
+
"""
|
64
|
+
|
65
|
+
def __init__(
|
66
|
+
self,
|
67
|
+
id: str = generate_api_key(prefix="swarms-"),
|
68
|
+
name: str = "InteractiveGroupChat",
|
69
|
+
description: str = "An interactive group chat for multiple agents",
|
70
|
+
agents: List[Union[Agent, Callable]] = [],
|
71
|
+
max_loops: int = 1,
|
72
|
+
output_type: str = "string",
|
73
|
+
interactive: bool = False,
|
74
|
+
):
|
75
|
+
self.id = id
|
76
|
+
self.name = name
|
77
|
+
self.description = description
|
78
|
+
self.agents = agents
|
79
|
+
self.max_loops = max_loops
|
80
|
+
self.output_type = output_type
|
81
|
+
self.interactive = interactive
|
82
|
+
|
83
|
+
# Initialize conversation history
|
84
|
+
self.conversation = Conversation(time_enabled=True)
|
85
|
+
|
86
|
+
# Create a mapping of agent names to agents for easy lookup
|
87
|
+
self.agent_map = {}
|
88
|
+
for agent in agents:
|
89
|
+
if isinstance(agent, Agent):
|
90
|
+
self.agent_map[agent.agent_name] = agent
|
91
|
+
elif callable(agent):
|
92
|
+
# For callable functions, use the function name as the agent name
|
93
|
+
self.agent_map[agent.__name__] = agent
|
94
|
+
|
95
|
+
self._validate_initialization()
|
96
|
+
self._setup_conversation_context()
|
97
|
+
self._update_agent_prompts()
|
98
|
+
|
99
|
+
def _validate_initialization(self) -> None:
|
100
|
+
"""
|
101
|
+
Validates the group chat configuration.
|
102
|
+
|
103
|
+
Raises:
|
104
|
+
ValueError: If any required components are missing or invalid
|
105
|
+
"""
|
106
|
+
if len(self.agents) < 1:
|
107
|
+
raise ValueError(
|
108
|
+
"At least one agent is required for the group chat"
|
109
|
+
)
|
110
|
+
|
111
|
+
if self.max_loops <= 0:
|
112
|
+
raise ValueError("Max loops must be greater than 0")
|
113
|
+
|
114
|
+
def _setup_conversation_context(self) -> None:
|
115
|
+
"""Sets up the initial conversation context with group chat information."""
|
116
|
+
agent_info = []
|
117
|
+
for agent in self.agents:
|
118
|
+
if isinstance(agent, Agent):
|
119
|
+
agent_info.append(
|
120
|
+
f"- {agent.agent_name}: {agent.system_prompt}"
|
121
|
+
)
|
122
|
+
elif callable(agent):
|
123
|
+
agent_info.append(
|
124
|
+
f"- {agent.__name__}: Custom callable function"
|
125
|
+
)
|
126
|
+
|
127
|
+
context = (
|
128
|
+
f"Group Chat Name: {self.name}\n"
|
129
|
+
f"Description: {self.description}\n"
|
130
|
+
f"Available Agents:\n" + "\n".join(agent_info)
|
131
|
+
)
|
132
|
+
self.conversation.add(role="System", content=context)
|
133
|
+
|
134
|
+
def _update_agent_prompts(self) -> None:
|
135
|
+
"""Updates each agent's system prompt with information about other agents and the group chat."""
|
136
|
+
agent_info = []
|
137
|
+
for agent in self.agents:
|
138
|
+
if isinstance(agent, Agent):
|
139
|
+
agent_info.append(
|
140
|
+
{
|
141
|
+
"name": agent.agent_name,
|
142
|
+
"description": agent.system_prompt,
|
143
|
+
}
|
144
|
+
)
|
145
|
+
elif callable(agent):
|
146
|
+
agent_info.append(
|
147
|
+
{
|
148
|
+
"name": agent.__name__,
|
149
|
+
"description": "Custom callable function",
|
150
|
+
}
|
151
|
+
)
|
152
|
+
|
153
|
+
group_context = (
|
154
|
+
f"\n\nYou are part of a group chat named '{self.name}' with the following description: {self.description}\n"
|
155
|
+
f"Other participants in this chat:\n"
|
156
|
+
)
|
157
|
+
|
158
|
+
for agent in self.agents:
|
159
|
+
if isinstance(agent, Agent):
|
160
|
+
# Create context excluding the current agent
|
161
|
+
other_agents = [
|
162
|
+
info
|
163
|
+
for info in agent_info
|
164
|
+
if info["name"] != agent.agent_name
|
165
|
+
]
|
166
|
+
agent_context = group_context
|
167
|
+
for other in other_agents:
|
168
|
+
agent_context += (
|
169
|
+
f"- {other['name']}: {other['description']}\n"
|
170
|
+
)
|
171
|
+
|
172
|
+
# Update the agent's system prompt
|
173
|
+
agent.system_prompt = (
|
174
|
+
agent.system_prompt + agent_context
|
175
|
+
)
|
176
|
+
logger.info(
|
177
|
+
f"Updated system prompt for agent: {agent.agent_name}"
|
178
|
+
)
|
179
|
+
|
180
|
+
def _extract_mentions(self, task: str) -> List[str]:
|
181
|
+
"""
|
182
|
+
Extracts @mentions from the task.
|
183
|
+
|
184
|
+
Args:
|
185
|
+
task (str): The input task
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
List[str]: List of mentioned agent names
|
189
|
+
|
190
|
+
Raises:
|
191
|
+
InvalidtaskFormatError: If the task format is invalid
|
192
|
+
"""
|
193
|
+
try:
|
194
|
+
# Find all @mentions using regex
|
195
|
+
mentions = re.findall(r"@(\w+)", task)
|
196
|
+
return [
|
197
|
+
mention
|
198
|
+
for mention in mentions
|
199
|
+
if mention in self.agent_map
|
200
|
+
]
|
201
|
+
except Exception as e:
|
202
|
+
logger.error(f"Error extracting mentions: {e}")
|
203
|
+
raise InvalidTaskFormatError(f"Invalid task format: {e}")
|
204
|
+
|
205
|
+
def start_interactive_session(self):
|
206
|
+
"""
|
207
|
+
Start an interactive terminal session for chatting with agents.
|
208
|
+
|
209
|
+
This method creates a REPL (Read-Eval-Print Loop) that allows users to:
|
210
|
+
- Chat with agents using @mentions
|
211
|
+
- See available agents and their descriptions
|
212
|
+
- Exit the session using 'exit' or 'quit'
|
213
|
+
- Get help using 'help' or '?'
|
214
|
+
"""
|
215
|
+
if not self.interactive:
|
216
|
+
raise InteractiveGroupChatError(
|
217
|
+
"Interactive mode is not enabled. Initialize with interactive=True"
|
218
|
+
)
|
219
|
+
|
220
|
+
print(f"\nWelcome to {self.name}!")
|
221
|
+
print(f"Description: {self.description}")
|
222
|
+
print("\nAvailable agents:")
|
223
|
+
for name, agent in self.agent_map.items():
|
224
|
+
if isinstance(agent, Agent):
|
225
|
+
print(
|
226
|
+
f"- @{name}: {agent.system_prompt.splitlines()[0]}"
|
227
|
+
)
|
228
|
+
else:
|
229
|
+
print(f"- @{name}: Custom callable function")
|
230
|
+
|
231
|
+
print("\nCommands:")
|
232
|
+
print("- Type 'help' or '?' for help")
|
233
|
+
print("- Type 'exit' or 'quit' to end the session")
|
234
|
+
print("- Use @agent_name to mention agents")
|
235
|
+
print("\nStart chatting:")
|
236
|
+
|
237
|
+
while True:
|
238
|
+
try:
|
239
|
+
# Get user input
|
240
|
+
user_input = input("\nYou: ").strip()
|
241
|
+
|
242
|
+
# Handle special commands
|
243
|
+
if user_input.lower() in ["exit", "quit"]:
|
244
|
+
print("Goodbye!")
|
245
|
+
break
|
246
|
+
|
247
|
+
if user_input.lower() in ["help", "?"]:
|
248
|
+
print("\nHelp:")
|
249
|
+
print("1. Mention agents using @agent_name")
|
250
|
+
print(
|
251
|
+
"2. You can mention multiple agents in one task"
|
252
|
+
)
|
253
|
+
print("3. Available agents:")
|
254
|
+
for name in self.agent_map:
|
255
|
+
print(f" - @{name}")
|
256
|
+
print(
|
257
|
+
"4. Type 'exit' or 'quit' to end the session"
|
258
|
+
)
|
259
|
+
continue
|
260
|
+
|
261
|
+
if not user_input:
|
262
|
+
continue
|
263
|
+
|
264
|
+
# Process the task and get responses
|
265
|
+
try:
|
266
|
+
response = self.run(user_input)
|
267
|
+
print("\nChat:")
|
268
|
+
print(response)
|
269
|
+
|
270
|
+
except NoMentionedAgentsError:
|
271
|
+
print(
|
272
|
+
"\nError: Please mention at least one agent using @agent_name"
|
273
|
+
)
|
274
|
+
except AgentNotFoundError as e:
|
275
|
+
print(f"\nError: {str(e)}")
|
276
|
+
except Exception as e:
|
277
|
+
print(f"\nAn error occurred: {str(e)}")
|
278
|
+
|
279
|
+
except KeyboardInterrupt:
|
280
|
+
print("\nSession terminated by user. Goodbye!")
|
281
|
+
break
|
282
|
+
except Exception as e:
|
283
|
+
print(f"\nAn unexpected error occurred: {str(e)}")
|
284
|
+
print(
|
285
|
+
"The session will continue. You can type 'exit' to end it."
|
286
|
+
)
|
287
|
+
|
288
|
+
def run(self, task: str) -> str:
|
289
|
+
"""
|
290
|
+
Process a task and get responses from mentioned agents.
|
291
|
+
If interactive mode is enabled, this will be called by start_interactive_session().
|
292
|
+
Otherwise, it can be called directly for single task processing.
|
293
|
+
"""
|
294
|
+
try:
|
295
|
+
# Extract mentioned agents
|
296
|
+
mentioned_agents = self._extract_mentions(task)
|
297
|
+
|
298
|
+
if not mentioned_agents:
|
299
|
+
raise NoMentionedAgentsError(
|
300
|
+
"No valid agents mentioned in the task"
|
301
|
+
)
|
302
|
+
|
303
|
+
# Add user task to conversation
|
304
|
+
self.conversation.add(role="User", content=task)
|
305
|
+
|
306
|
+
# Get responses from mentioned agents
|
307
|
+
for agent_name in mentioned_agents:
|
308
|
+
agent = self.agent_map.get(agent_name)
|
309
|
+
if not agent:
|
310
|
+
raise AgentNotFoundError(
|
311
|
+
f"Agent '{agent_name}' not found"
|
312
|
+
)
|
313
|
+
|
314
|
+
try:
|
315
|
+
# Get the complete conversation history
|
316
|
+
context = (
|
317
|
+
self.conversation.return_history_as_string()
|
318
|
+
)
|
319
|
+
|
320
|
+
# Get response from agent
|
321
|
+
if isinstance(agent, Agent):
|
322
|
+
response = agent.run(
|
323
|
+
task=f"{context}\nPlease respond to the latest task as {agent_name}."
|
324
|
+
)
|
325
|
+
else:
|
326
|
+
# For callable functions
|
327
|
+
response = agent(context)
|
328
|
+
|
329
|
+
# Add response to conversation
|
330
|
+
if response and not response.isspace():
|
331
|
+
self.conversation.add(
|
332
|
+
role=agent_name, content=response
|
333
|
+
)
|
334
|
+
logger.info(f"Agent {agent_name} responded")
|
335
|
+
|
336
|
+
except Exception as e:
|
337
|
+
logger.error(
|
338
|
+
f"Error getting response from {agent_name}: {e}"
|
339
|
+
)
|
340
|
+
self.conversation.add(
|
341
|
+
role=agent_name,
|
342
|
+
content=f"Error: Unable to generate response - {str(e)}",
|
343
|
+
)
|
344
|
+
|
345
|
+
return history_output_formatter(
|
346
|
+
self.conversation, self.output_type
|
347
|
+
)
|
348
|
+
|
349
|
+
except InteractiveGroupChatError as e:
|
350
|
+
logger.error(f"GroupChat error: {e}")
|
351
|
+
raise
|
352
|
+
except Exception as e:
|
353
|
+
logger.error(f"Unexpected error: {e}")
|
354
|
+
raise InteractiveGroupChatError(
|
355
|
+
f"Unexpected error occurred: {str(e)}"
|
356
|
+
)
|
swarms/structs/ma_blocks.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from typing import Union
|
1
2
|
from swarms.structs.agent import Agent
|
2
3
|
from typing import List, Callable
|
3
4
|
from swarms.structs.conversation import Conversation
|
@@ -82,3 +83,77 @@ def aggregate(
|
|
82
83
|
return history_output_formatter(
|
83
84
|
conversation=conversation, type=type
|
84
85
|
)
|
86
|
+
|
87
|
+
|
88
|
+
def run_agent(
|
89
|
+
agent: Agent,
|
90
|
+
task: str,
|
91
|
+
type: HistoryOutputType = "all",
|
92
|
+
*args,
|
93
|
+
**kwargs,
|
94
|
+
):
|
95
|
+
"""
|
96
|
+
Run an agent on a task.
|
97
|
+
|
98
|
+
Args:
|
99
|
+
agent (Agent): The agent to run
|
100
|
+
task (str): The task to run the agent on
|
101
|
+
type (HistoryOutputType, optional): The type of history output. Defaults to "all".
|
102
|
+
*args: Variable length argument list
|
103
|
+
**kwargs: Arbitrary keyword arguments
|
104
|
+
|
105
|
+
Returns:
|
106
|
+
Any: The result of running the agent
|
107
|
+
|
108
|
+
Raises:
|
109
|
+
ValueError: If agent or task is None
|
110
|
+
TypeError: If agent is not an instance of Agent
|
111
|
+
"""
|
112
|
+
if agent is None:
|
113
|
+
raise ValueError("Agent cannot be None")
|
114
|
+
|
115
|
+
if task is None:
|
116
|
+
raise ValueError("Task cannot be None")
|
117
|
+
|
118
|
+
if not isinstance(agent, Agent):
|
119
|
+
raise TypeError("Agent must be an instance of Agent")
|
120
|
+
|
121
|
+
try:
|
122
|
+
return agent.run(task=task, *args, **kwargs)
|
123
|
+
except Exception as e:
|
124
|
+
raise RuntimeError(f"Error running agent: {str(e)}")
|
125
|
+
|
126
|
+
|
127
|
+
def find_agent_by_name(
|
128
|
+
agents: List[Union[Agent, Callable]], agent_name: str
|
129
|
+
) -> Agent:
|
130
|
+
"""
|
131
|
+
Find an agent by its name in a list of agents.
|
132
|
+
|
133
|
+
Args:
|
134
|
+
agents (List[Union[Agent, Callable]]): List of agents to search through
|
135
|
+
agent_name (str): Name of the agent to find
|
136
|
+
|
137
|
+
Returns:
|
138
|
+
Agent: The found agent
|
139
|
+
|
140
|
+
Raises:
|
141
|
+
ValueError: If agents list is empty or agent not found
|
142
|
+
TypeError: If agent_name is not a string
|
143
|
+
"""
|
144
|
+
if not agents:
|
145
|
+
raise ValueError("Agents list cannot be empty")
|
146
|
+
|
147
|
+
if not isinstance(agent_name, str):
|
148
|
+
raise TypeError("Agent name must be a string")
|
149
|
+
|
150
|
+
if not agent_name.strip():
|
151
|
+
raise ValueError("Agent name cannot be empty or whitespace")
|
152
|
+
|
153
|
+
try:
|
154
|
+
for agent in agents:
|
155
|
+
if hasattr(agent, "name") and agent.name == agent_name:
|
156
|
+
return agent
|
157
|
+
raise ValueError(f"Agent with name '{agent_name}' not found")
|
158
|
+
except Exception as e:
|
159
|
+
raise RuntimeError(f"Error finding agent: {str(e)}")
|
@@ -9,7 +9,7 @@ from typing import Any, Callable, List, Optional
|
|
9
9
|
from swarms.structs.agent import Agent
|
10
10
|
from swarms.structs.conversation import Conversation
|
11
11
|
from swarms.structs.multi_agent_exec import run_agents_concurrently
|
12
|
-
from swarms.
|
12
|
+
from swarms.utils.output_types import OutputType
|
13
13
|
from swarms.utils.formatter import formatter
|
14
14
|
from swarms.utils.loguru_logger import initialize_logger
|
15
15
|
|