swarms 7.7.2__py3-none-any.whl → 7.7.3__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.
Files changed (34) hide show
  1. swarms/prompts/ag_prompt.py +51 -19
  2. swarms/prompts/agent_system_prompts.py +13 -4
  3. swarms/prompts/multi_agent_collab_prompt.py +18 -0
  4. swarms/prompts/prompt.py +6 -10
  5. swarms/schemas/__init__.py +0 -3
  6. swarms/structs/__init__.py +2 -4
  7. swarms/structs/agent.py +201 -160
  8. swarms/structs/aop.py +8 -1
  9. swarms/structs/auto_swarm_builder.py +271 -210
  10. swarms/structs/conversation.py +22 -65
  11. swarms/structs/hiearchical_swarm.py +93 -122
  12. swarms/structs/ma_utils.py +96 -0
  13. swarms/structs/mixture_of_agents.py +20 -103
  14. swarms/structs/multi_agent_router.py +32 -95
  15. swarms/structs/output_types.py +3 -16
  16. swarms/structs/stopping_conditions.py +30 -0
  17. swarms/structs/swarm_router.py +56 -4
  18. swarms/structs/swarming_architectures.py +576 -185
  19. swarms/telemetry/main.py +1 -7
  20. swarms/tools/mcp_client.py +209 -53
  21. swarms/tools/mcp_integration.py +1 -53
  22. swarms/utils/generate_keys.py +64 -0
  23. swarms/utils/history_output_formatter.py +2 -0
  24. {swarms-7.7.2.dist-info → swarms-7.7.3.dist-info}/METADATA +98 -263
  25. {swarms-7.7.2.dist-info → swarms-7.7.3.dist-info}/RECORD +28 -32
  26. swarms/schemas/agent_input_schema.py +0 -149
  27. swarms/structs/agents_available.py +0 -87
  28. swarms/structs/graph_swarm.py +0 -612
  29. swarms/structs/queue_swarm.py +0 -193
  30. swarms/structs/swarm_builder.py +0 -395
  31. swarms/structs/swarm_output_type.py +0 -23
  32. {swarms-7.7.2.dist-info → swarms-7.7.3.dist-info}/LICENSE +0 -0
  33. {swarms-7.7.2.dist-info → swarms-7.7.3.dist-info}/WHEEL +0 -0
  34. {swarms-7.7.2.dist-info → swarms-7.7.3.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  import datetime
2
2
  import json
3
- from typing import Any, Optional, Union
3
+ from typing import Any, List, Optional, Union
4
4
 
5
5
  import yaml
6
6
  from swarms.structs.base_structure import BaseStructure
@@ -93,7 +93,7 @@ class Conversation(BaseStructure):
93
93
 
94
94
  # If system prompt is not None, add it to the conversation history
95
95
  if self.system_prompt is not None:
96
- self.add("System: ", self.system_prompt)
96
+ self.add("System", self.system_prompt)
97
97
 
98
98
  if self.rules is not None:
99
99
  self.add("User", rules)
@@ -105,7 +105,7 @@ class Conversation(BaseStructure):
105
105
  if tokenizer is not None:
106
106
  self.truncate_memory_with_tokenizer()
107
107
 
108
- def _add(
108
+ def add(
109
109
  self,
110
110
  role: str,
111
111
  content: Union[str, dict, list],
@@ -137,8 +137,19 @@ class Conversation(BaseStructure):
137
137
  # Add the message to history immediately without waiting for token count
138
138
  self.conversation_history.append(message)
139
139
 
140
+ if self.token_count is True:
141
+ self._count_tokens(content, message)
142
+
143
+ def add_multiple_messages(
144
+ self, roles: List[str], contents: List[Union[str, dict, list]]
145
+ ):
146
+ for role, content in zip(roles, contents):
147
+ self.add(role, content)
148
+
149
+ def _count_tokens(self, content: str, message: dict):
140
150
  # If token counting is enabled, do it in a separate thread
141
151
  if self.token_count is True:
152
+
142
153
  # Define a function to count tokens and update the message
143
154
  def count_tokens_thread():
144
155
  tokens = count_tokens(any_to_str(content))
@@ -158,21 +169,6 @@ class Conversation(BaseStructure):
158
169
  )
159
170
  token_thread.start()
160
171
 
161
- def add(self, role: str, content: Union[str, dict, list]):
162
- """Add a message to the conversation history.
163
-
164
- Args:
165
- role (str): The role of the speaker (e.g., 'User', 'System').
166
- content (Union[str, dict, list]): The content of the message to be added.
167
- """
168
- process_thread = threading.Thread(
169
- target=self._add,
170
- args=(role, content),
171
- daemon=True,
172
- )
173
- process_thread.start()
174
- # process_thread.join()
175
-
176
172
  def delete(self, index: str):
177
173
  """Delete a message from the conversation history.
178
174
 
@@ -324,53 +320,6 @@ class Conversation(BaseStructure):
324
320
  if keyword in msg["content"]
325
321
  ]
326
322
 
327
- def pretty_print_conversation(self, messages):
328
- """Pretty print the conversation history.
329
-
330
- Args:
331
- messages (list): List of messages to print.
332
- """
333
- role_to_color = {
334
- "system": "red",
335
- "user": "green",
336
- "assistant": "blue",
337
- "tool": "magenta",
338
- }
339
-
340
- for message in messages:
341
- if message["role"] == "system":
342
- formatter.print_panel(
343
- f"system: {message['content']}\n",
344
- role_to_color[message["role"]],
345
- )
346
- elif message["role"] == "user":
347
- formatter.print_panel(
348
- f"user: {message['content']}\n",
349
- role_to_color[message["role"]],
350
- )
351
- elif message["role"] == "assistant" and message.get(
352
- "function_call"
353
- ):
354
- formatter.print_panel(
355
- f"assistant: {message['function_call']}\n",
356
- role_to_color[message["role"]],
357
- )
358
- elif message["role"] == "assistant" and not message.get(
359
- "function_call"
360
- ):
361
- formatter.print_panel(
362
- f"assistant: {message['content']}\n",
363
- role_to_color[message["role"]],
364
- )
365
- elif message["role"] == "tool":
366
- formatter.print_panel(
367
- (
368
- f"function ({message['name']}):"
369
- f" {message['content']}\n"
370
- ),
371
- role_to_color[message["role"]],
372
- )
373
-
374
323
  def truncate_memory_with_tokenizer(self):
375
324
  """
376
325
  Truncates the conversation history based on the total number of tokens using a tokenizer.
@@ -555,6 +504,14 @@ class Conversation(BaseStructure):
555
504
  ]
556
505
  )
