swarms 7.8.9__py3-none-any.whl → 7.9.0__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/cli/onboarding_process.py +1 -3
- swarms/prompts/collaborative_prompts.py +177 -0
- swarms/structs/agent.py +349 -87
- swarms/structs/concurrent_workflow.py +70 -196
- swarms/structs/interactive_groupchat.py +319 -12
- swarms/structs/ma_utils.py +25 -6
- swarms/structs/mixture_of_agents.py +88 -113
- swarms/structs/swarm_router.py +148 -187
- swarms/telemetry/__init__.py +4 -18
- swarms/telemetry/log_executions.py +43 -0
- swarms/telemetry/main.py +53 -217
- swarms/tools/base_tool.py +8 -3
- swarms/utils/formatter.py +130 -13
- swarms/utils/litellm_wrapper.py +5 -1
- swarms-7.9.0.dist-info/METADATA +626 -0
- {swarms-7.8.9.dist-info → swarms-7.9.0.dist-info}/RECORD +19 -17
- swarms-7.8.9.dist-info/METADATA +0 -2119
- {swarms-7.8.9.dist-info → swarms-7.9.0.dist-info}/LICENSE +0 -0
- {swarms-7.8.9.dist-info → swarms-7.9.0.dist-info}/WHEEL +0 -0
- {swarms-7.8.9.dist-info → swarms-7.9.0.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
import re
|
2
|
-
|
2
|
+
import random
|
3
|
+
from typing import Callable, List, Union, Optional
|
3
4
|
|
4
5
|
from loguru import logger
|
5
6
|
|
@@ -35,6 +36,91 @@ class InvalidTaskFormatError(InteractiveGroupChatError):
|
|
35
36
|
pass
|
36
37
|
|
37
38
|
|
39
|
+
class InvalidSpeakerFunctionError(InteractiveGroupChatError):
|
40
|
+
"""Raised when an invalid speaker function is provided"""
|
41
|
+
|
42
|
+
pass
|
43
|
+
|
44
|
+
|
45
|
+
# Built-in speaker functions
|
46
|
+
def round_robin_speaker(
|
47
|
+
agents: List[str], current_index: int = 0
|
48
|
+
) -> str:
|
49
|
+
"""
|
50
|
+
Round robin speaker function that cycles through agents in order.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
agents: List of agent names
|
54
|
+
current_index: Current position in the cycle
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
Next agent name in the round robin sequence
|
58
|
+
"""
|
59
|
+
if not agents:
|
60
|
+
raise ValueError("No agents provided for round robin")
|
61
|
+
return agents[current_index % len(agents)]
|
62
|
+
|
63
|
+
|
64
|
+
def random_speaker(agents: List[str], **kwargs) -> str:
|
65
|
+
"""
|
66
|
+
Random speaker function that selects agents randomly.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
agents: List of agent names
|
70
|
+
**kwargs: Additional arguments (ignored)
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
Randomly selected agent name
|
74
|
+
"""
|
75
|
+
if not agents:
|
76
|
+
raise ValueError("No agents provided for random selection")
|
77
|
+
return random.choice(agents)
|
78
|
+
|
79
|
+
|
80
|
+
def priority_speaker(
|
81
|
+
agents: List[str], priorities: dict, **kwargs
|
82
|
+
) -> str:
|
83
|
+
"""
|
84
|
+
Priority-based speaker function that selects agents based on priority weights.
|
85
|
+
|
86
|
+
Args:
|
87
|
+
agents: List of agent names
|
88
|
+
priorities: Dictionary mapping agent names to priority weights
|
89
|
+
**kwargs: Additional arguments (ignored)
|
90
|
+
|
91
|
+
Returns:
|
92
|
+
Selected agent name based on priority weights
|
93
|
+
"""
|
94
|
+
if not agents:
|
95
|
+
raise ValueError("No agents provided for priority selection")
|
96
|
+
|
97
|
+
# Filter agents that exist in the priorities dict
|
98
|
+
available_agents = [
|
99
|
+
agent for agent in agents if agent in priorities
|
100
|
+
]
|
101
|
+
if not available_agents:
|
102
|
+
# Fallback to random if no priorities match
|
103
|
+
return random.choice(agents)
|
104
|
+
|
105
|
+
# Calculate total weight
|
106
|
+
total_weight = sum(
|
107
|
+
priorities[agent] for agent in available_agents
|
108
|
+
)
|
109
|
+
if total_weight == 0:
|
110
|
+
return random.choice(available_agents)
|
111
|
+
|
112
|
+
# Select based on weighted probability
|
113
|
+
rand_val = random.uniform(0, total_weight)
|
114
|
+
current_weight = 0
|
115
|
+
|
116
|
+
for agent in available_agents:
|
117
|
+
current_weight += priorities[agent]
|
118
|
+
if rand_val <= current_weight:
|
119
|
+
return agent
|
120
|
+
|
121
|
+
return available_agents[-1] # Fallback
|
122
|
+
|
123
|
+
|
38
124
|
class InteractiveGroupChat:
|
39
125
|
"""
|
40
126
|
An interactive group chat system that enables conversations with multiple agents using @mentions.
|
@@ -49,6 +135,8 @@ class InteractiveGroupChat:
|
|
49
135
|
max_loops (int): Maximum number of conversation turns
|
50
136
|
conversation (Conversation): Stores the chat history
|
51
137
|
agent_map (Dict[str, Union[Agent, Callable]]): Mapping of agent names to their instances
|
138
|
+
speaker_function (Callable): Function to determine speaking order
|
139
|
+
speaker_state (dict): State for speaker functions that need it
|
52
140
|
|
53
141
|
Args:
|
54
142
|
name (str, optional): Name of the group chat. Defaults to "InteractiveGroupChat".
|
@@ -57,6 +145,8 @@ class InteractiveGroupChat:
|
|
57
145
|
max_loops (int, optional): Maximum conversation turns. Defaults to 1.
|
58
146
|
output_type (str, optional): Type of output format. Defaults to "string".
|
59
147
|
interactive (bool, optional): Whether to enable interactive terminal mode. Defaults to False.
|
148
|
+
speaker_function (Callable, optional): Function to determine speaking order. Defaults to round_robin_speaker.
|
149
|
+
speaker_state (dict, optional): Initial state for speaker function. Defaults to empty dict.
|
60
150
|
|
61
151
|
Raises:
|
62
152
|
ValueError: If invalid initialization parameters are provided
|
@@ -71,6 +161,8 @@ class InteractiveGroupChat:
|
|
71
161
|
max_loops: int = 1,
|
72
162
|
output_type: str = "string",
|
73
163
|
interactive: bool = False,
|
164
|
+
speaker_function: Optional[Callable] = None,
|
165
|
+
speaker_state: Optional[dict] = None,
|
74
166
|
):
|
75
167
|
self.id = id
|
76
168
|
self.name = name
|
@@ -80,6 +172,15 @@ class InteractiveGroupChat:
|
|
80
172
|
self.output_type = output_type
|
81
173
|
self.interactive = interactive
|
82
174
|
|
175
|
+
# Speaker function configuration
|
176
|
+
self.speaker_function = (
|
177
|
+
speaker_function or round_robin_speaker
|
178
|
+
)
|
179
|
+
self.speaker_state = speaker_state or {"current_index": 0}
|
180
|
+
|
181
|
+
# Validate speaker function
|
182
|
+
self._validate_speaker_function()
|
183
|
+
|
83
184
|
# Initialize conversation history
|
84
185
|
self.conversation = Conversation(time_enabled=True)
|
85
186
|
|
@@ -96,6 +197,32 @@ class InteractiveGroupChat:
|
|
96
197
|
self._setup_conversation_context()
|
97
198
|
self._update_agent_prompts()
|
98
199
|
|
200
|
+
def _validate_speaker_function(self) -> None:
|
201
|
+
"""
|
202
|
+
Validates the speaker function.
|
203
|
+
|
204
|
+
Raises:
|
205
|
+
InvalidSpeakerFunctionError: If the speaker function is invalid
|
206
|
+
"""
|
207
|
+
if not callable(self.speaker_function):
|
208
|
+
raise InvalidSpeakerFunctionError(
|
209
|
+
"Speaker function must be callable"
|
210
|
+
)
|
211
|
+
|
212
|
+
# Test the speaker function with a dummy list
|
213
|
+
try:
|
214
|
+
test_result = self.speaker_function(
|
215
|
+
["test_agent"], **self.speaker_state
|
216
|
+
)
|
217
|
+
if not isinstance(test_result, str):
|
218
|
+
raise InvalidSpeakerFunctionError(
|
219
|
+
"Speaker function must return a string"
|
220
|
+
)
|
221
|
+
except Exception as e:
|
222
|
+
raise InvalidSpeakerFunctionError(
|
223
|
+
f"Speaker function validation failed: {e}"
|
224
|
+
)
|
225
|
+
|
99
226
|
def _validate_initialization(self) -> None:
|
100
227
|
"""
|
101
228
|
Validates the group chat configuration.
|
@@ -150,6 +277,26 @@ class InteractiveGroupChat:
|
|
150
277
|
}
|
151
278
|
)
|
152
279
|
|
280
|
+
# Create the enhanced prompt that teaches agents how to use @mentions
|
281
|
+
mention_instruction = """
|
282
|
+
|
283
|
+
IMPORTANT: You are part of a collaborative group chat where you can interact with other agents using @mentions.
|
284
|
+
|
285
|
+
-COLLABORATIVE RESPONSE PROTOCOL:
|
286
|
+
1. FIRST: Read and understand all previous responses from other agents
|
287
|
+
2. ACKNOWLEDGE: Reference and acknowledge what other agents have said
|
288
|
+
3. BUILD UPON: Add your perspective while building upon their insights
|
289
|
+
4. MENTION: Use @agent_name to call on other agents when needed
|
290
|
+
|
291
|
+
HOW TO MENTION OTHER AGENTS:
|
292
|
+
- Use @agent_name to mention another agent in your response
|
293
|
+
- You can mention multiple agents: @agent1 @agent2
|
294
|
+
- When you mention an agent, they will be notified and can respond
|
295
|
+
- Example: "I think @analyst should review this data" or "Let's ask @researcher to investigate this further"
|
296
|
+
|
297
|
+
AVAILABLE AGENTS TO MENTION:
|
298
|
+
"""
|
299
|
+
|
153
300
|
group_context = (
|
154
301
|
f"\n\nYou are part of a group chat named '{self.name}' with the following description: {self.description}\n"
|
155
302
|
f"Other participants in this chat:\n"
|
@@ -163,11 +310,40 @@ class InteractiveGroupChat:
|
|
163
310
|
for info in agent_info
|
164
311
|
if info["name"] != agent.agent_name
|
165
312
|
]
|
166
|
-
agent_context = group_context
|
313
|
+
agent_context = group_context + mention_instruction
|
167
314
|
for other in other_agents:
|
168
|
-
agent_context +=
|
169
|
-
|
170
|
-
|
315
|
+
agent_context += f"- @{other['name']}: {other['description']}\n"
|
316
|
+
|
317
|
+
# Add final instruction
|
318
|
+
agent_context += """
|
319
|
+
|
320
|
+
COLLABORATION GUIDELINES:
|
321
|
+
- ALWAYS read the full conversation history before responding
|
322
|
+
- ACKNOWLEDGE other agents' contributions: "Building on @analyst's data insights..." or "I agree with @researcher's findings that..."
|
323
|
+
- BUILD UPON previous responses rather than repeating information
|
324
|
+
- SYNTHESIZE multiple perspectives when possible
|
325
|
+
- ASK CLARIFYING QUESTIONS if you need more information from other agents
|
326
|
+
- DELEGATE appropriately: "Let me ask @expert_agent to verify this" or "@specialist, can you elaborate on this point?"
|
327
|
+
|
328
|
+
RESPONSE STRUCTURE:
|
329
|
+
1. ACKNOWLEDGE: "I've reviewed the responses from @agent1 and @agent2..."
|
330
|
+
2. BUILD: "Building on @agent1's analysis of the data..."
|
331
|
+
3. CONTRIBUTE: "From my perspective, I would add..."
|
332
|
+
4. COLLABORATE: "To get a complete picture, let me ask @agent3 to..."
|
333
|
+
5. SYNTHESIZE: "Combining our insights, the key findings are..."
|
334
|
+
|
335
|
+
EXAMPLES OF GOOD COLLABORATION:
|
336
|
+
- "I've reviewed @analyst's data analysis and @researcher's market insights. The data shows strong growth potential, and I agree with @researcher that we should focus on emerging markets. Let me add that from a content perspective, we should @writer to create targeted messaging for these markets."
|
337
|
+
- "Building on @researcher's findings about customer behavior, I can see that @analyst's data supports this trend. To get a complete understanding, let me ask @writer to help us craft messaging that addresses these specific customer needs."
|
338
|
+
|
339
|
+
AVOID:
|
340
|
+
- Ignoring other agents' responses
|
341
|
+
- Repeating what others have already said
|
342
|
+
- Making assumptions without consulting relevant experts
|
343
|
+
- Responding in isolation without considering the group's collective knowledge
|
344
|
+
|
345
|
+
Remember: You are part of a team. Your response should reflect that you've read, understood, and built upon the contributions of others.
|
346
|
+
"""
|
171
347
|
|
172
348
|
# Update the agent's system prompt
|
173
349
|
agent.system_prompt = (
|
@@ -202,6 +378,118 @@ class InteractiveGroupChat:
|
|
202
378
|
logger.error(f"Error extracting mentions: {e}")
|
203
379
|
raise InvalidTaskFormatError(f"Invalid task format: {e}")
|
204
380
|
|
381
|
+
def _get_speaking_order(
|
382
|
+
self, mentioned_agents: List[str]
|
383
|
+
) -> List[str]:
|
384
|
+
"""
|
385
|
+
Determines the speaking order using the configured speaker function.
|
386
|
+
|
387
|
+
Args:
|
388
|
+
mentioned_agents: List of agent names that were mentioned
|
389
|
+
|
390
|
+
Returns:
|
391
|
+
List of agent names in the order they should speak
|
392
|
+
"""
|
393
|
+
if not mentioned_agents:
|
394
|
+
return []
|
395
|
+
|
396
|
+
# Use the speaker function to determine order
|
397
|
+
try:
|
398
|
+
if self.speaker_function == round_robin_speaker:
|
399
|
+
# For round robin, we need to maintain state
|
400
|
+
current_index = self.speaker_state.get(
|
401
|
+
"current_index", 0
|
402
|
+
)
|
403
|
+
ordered_agents = []
|
404
|
+
|
405
|
+
# Create the order starting from current index
|
406
|
+
for i in range(len(mentioned_agents)):
|
407
|
+
agent = round_robin_speaker(
|
408
|
+
mentioned_agents, current_index + i
|
409
|
+
)
|
410
|
+
ordered_agents.append(agent)
|
411
|
+
|
412
|
+
# Update state for next round
|
413
|
+
self.speaker_state["current_index"] = (
|
414
|
+
current_index + len(mentioned_agents)
|
415
|
+
) % len(mentioned_agents)
|
416
|
+
return ordered_agents
|
417
|
+
|
418
|
+
elif self.speaker_function == random_speaker:
|
419
|
+
# For random, shuffle the list
|
420
|
+
shuffled = mentioned_agents.copy()
|
421
|
+
random.shuffle(shuffled)
|
422
|
+
return shuffled
|
423
|
+
|
424
|
+
elif self.speaker_function == priority_speaker:
|
425
|
+
# For priority, we need priorities in speaker_state
|
426
|
+
priorities = self.speaker_state.get("priorities", {})
|
427
|
+
if not priorities:
|
428
|
+
# Fallback to random if no priorities set
|
429
|
+
shuffled = mentioned_agents.copy()
|
430
|
+
random.shuffle(shuffled)
|
431
|
+
return shuffled
|
432
|
+
|
433
|
+
# Sort by priority (higher priority first)
|
434
|
+
sorted_agents = sorted(
|
435
|
+
mentioned_agents,
|
436
|
+
key=lambda x: priorities.get(x, 0),
|
437
|
+
reverse=True,
|
438
|
+
)
|
439
|
+
return sorted_agents
|
440
|
+
|
441
|
+
else:
|
442
|
+
# Custom speaker function
|
443
|
+
# For custom functions, we'll use the first agent returned
|
444
|
+
# and then process the rest in original order
|
445
|
+
first_speaker = self.speaker_function(
|
446
|
+
mentioned_agents, **self.speaker_state
|
447
|
+
)
|
448
|
+
if first_speaker in mentioned_agents:
|
449
|
+
remaining = [
|
450
|
+
agent
|
451
|
+
for agent in mentioned_agents
|
452
|
+
if agent != first_speaker
|
453
|
+
]
|
454
|
+
return [first_speaker] + remaining
|
455
|
+
else:
|
456
|
+
return mentioned_agents
|
457
|
+
|
458
|
+
except Exception as e:
|
459
|
+
logger.error(f"Error in speaker function: {e}")
|
460
|
+
# Fallback to original order
|
461
|
+
return mentioned_agents
|
462
|
+
|
463
|
+
def set_speaker_function(
|
464
|
+
self,
|
465
|
+
speaker_function: Callable,
|
466
|
+
speaker_state: Optional[dict] = None,
|
467
|
+
) -> None:
|
468
|
+
"""
|
469
|
+
Set a custom speaker function and optional state.
|
470
|
+
|
471
|
+
Args:
|
472
|
+
speaker_function: Function that determines speaking order
|
473
|
+
speaker_state: Optional state for the speaker function
|
474
|
+
"""
|
475
|
+
self.speaker_function = speaker_function
|
476
|
+
if speaker_state:
|
477
|
+
self.speaker_state.update(speaker_state)
|
478
|
+
self._validate_speaker_function()
|
479
|
+
logger.info(
|
480
|
+
f"Speaker function updated to: {speaker_function.__name__}"
|
481
|
+
)
|
482
|
+
|
483
|
+
def set_priorities(self, priorities: dict) -> None:
|
484
|
+
"""
|
485
|
+
Set agent priorities for priority-based speaking order.
|
486
|
+
|
487
|
+
Args:
|
488
|
+
priorities: Dictionary mapping agent names to priority weights
|
489
|
+
"""
|
490
|
+
self.speaker_state["priorities"] = priorities
|
491
|
+
logger.info(f"Agent priorities set: {priorities}")
|
492
|
+
|
205
493
|
def start_interactive_session(self):
|
206
494
|
"""
|
207
495
|
Start an interactive terminal session for chatting with agents.
|
@@ -263,9 +551,9 @@ class InteractiveGroupChat:
|
|
263
551
|
|
264
552
|
# Process the task and get responses
|
265
553
|
try:
|
266
|
-
|
554
|
+
self.run(user_input)
|
267
555
|
print("\nChat:")
|
268
|
-
print(response)
|
556
|
+
# print(response)
|
269
557
|
|
270
558
|
except NoMentionedAgentsError:
|
271
559
|
print(
|
@@ -303,8 +591,16 @@ class InteractiveGroupChat:
|
|
303
591
|
# Add user task to conversation
|
304
592
|
self.conversation.add(role="User", content=task)
|
305
593
|
|
306
|
-
#
|
307
|
-
|
594
|
+
# Determine speaking order using speaker function
|
595
|
+
speaking_order = self._get_speaking_order(
|
596
|
+
mentioned_agents
|
597
|
+
)
|
598
|
+
logger.info(
|
599
|
+
f"Speaking order determined: {speaking_order}"
|
600
|
+
)
|
601
|
+
|
602
|
+
# Get responses from mentioned agents in the determined order
|
603
|
+
for agent_name in speaking_order:
|
308
604
|
agent = self.agent_map.get(agent_name)
|
309
605
|
if not agent:
|
310
606
|
raise AgentNotFoundError(
|
@@ -319,9 +615,20 @@ class InteractiveGroupChat:
|
|
319
615
|
|
320
616
|
# Get response from agent
|
321
617
|
if isinstance(agent, Agent):
|
322
|
-
|
323
|
-
|
324
|
-
|
618
|
+
collaborative_task = f"""{context}
|
619
|
+
|
620
|
+
COLLABORATIVE TASK: Please respond to the latest task as {agent_name}.
|
621
|
+
|
622
|
+
IMPORTANT INSTRUCTIONS:
|
623
|
+
1. Read the ENTIRE conversation history above
|
624
|
+
2. Acknowledge what other agents have said before adding your perspective
|
625
|
+
3. Build upon their insights rather than repeating information
|
626
|
+
4. If you need input from other agents, mention them using @agent_name
|
627
|
+
5. Provide your unique expertise while showing you understand the group's collective knowledge
|
628
|
+
|
629
|
+
Remember: You are part of a collaborative team. Your response should demonstrate that you've read, understood, and are building upon the contributions of others."""
|
630
|
+
|
631
|
+
response = agent.run(task=collaborative_task)
|
325
632
|
else:
|
326
633
|
# For callable functions
|
327
634
|
response = agent(context)
|
swarms/structs/ma_utils.py
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
from typing import List, Any, Optional, Union, Callable
|
2
2
|
import random
|
3
|
+
from swarms.prompts.collaborative_prompts import (
|
4
|
+
get_multi_agent_collaboration_prompt_one,
|
5
|
+
)
|
3
6
|
|
4
7
|
|
5
8
|
def list_all_agents(
|
6
9
|
agents: List[Union[Callable, Any]],
|
7
10
|
conversation: Optional[Any] = None,
|
8
|
-
name: str =
|
9
|
-
|
11
|
+
name: Optional[str] = None,
|
12
|
+
description: Optional[str] = None,
|
13
|
+
add_to_conversation: Optional[bool] = False,
|
14
|
+
add_collaboration_prompt: Optional[bool] = True,
|
10
15
|
) -> str:
|
11
16
|
"""Lists all agents in a swarm and optionally adds them to a conversation.
|
12
17
|
|
@@ -27,6 +32,7 @@ def list_all_agents(
|
|
27
32
|
>>> conversation = Conversation()
|
28
33
|
>>> agent_info = list_all_agents(agents, conversation, "MySwarm")
|
29
34
|
>>> print(agent_info)
|
35
|
+
Swarm: MySwarm
|
30
36
|
Total Agents: 2
|
31
37
|
|
32
38
|
Agent: Agent1
|
@@ -39,8 +45,15 @@ def list_all_agents(
|
|
39
45
|
# Compile information about all agents
|
40
46
|
total_agents = len(agents)
|
41
47
|
|
42
|
-
all_agents = f"
|
43
|
-
|
48
|
+
all_agents = f"Team Name: {name}\n" if name else ""
|
49
|
+
all_agents += (
|
50
|
+
f"Team Description: {description}\n" if description else ""
|
51
|
+
)
|
52
|
+
all_agents += f"Total Agents: {total_agents}\n\n"
|
53
|
+
all_agents += "| Agent | Description |\n"
|
54
|
+
all_agents += "|-------|-------------|\n"
|
55
|
+
all_agents += "\n".join(
|
56
|
+
f"| {agent.agent_name} | {agent.description or (agent.system_prompt[:50] + '...' if len(agent.system_prompt) > 50 else agent.system_prompt)} |"
|
44
57
|
for agent in agents
|
45
58
|
)
|
46
59
|
|
@@ -48,10 +61,15 @@ def list_all_agents(
|
|
48
61
|
# Add the agent information to the conversation
|
49
62
|
conversation.add(
|
50
63
|
role="System",
|
51
|
-
content=
|
64
|
+
content=all_agents,
|
52
65
|
)
|
53
66
|
|
54
|
-
|
67
|
+
if add_collaboration_prompt:
|
68
|
+
return get_multi_agent_collaboration_prompt_one(
|
69
|
+
agents_in_swarm=all_agents
|
70
|
+
)
|
71
|
+
else:
|
72
|
+
return all_agents
|
55
73
|
|
56
74
|
|
57
75
|
models = [
|
@@ -68,6 +86,7 @@ models = [
|
|
68
86
|
"o4-mini",
|
69
87
|
"o3",
|
70
88
|
"gpt-4.1",
|
89
|
+
"groq/llama-3.1-8b-instant",
|
71
90
|
"gpt-4.1-nano",
|
72
91
|
]
|
73
92
|
|