557
506
 
507
+ def batch_add(self, messages: List[dict]):
508
+ """Batch add messages to the conversation history.
509
+
510
+ Args:
511
+ messages (List[dict]): List of messages to add.
512
+ """
513
+ self.conversation_history.extend(messages)
514
+
558
515
 
559
516
  # # Example usage
560
517
  # # conversation = Conversation()
@@ -1,4 +1,3 @@
1
- from concurrent.futures import ThreadPoolExecutor
2
1
  import json
3
2
  import os
4
3
  from typing import Any, List, Optional, Union, Dict
@@ -9,10 +8,15 @@ from swarms.structs.agent import Agent
9
8
  from swarms.structs.base_swarm import BaseSwarm
10
9
  from swarms.structs.conversation import Conversation
11
10
  from swarms.structs.output_types import OutputType
11
+ from swarms.utils.any_to_str import any_to_str
12
12
  from swarms.utils.formatter import formatter
13
13
 
14
14
  from swarms.utils.function_caller_model import OpenAIFunctionCaller
15
15
  from swarms.utils.loguru_logger import initialize_logger
16
+ from swarms.utils.history_output_formatter import (
17
+ history_output_formatter,
18
+ )
19
+ from swarms.structs.ma_utils import list_all_agents
16
20
 
17
21
  logger = initialize_logger(log_folder="hierarchical_swarm")
18
22
 
@@ -48,9 +52,6 @@ class SwarmSpec(BaseModel):
48
52
 
49
53
 
50
54
  HIEARCHICAL_SWARM_SYSTEM_PROMPT = """
51
- Below is a comprehensive production-grade hierarchical agent director prompt that is designed to break down orders, distribute tasks, and select the best worker agents to achieve the overall objectives. This prompt follows the schematic provided by the HierarchicalOrder and SwarmSpec classes and is composed of nearly 2,000 words. You can use this as your system prompt for the director agent in a multi-agent swarm system.
52
-
53
- ---
54
55
 
55
56
  **SYSTEM PROMPT: HIERARCHICAL AGENT DIRECTOR**
56
57
 
@@ -211,6 +212,27 @@ Remember: the success of the swarm depends on your ability to manage complexity,
211
212
  """
212
213
 
213
214
 
215
+ class TeamUnit(BaseModel):
216
+ """Represents a team within a department."""
217
+
218
+ name: Optional[str] = Field(
219
+ None, description="The name of the team."
220
+ )
221
+ description: Optional[str] = Field(
222
+ None, description="A brief description of the team's purpose."
223
+ )
224
+ agents: Optional[List[Union[Agent, Any]]] = Field(
225
+ None,
226
+ description="A list of agents that are part of the team.",
227
+ )
228
+ team_leader: Optional[Union[Agent, Any]] = Field(
229
+ None, description="The team leader of the team."
230
+ )
231
+
232
+ class Config:
233
+ arbitrary_types_allowed = True
234
+
235
+
214
236
  class HierarchicalSwarm(BaseSwarm):
215
237
  """
216
238
  _Representer a hierarchical swarm of agents, with a director that orchestrates tasks among the agents.
@@ -230,8 +252,9 @@ class HierarchicalSwarm(BaseSwarm):
230
252
  agents: List[Union[Agent, Any]] = None,
231
253
  max_loops: int = 1,
232
254
  output_type: OutputType = "dict",
233
- return_all_history: bool = False,
234
255
  director_model_name: str = "gpt-4o",
256
+ teams: Optional[List[TeamUnit]] = None,
257
+ inter_agent_loops: int = 1,
235
258
  *args,
236
259
  **kwargs,
237
260
  ):
@@ -244,7 +267,6 @@ class HierarchicalSwarm(BaseSwarm):
244
267
  :param agents: A list of agents within the swarm.
245
268
  :param max_loops: The maximum number of feedback loops between the director and agents.
246
269
  :param output_type: The format in which to return the output (dict, str, or list).
247
- :param return_all_history: A flag indicating whether to return all conversation history.
248
270
  """
249
271
  super().__init__(
250
272
  name=name,
@@ -254,18 +276,46 @@ class HierarchicalSwarm(BaseSwarm):
254
276
  self.director = director
255
277
  self.agents = agents
256
278
  self.max_loops = max_loops
257
- self.return_all_history = return_all_history
258
279
  self.output_type = output_type
259
280
  self.director_model_name = director_model_name
281
+ self.teams = teams
282
+ self.inter_agent_loops = inter_agent_loops
283
+
260
284
  self.conversation = Conversation(time_enabled=False)
261
285
  self.current_loop = 0
262
286
  self.agent_outputs = {} # Store agent outputs for each loop
263
287
 
264
288
  self.add_name_and_description()
265
- self.check_agents()
266
- self.list_all_agents()
289
+
290
+ # Reliability checks
291
+ self.reliability_checks()
292
+
293
+ # Handle teams
294
+ self.handle_teams()
295
+
296
+ # List all agents
297
+ list_all_agents(self.agents, self.conversation, self.name)
298
+
267
299
  self.director = self.setup_director()
268
300
 
301
+ def handle_teams(self):
302
+ if not self.teams:
303
+ return
304
+
305
+ # Use list comprehension for faster team processing
306
+ team_list = [team.model_dump() for team in self.teams]
307
+
308
+ # Use extend() instead of append() in a loop for better performance
309
+ self.agents.extend(
310
+ agent for team in team_list for agent in team["agents"]
311
+ )
312
+
313
+ # Add conversation message
314
+ self.conversation.add(
315
+ role="System",
316
+ content=f"Teams Available: {any_to_str(team_list)}",
317
+ )
318
+
269
319
  def setup_director(self):
270
320
  director = OpenAIFunctionCaller(
271
321
  model_name=self.director_model_name,
@@ -278,12 +328,15 @@ class HierarchicalSwarm(BaseSwarm):
278
328
 
279
329
  return director
280
330
 
281
- def check_agents(self):
331
+ def reliability_checks(self):
282
332
  """
283
333
  Checks if there are any agents and a director set for the swarm.
284
334
  Raises ValueError if either condition is not met.
285
335
  """
286
- if not self.agents:
336
+
337
+ logger.info(f"🔍 CHECKING RELIABILITY OF SWARM: {self.name}")
338
+
339
+ if len(self.agents) == 0:
287
340
  raise ValueError(
288
341
  "No agents found in the swarm. At least one agent must be provided to create a hierarchical swarm."
289
342
  )
@@ -301,9 +354,7 @@ class HierarchicalSwarm(BaseSwarm):
301
354
  "Director not set for the swarm. A director agent is required to coordinate and orchestrate tasks among the agents."
302
355
  )
303
356
 
304
- logger.info(
305
- "Reliability checks have passed. Swarm is ready to execute."
306
- )
357
+ logger.info(f"🔍 RELIABILITY CHECKS PASSED: {self.name}")
307
358
 
308
359
  def run_director(
309
360
  self, task: str, loop_context: str = "", img: str = None
@@ -327,12 +378,6 @@ class HierarchicalSwarm(BaseSwarm):
327
378
  # Run the director with the context
328
379
  function_call = self.director.run(task=director_context)
329
380
 
330
- print(function_call)
331
-
332
- # function_call = self.check_director_agent_output(
333
- # function_call
334
- # )
335
-
336
381
  formatter.print_panel(
337
382
  f"Director Output (Loop {self.current_loop}/{self.max_loops}):\n{function_call}",
338
383
  title="Director's Orders",
@@ -374,10 +419,6 @@ class HierarchicalSwarm(BaseSwarm):
374
419
  logger.info(
375
420
  f"Starting loop {self.current_loop}/{self.max_loops}"
376
421
  )
377
- formatter.print_panel(
378
- f"⚡ EXECUTING LOOP {self.current_loop}/{self.max_loops}",
379
- title="SWARM EXECUTION CYCLE",
380
- )
381
422
 
382
423
  # Get director's orders
383
424
  swarm_spec = self.run_director(
@@ -416,7 +457,9 @@ class HierarchicalSwarm(BaseSwarm):
416
457
  break
417
458
 
418
459
  # Return the results in the specified format
419
- return self.format_output()
460
+ return history_output_formatter(
461
+ self.conversation, self.output_type
462
+ )
420
463
 
421
464
  def compile_loop_context(self, loop_number: int) -> str:
422
465
  """
@@ -437,74 +480,41 @@ class HierarchicalSwarm(BaseSwarm):
437
480
 
438
481
  return context
439
482
 
440
- def format_output(self) -> Union[str, Dict, List]:
441
- """
442
- Formats the output according to the specified output_type.
443
-
444
- :return: The formatted output.
445
- """
446
- if self.output_type == "str" or self.return_all_history:
447
- return self.conversation.get_str()
448
- elif self.output_type == "dict":
449
- return self.conversation.return_messages_as_dictionary()
450
- elif self.output_type == "list":
451
- return self.conversation.return_messages_as_list()
452
- else:
453
- return self.conversation.get_str()
454
-
455
483
  def add_name_and_description(self):
456
484
  """
457
485
  Adds the swarm's name and description to the conversation.
458
486
  """
459
487
  self.conversation.add(
460
488
  role="System",
461
- content=f"Swarm Name: {self.name}\nSwarm Description: {self.description}",
489
+ content=f"\n\nSwarm Name: {self.name}\n\nSwarm Description: {self.description}",
462
490
  )
463
491
 
464
- formatter.print_panel(
465
- f"⚡ INITIALIZING HIERARCHICAL SWARM UNIT: {self.name}\n"
466
- f"🔒 CLASSIFIED DIRECTIVE: {self.description}\n"
467
- f"📡 STATUS: ACTIVATING SWARM PROTOCOLS\n"
468
- f"🌐 ESTABLISHING SECURE AGENT MESH NETWORK\n"
469
- f"⚠️ CYBERSECURITY MEASURES ENGAGED\n",
470
- title="SWARM CORPORATION - HIERARCHICAL SWARMS ACTIVATING...",
471
- )
472
-
473
- def list_all_agents(self) -> str:
474
- """
475
- Lists all agents available in the swarm.
476
-
477
- :return: A string representation of all agents in the swarm.
478
- """
479
- # Compile information about all agents
480
- all_agents = "\n".join(
481
- f"Agent: {agent.agent_name} || Description: {agent.description or agent.system_prompt}"
482
- for agent in self.agents
483
- )
484
-
485
- # Add the agent information to the conversation
486
- self.conversation.add(
487
- role="System",
488
- content=f"All Agents Available in the Swarm {self.name}:\n{all_agents}",
489
- )
490
-
491
- formatter.print_panel(
492
- all_agents, title="All Agents Available in the Swarm"
493
- )
494
-
495
- return all_agents
496
-
497
492
  def find_agent(self, name: str) -> Optional[Agent]:
498
493
  """
499
494
  Finds an agent by its name within the swarm.
500
495
 
501
496
  :param name: The name of the agent to find.
502
497
  :return: The agent if found, otherwise None.
498
+ :raises: ValueError if agent is not found
503
499
  """
504
- for agent in self.agents:
505
- if agent.agent_name == name:
506
- return agent
507
- return None
500
+ try:
501
+ # Fast path: use list comprehension for quick lookup
502
+ matching_agents = [
503
+ agent
504
+ for agent in self.agents
505
+ if agent.agent_name == name
506
+ ]
507
+
508
+ if not matching_agents:
509
+ error_msg = f"Agent '{name}' not found in the swarm '{self.name}'"
510
+ logger.error(error_msg)
511
+ return None
512
+
513
+ return matching_agents[0]
514
+
515
+ except Exception as e:
516
+ logger.error(f"Error finding agent '{name}': {str(e)}")
517
+ return None
508
518
 
509
519
  def run_agent(
510
520
  self, agent_name: str, task: str, img: str = None
@@ -520,14 +530,6 @@ class HierarchicalSwarm(BaseSwarm):
520
530
  try:
521
531
  agent = self.find_agent(agent_name)
522
532
 
523
- if not agent:
524
- error_msg = f"Agent '{agent_name}' not found in the swarm '{self.name}'"
525
- logger.error(error_msg)
526
- self.conversation.add(
527
- role="System", content=f"Error: {error_msg}"
528
- )
529
- return error_msg
530
-
531
533
  # Prepare context for the agent
532
534
  agent_context = (
533
535
  f"Loop: {self.current_loop}/{self.max_loops}\n"
@@ -541,7 +543,7 @@ class HierarchicalSwarm(BaseSwarm):
541
543
  title=f"Agent Task - Loop {self.current_loop}/{self.max_loops}",
542
544
  )
543
545
 
544
- out = agent.run(task=agent_context, img=img)
546
+ out = agent.run(task=agent_context)
545
547
 
546
548
  # Add the agent's output to the conversation
547
549
  self.conversation.add(
@@ -560,9 +562,6 @@ class HierarchicalSwarm(BaseSwarm):
560
562
  f"Error running agent '{agent_name}': {str(e)}"
561
563
  )
562
564
  logger.error(error_msg)
563
- self.conversation.add(
564
- role="System", content=f"Error: {error_msg}"
565
- )
566
565
  return error_msg
567
566
 
568
567
  def parse_orders(self, swarm_spec: SwarmSpec) -> List[Any]:
@@ -590,9 +589,6 @@ class HierarchicalSwarm(BaseSwarm):
590
589
  f"Error parsing and executing orders: {str(e)}"
591
590
  )
592
591
  logger.error(error_msg)
593
- self.conversation.add(
594
- role="System", content=f"Error: {error_msg}"
595
- )
596
592
  return [error_msg]
597
593
 
598
594
  def parse_swarm_spec(
@@ -668,19 +664,14 @@ class HierarchicalSwarm(BaseSwarm):
668
664
  :param swarm_spec: The SwarmSpec containing the goals, plan, and rules.
669
665
  """
670
666
  try:
671
- goals = swarm_spec.goals
672
- plan = swarm_spec.plan
673
- rules = swarm_spec.rules
674
-
667
+ # Directly access and format attributes in one line
675
668
  self.conversation.add(
676
669
  role="Director",
677
- content=f"Goals: {goals}\nPlan: {plan}\nRules: {rules}",
670
+ content=f"Goals:\n{swarm_spec.goals}\n\nPlan:\n{swarm_spec.plan}\n\nRules:\n{swarm_spec.rules}",
678
671
  )
679
672
  except Exception as e:
680
- error_msg = f"Error adding goals and plan to conversation: {str(e)}"
681
- logger.error(error_msg)
682
- self.conversation.add(
683
- role="System", content=f"Error: {error_msg}"
673
+ logger.error(
674
+ f"Error adding goals and plan to conversation: {str(e)}"
684
675
  )
685
676
 
686
677
  def batch_run(
@@ -714,23 +705,3 @@ class HierarchicalSwarm(BaseSwarm):
714
705
  "Output is neither a dictionary nor a string."
715
706
  )
716
707
  return {}
717
-
718
- def concurrent_run(
719
- self, tasks: List[str], img: str = None
720
- ) -> List[Union[str, Dict, List]]:
721
- """
722
- Runs multiple tasks concurrently through the swarm.
723
-
724
- :param tasks: The list of tasks to be executed.
725
- :param img: Optional image to be used with the tasks.
726
- :return: A list of outputs from each task execution.
727
- """
728
- with ThreadPoolExecutor(max_workers=len(tasks)) as executor:
729
- # Create a list of partial functions with the img parameter
730
- task_functions = [(task, img) for task in tasks]
731
- # Use starmap to unpack the arguments
732
- return list(
733
- executor.map(
734
- lambda args: self.run(*args), task_functions
735
- )
736
- )
@@ -0,0 +1,96 @@
1
+ from swarms.structs.agent import Agent
2
+ from typing import List, Any, Optional, Union
3
+ import random
4
+
5
+
6
+ def list_all_agents(
7
+ agents: List[Union[Agent, Any]],
8
+ conversation: Optional[Any] = None,
9
+ name: str = "",
10
+ add_to_conversation: bool = False,
11
+ ) -> str:
12
+ """Lists all agents in a swarm and optionally adds them to a conversation.
13
+
14
+ This function compiles information about all agents in a swarm, including their names and descriptions.
15
+ It can optionally add this information to a conversation history.
16
+
17
+ Args:
18
+ agents (List[Union[Agent, Any]]): List of agents to list information about
19
+ conversation (Any): Conversation object to optionally add agent information to
20
+ name (str): Name of the swarm/group of agents
21
+ add_to_conversation (bool, optional): Whether to add agent information to conversation. Defaults to False.
22
+
23
+ Returns:
24
+ str: Formatted string containing information about all agents
25
+
26
+ Example:
27
+ >>> agents = [agent1, agent2]
28
+ >>> conversation = Conversation()
29
+ >>> agent_info = list_all_agents(agents, conversation, "MySwarm")
30
+ >>> print(agent_info)
31
+ Total Agents: 2
32
+
33
+ Agent: Agent1
34
+ Description: First agent description...
35
+
36
+ Agent: Agent2
37
+ Description: Second agent description...
38
+ """
39
+
40
+ # Compile information about all agents
41
+ total_agents = len(agents)
42
+
43
+ all_agents = f"Total Agents: {total_agents}\n\n" + "\n\n".join(
44
+ f"Agent: {agent.agent_name} \n\n Description: {agent.description or (agent.system_prompt[:50] + '...' if len(agent.system_prompt) > 50 else agent.system_prompt)}"
45
+ for agent in agents
46
+ )
47
+
48
+ if add_to_conversation:
49
+ # Add the agent information to the conversation
50
+ conversation.add(
51
+ role="System",
52
+ content=f"All Agents Available in the Swarm {name}:\n\n{all_agents}",
53
+ )
54
+
55
+ return all_agents
56
+
57
+
58
+ models = [
59
+ "anthropic/claude-3-sonnet-20240229",
60
+ "openai/gpt-4o-mini",
61
+ "openai/gpt-4o",
62
+ "deepseek/deepseek-chat",
63
+ "deepseek/deepseek-reasoner",
64
+ "groq/deepseek-r1-distill-qwen-32b",
65
+ "groq/deepseek-r1-distill-qwen-32b",
66
+ # "gemini/gemini-pro",
67
+ # "gemini/gemini-1.5-pro",
68
+ "openai/03-mini",
69
+ "o4-mini",
70
+ "o3",
71
+ "gpt-4.1",
72
+ "gpt-4.1-nano",
73
+ ]
74
+
75
+
76
+ def set_random_models_for_agents(
77
+ agents: Union[List[Agent], Agent], model_names: List[str] = models
78
+ ) -> Union[List[Agent], Agent]:
79
+ """Sets random models for agents in the swarm.
80
+
81
+ Args:
82
+ agents (Union[List[Agent], Agent]): Either a single agent or a list of agents
83
+ model_names (List[str], optional): List of model names to choose from. Defaults to models.
84
+
85
+ Returns:
86
+ Union[List[Agent], Agent]: The agent(s) with randomly assigned models
87
+ """
88
+ if isinstance(agents, list):
89
+ return [
90
+ setattr(agent, "model_name", random.choice(model_names))
91
+ or agent
92
+ for agent in agents
93
+ ]
94
+ else:
95
+ setattr(agents, "model_name", random.choice(model_names))
96
+ return agents