swarms 7.6.2__py3-none-any.whl → 7.6.5__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/__init__.py +1 -0
  2. swarms/agents/__init__.py +0 -3
  3. swarms/agents/flexion_agent.py +2 -1
  4. swarms/client/__init__.py +15 -0
  5. swarms/prompts/multi_agent_collab_prompt.py +313 -0
  6. swarms/structs/__init__.py +5 -17
  7. swarms/structs/agent.py +219 -255
  8. swarms/structs/base_swarm.py +0 -7
  9. swarms/structs/concurrent_workflow.py +1 -1
  10. swarms/structs/conversation.py +16 -2
  11. swarms/structs/de_hallucination_swarm.py +8 -4
  12. swarms/structs/groupchat.py +80 -84
  13. swarms/structs/hybrid_hiearchical_peer_swarm.py +23 -40
  14. swarms/structs/multi_agent_exec.py +63 -139
  15. swarms/structs/rearrange.py +65 -204
  16. swarms/structs/sequential_workflow.py +34 -47
  17. swarms/structs/swarm_router.py +2 -1
  18. swarms/telemetry/bootup.py +19 -38
  19. swarms/telemetry/main.py +56 -20
  20. swarms/tools/mcp_integration.py +321 -483
  21. swarms/utils/auto_download_check_packages.py +2 -2
  22. swarms/utils/disable_logging.py +0 -17
  23. swarms/utils/history_output_formatter.py +8 -3
  24. swarms/utils/litellm_wrapper.py +117 -1
  25. swarms/utils/vllm_wrapper.py +146 -0
  26. {swarms-7.6.2.dist-info → swarms-7.6.5.dist-info}/METADATA +1 -5
  27. {swarms-7.6.2.dist-info → swarms-7.6.5.dist-info}/RECORD +31 -31
  28. swarms/structs/auto_swarm.py +0 -229
  29. swarms/utils/agent_ops_check.py +0 -26
  30. swarms/utils/pandas_utils.py +0 -92
  31. /swarms/{structs/swarms_api.py → client/main.py} +0 -0
  32. {swarms-7.6.2.dist-info → swarms-7.6.5.dist-info}/LICENSE +0 -0
  33. {swarms-7.6.2.dist-info → swarms-7.6.5.dist-info}/WHEEL +0 -0
  34. {swarms-7.6.2.dist-info → swarms-7.6.5.dist-info}/entry_points.txt +0 -0
@@ -119,7 +119,7 @@ class Conversation(BaseStructure):
119
119
  content (Union[str, dict, list]): The content of the message to be added.
120
120
  """
121
121
  now = datetime.datetime.now()
122
- timestamp = now.strftime("%Y-%m-%d %H:%M:%S")
122
+ now.strftime("%Y-%m-%d %H:%M:%S")
123
123
 
124
124
  # Base message with role
125
125
  message = {
@@ -129,8 +129,12 @@ class Conversation(BaseStructure):
129
129
  # Handle different content types
130
130
  if isinstance(content, dict) or isinstance(content, list):
131
131
  message["content"] = content
132
+ elif self.time_enabled:
133
+ message["content"] = (
134
+ f"Time: {now.strftime('%Y-%m-%d %H:%M:%S')} \n {content}"
135
+ )
132
136
  else:
133
- message["content"] = f"Time: {timestamp} \n {content}"
137
+ message["content"] = content
134
138
 
135
139
  # Add the message to history immediately without waiting for token count
136
140
  self.conversation_history.append(message)
@@ -510,6 +514,16 @@ class Conversation(BaseStructure):
510
514
  """
511
515
  return f"{self.conversation_history[-1]['role']}: {self.conversation_history[-1]['content']}"
512
516
 
517
+ def get_final_message_content(self):
518
+ """Return the content of the final message from the conversation history.
519
+
520
+ Returns:
521
+ str: The content of the final message.
522
+ """
523
+ output = self.conversation_history[-1]["content"]
524
+ # print(output)
525
+ return output
526
+
513
527
 
514
528
  # # Example usage
515
529
  # # conversation = Conversation()
@@ -4,7 +4,8 @@ from loguru import logger
4
4
  from swarms.structs.agent import Agent
5
5
 
6
6
  # Prompt templates for different agent roles
7
- GENERATOR_PROMPT = """You are a knowledgeable assistant tasked with providing accurate information on a wide range of topics.
7
+ GENERATOR_PROMPT = """
8
+ You are a knowledgeable assistant tasked with providing accurate information on a wide range of topics.
8
9
 
9
10
  Your responsibilities:
10
11
  1. Provide accurate information based on your training data
@@ -22,7 +23,8 @@ When responding to queries:
22
23
  Remember, it's better to acknowledge ignorance than to provide incorrect information.
23
24
  """
24
25
 
25
- CRITIC_PROMPT = """You are a critical reviewer tasked with identifying potential inaccuracies, hallucinations, or unsupported claims in AI-generated text.
26
+ CRITIC_PROMPT = """
27
+ You are a critical reviewer tasked with identifying potential inaccuracies, hallucinations, or unsupported claims in AI-generated text.
26
28
 
27
29
  Your responsibilities:
28
30
  1. Carefully analyze the provided text for factual errors
@@ -47,7 +49,8 @@ Focus particularly on:
47
49
  Be thorough and specific in your critique. Provide actionable feedback for improvement.
48
50
  """
49
51
 
50
- REFINER_PROMPT = """You are a refinement specialist tasked with improving text based on critical feedback.
52
+ REFINER_PROMPT = """
53
+ You are a refinement specialist tasked with improving text based on critical feedback.
51
54
 
52
55
  Your responsibilities:
53
56
  1. Carefully review the original text and the critical feedback
@@ -67,7 +70,8 @@ Guidelines for refinement:
67
70
  The refined text should be helpful and informative while being scrupulously accurate.
68
71
  """
69
72
 
70
- VALIDATOR_PROMPT = """You are a validation expert tasked with ensuring the highest standards of accuracy in refined AI outputs.
73
+ VALIDATOR_PROMPT = """
74
+ You are a validation expert tasked with ensuring the highest standards of accuracy in refined AI outputs.
71
75
 
72
76
  Your responsibilities:
73
77
  1. Verify that all critical issues from previous feedback have been properly addressed
@@ -1,4 +1,5 @@
1
1
  import concurrent.futures
2
+ import random
2
3
  from datetime import datetime
3
4
  from typing import Callable, List
4
5
 
@@ -7,6 +8,13 @@ from pydantic import BaseModel, Field
7
8
 
8
9
  from swarms.structs.agent import Agent
9
10
  from swarms.structs.conversation import Conversation
11
+ from swarms.structs.multi_agent_exec import get_agents_info
12
+ from swarms.utils.history_output_formatter import (
13
+ history_output_formatter,
14
+ )
15
+ from swarms.prompts.multi_agent_collab_prompt import (
16
+ MULTI_AGENT_COLLAB_PROMPT_TWO,
17
+ )
10
18
 
11
19
 
12
20
  class AgentResponse(BaseModel):
@@ -230,15 +238,23 @@ class GroupChat:
230
238
  speaker_fn: SpeakerFunction = round_robin,
231
239
  max_loops: int = 1,
232
240
  rules: str = "",
241
+ output_type: str = "string",
233
242
  ):
234
243
  self.name = name
235
244
  self.description = description
236
245
  self.agents = agents
237
246
  self.speaker_fn = speaker_fn
238
247
  self.max_loops = max_loops
239
- self.conversation = Conversation(time_enabled=False)
248
+ self.output_type = output_type
240
249
  self.rules = rules
241
250
 
251
+ self.conversation = Conversation(
252
+ time_enabled=False, rules=rules
253
+ )
254
+
255
+ agent_context = f"\n Group Chat Name: {self.name}\nGroup Chat Description: {self.description}\n Agents in your Group Chat: {get_agents_info(self.agents)}"
256
+ self.conversation.add(role="System", content=agent_context)
257
+
242
258
  self.reliability_check()
243
259
 
244
260
  def reliability_check(self):
@@ -248,23 +264,24 @@ class GroupChat:
248
264
  Raises:
249
265
  ValueError: If any required components are missing or invalid
250
266
  """
267
+
251
268
  if len(self.agents) < 2:
252
269
  raise ValueError(
253
270
  "At least two agents are required for a group chat"
254
271
  )
255
- if self.speaker_fn is None:
256
- raise ValueError("No speaker function provided")
272
+
257
273
  if self.max_loops <= 0:
258
274
  raise ValueError("Max loops must be greater than 0")
275
+
259
276
  for agent in self.agents:
260
- if not isinstance(agent, Agent):
261
- raise ValueError(
262
- f"Invalid agent type: {type(agent)}. Must be Agent instance"
263
- )
277
+ agent.system_prompt += MULTI_AGENT_COLLAB_PROMPT_TWO
264
278
 
265
279
  def run(self, task: str, img: str = None, *args, **kwargs) -> str:
266
280
  """
267
- Executes a conversation between agents about the given task.
281
+ Executes a dynamic conversation between agents about the given task.
282
+
283
+ Agents are selected randomly to speak, creating a more natural flow
284
+ with varying conversation lengths.
268
285
 
269
286
  Args:
270
287
  task (str): The task or topic for agents to discuss
@@ -279,106 +296,85 @@ class GroupChat:
279
296
  ValueError: If task is empty or invalid
280
297
  Exception: If any error occurs during conversation
281
298
  """
299
+
282
300
  if not task or not isinstance(task, str):
283
301
  raise ValueError("Task must be a non-empty string")
284
302
 
285
303
  # Initialize conversation with context
286
- agent_context = f"Group Chat Name: {self.name}\nGroup Chat Description: {self.description}\nRules: {self.rules}\n Other agents: {', '.join([a.agent_name for a in self.agents])}"
287
- self.conversation.add(role="system", content=agent_context)
288
304
  self.conversation.add(role="User", content=task)
289
305
 
290
- print(
291
- f"....... conversation history: \n {self.conversation.return_history_as_string()}"
292
- )
293
-
294
306
  try:
295
307
  turn = 0
296
- consecutive_silent_turns = 0
297
- max_silent_turns = 2 # End conversation if no one speaks for this many turns
298
-
299
- while turn < self.max_loops:
300
- context = self.conversation.return_messages_as_list()
301
-
302
- # Get agents who should speak this turn
303
- speaking_agents = [
304
- agent
305
- for agent in self.agents
306
- if self.speaker_fn(context, agent)
307
- ]
308
-
309
- if not speaking_agents:
310
- consecutive_silent_turns += 1
311
- if consecutive_silent_turns >= max_silent_turns:
312
- logger.debug(
313
- "Multiple silent turns, ending conversation"
314
- )
315
- break
316
- continue
308
+ # Determine a random number of conversation turns
309
+ target_turns = random.randint(1, 4)
310
+ logger.debug(
311
+ f"Planning for approximately {target_turns} conversation turns"
312
+ )
317
313
 
318
- consecutive_silent_turns = (
319
- 0 # Reset counter when agents speak
320
- )
314
+ # Keep track of which agent spoke last to create realistic exchanges
315
+ last_speaker = None
321
316
 
322
- # Process each speaking agent
323
- for agent in speaking_agents:
324
- try:
325
- # Build context-aware prompt
326
- prompt = (
327
- f"You're {agent.agent_name} participating in a group chat.\n"
328
- f"Chat Purpose: {self.description}\n"
329
- f"Current Discussion: {task}\n"
330
- f"Chat History:\n{self.conversation.return_history_as_string()}\n"
331
- f"As {agent.agent_name}, please provide your response:"
332
- )
317
+ while turn < target_turns:
333
318
 
334
- print(
335
- f"....... what the agent sees prompt: \n {prompt}"
336
- )
319
+ # Select an agent to speak (different from the last speaker if possible)
320
+ available_agents = self.agents.copy()
337
321
 
338
- message = agent.run(
339
- task=prompt,
340
- img=img,
341
- *args,
342
- **kwargs,
343
- )
322
+ if last_speaker and len(available_agents) > 1:
323
+ available_agents.remove(last_speaker)
344
324
 
345
- if not message or message.isspace():
346
- logger.warning(
347
- f"Empty response from {agent.agent_name}, skipping"
348
- )
349
- continue
325
+ current_speaker = random.choice(available_agents)
326
+
327
+ try:
328
+ # Build complete context with conversation history
329
+ conversation_history = (
330
+ self.conversation.return_history_as_string()
331
+ )
332
+
333
+ # Prepare a prompt that explicitly encourages responding to others
334
+ if last_speaker:
335
+ prompt = f"The previous message was from {last_speaker.agent_name}. As {current_speaker.agent_name}, please respond to what they and others have said about: {task}"
336
+ else:
337
+ prompt = f"As {current_speaker.agent_name}, please start the discussion about: {task}"
350
338
 
339
+ # Get the agent's response with full context awareness
340
+ message = current_speaker.run(
341
+ task=f"{conversation_history} {prompt}",
342
+ )
343
+
344
+ # Only add meaningful responses
345
+ if message and not message.isspace():
351
346
  self.conversation.add(
352
- role=agent.agent_name, content=message
347
+ role=current_speaker.agent_name,
348
+ content=message,
353
349
  )
354
350
 
355
351
  logger.info(
356
- f"Turn {turn}, {agent.agent_name} responded"
352
+ f"Turn {turn}, {current_speaker.agent_name} responded"
357
353
  )
358
354
 
359
- except Exception as e:
360
- logger.error(
361
- f"Error from {agent.agent_name}: {e}"
362
- )
363
- # Continue with other agents instead of crashing
364
- continue
355
+ # Update the last speaker
356
+ last_speaker = current_speaker
357
+ turn += 1
365
358
 
366
- turn += 1
359
+ # Occasionally end early to create natural variation
360
+ if (
361
+ turn > 3 and random.random() < 0.15
362
+ ): # 15% chance to end after at least 3 turns
363
+ logger.debug(
364
+ "Random early conversation end"
365
+ )
366
+ break
367
367
 
368
- # Check if conversation has reached a natural conclusion
369
- last_messages = (
370
- context[-3:] if len(context) >= 3 else context
371
- )
372
- if all(
373
- "conclusion" in msg.lower()
374
- for msg in last_messages
375
- ):
376
- logger.debug(
377
- "Natural conversation conclusion detected"
368
+ except Exception as e:
369
+ logger.error(
370
+ f"Error from {current_speaker.agent_name}: {e}"
378
371
  )
379
- break
372
+ # Skip this agent and continue conversation
373
+ continue
380
374
 
381
- return self.conversation.return_history_as_string()
375
+ return history_output_formatter(
376
+ self.conversation, self.output_type
377
+ )
382
378
 
383
379
  except Exception as e:
384
380
  logger.error(f"Error in chat: {e}")
@@ -2,6 +2,7 @@ import os
2
2
  from typing import List
3
3
  from swarms.structs.agent import Agent
4
4
  from swarms.structs.conversation import Conversation
5
+ from swarms.structs.multi_agent_exec import get_swarms_info
5
6
  from swarms.structs.swarm_router import SwarmRouter
6
7
  from swarms.utils.history_output_formatter import (
7
8
  history_output_formatter,
@@ -120,12 +121,27 @@ class HybridHierarchicalClusterSwarm:
120
121
  self.router_agent = Agent(
121
122
  agent_name="Router Agent",
122
123
  agent_description="A router agent that routes tasks to the appropriate swarms.",
123
- system_prompt=f"{router_system_prompt}\n\n{self.get_swarms_info()}",
124
+ system_prompt=f"{router_system_prompt}\n\n{get_swarms_info()}",
124
125
  tools_list_dictionary=tools,
125
126
  model_name=router_agent_model_name,
126
127
  max_loops=1,
128
+ output_type="final",
127
129
  )
128
130
 
131
+ def convert_str_to_dict(self, response: str):
132
+ # Handle response whether it's a string or dictionary
133
+ if isinstance(response, str):
134
+ try:
135
+ import json
136
+
137
+ response = json.loads(response)
138
+ except json.JSONDecodeError:
139
+ raise ValueError(
140
+ "Invalid JSON response from router agent"
141
+ )
142
+
143
+ return response
144
+
129
145
  def run(self, task: str, *args, **kwargs):
130
146
  """
131
147
  Runs the routing process for a given task.
@@ -146,23 +162,19 @@ class HybridHierarchicalClusterSwarm:
146
162
 
147
163
  response = self.router_agent.run(task=task)
148
164
 
149
- # Handle response whether it's a string or dictionary
150
165
  if isinstance(response, str):
151
- try:
152
- import json
153
-
154
- response = json.loads(response)
155
- except json.JSONDecodeError:
156
- raise ValueError(
157
- "Invalid JSON response from router agent"
158
- )
166
+ response = self.convert_str_to_dict(response)
167
+ else:
168
+ pass
159
169
 
160
170
  swarm_name = response.get("swarm_name")
161
171
  task_description = response.get("task_description")
162
172
 
163
173
  if not swarm_name or not task_description:
164
174
  raise ValueError(
165
- "Invalid response from router agent: missing swarm_name or task_description."
175
+ "Invalid response from router agent: both 'swarm_name' and 'task_description' must be present. "
176
+ f"Received: swarm_name={swarm_name}, task_description={task_description}. "
177
+ f"Please check the response format from the model: {self.router_agent.model_name}."
166
178
  )
167
179
 
168
180
  self.route_task(swarm_name, task_description)
@@ -242,32 +254,3 @@ class HybridHierarchicalClusterSwarm:
242
254
  results.append(f"Error processing task: {str(e)}")
243
255
 
244
256
  return results
245
-
246
- def get_swarms_info(self) -> str:
247
- """
248
- Fetches and formats information about all available swarms in the system.
249
-
250
- Returns:
251
- str: A formatted string containing names and descriptions of all swarms.
252
- """
253
- if not self.swarms:
254
- return "No swarms currently available in the system."
255
-
256
- swarm_info = [
257
- "Available Swarms:",
258
- "",
259
- ] # Empty string for line spacing
260
-
261
- for idx, swarm in enumerate(self.swarms, 1):
262
- swarm_info.extend(
263
- [
264
- f"[Swarm {idx}]",
265
- f"Name: {swarm.name}",
266
- f"Description: {swarm.description}",
267
- f"Length of Agents: {len(swarm.agents)}",
268
- f"Swarm Type: {swarm.swarm_type}",
269
- "", # Empty string for line spacing between swarms
270
- ]
271
- )
272
-
273
- return "\n".join(swarm_info).strip()
@@ -5,7 +5,7 @@ from concurrent.futures import (
5
5
  ThreadPoolExecutor,
6
6
  )
7
7
  from dataclasses import dataclass
8
- from typing import Any, List
8
+ from typing import Any, Callable, List, Union
9
9
 
10
10
  import psutil
11
11
 
@@ -415,141 +415,65 @@ def run_agents_with_tasks_concurrently(
415
415
  )
416
416
 
417
417
 
418
- # from joblib import Parallel, delayed
419
-
420
-
421
- # def run_agents_joblib(
422
- # agents: List[Any],
423
- # tasks: List[str] = [],
424
- # img: List[str] = None,
425
- # max_workers: int = None,
426
- # max_loops: int = 1,
427
- # prefer: str = "threads",
428
- # ) -> List[Any]:
429
- # """
430
- # Executes a list of agents with their corresponding tasks concurrently using joblib.
431
-
432
- # Each agent is expected to have a .run() method that accepts at least:
433
- # - task: A string indicating the task to execute.
434
- # - img: (Optional) A string representing image input.
435
-
436
- # Args:
437
- # agents (List[Any]): A list of agent instances.
438
- # tasks (List[str], optional): A list of task strings. If provided, each agent gets a task.
439
- # If fewer tasks than agents, the first task is reused.
440
- # img (List[str], optional): A list of image strings. If provided, each agent gets an image.
441
- # If fewer images than agents, the first image is reused.
442
- # max_workers (int, optional): The maximum number of processes to use.
443
- # Defaults to all available CPU cores.
444
- # max_loops (int, optional): Number of times to execute the whole batch.
445
-
446
- # Returns:
447
- # List[Any]: The list of results returned by each agent’s run() method.
448
- # """
449
- # max_workers = max_workers or os.cpu_count()
450
- # results = []
451
-
452
- # for _ in range(max_loops):
453
- # results.extend(
454
- # Parallel(n_jobs=max_workers, prefer=prefer)(
455
- # delayed(lambda a, t, i: a.run(task=t, img=i))(
456
- # agent,
457
- # (
458
- # tasks[idx]
459
- # if tasks and idx < len(tasks)
460
- # else (tasks[0] if tasks else "")
461
- # ),
462
- # (
463
- # img[idx]
464
- # if img and idx < len(img)
465
- # else (img[0] if img else None)
466
- # ),
467
- # )
468
- # for idx, agent in enumerate(agents)
469
- # )
470
- # )
471
-
472
- # return results
473
-
474
-
475
- # # Example usage:
476
- # if __name__ == '__main__':
477
- # # Dummy Agent class for demonstration.
478
- # class Agent:
479
- # def __init__(self, agent_name, max_loops, model_name):
480
- # self.agent_name = agent_name
481
- # self.max_loops = max_loops
482
- # self.model_name = model_name
483
-
484
- # def run(self, task: str, img: str = None) -> str:
485
- # img_info = f" with image '{img}'" if img else ""
486
- # return (f"{self.agent_name} using model '{self.model_name}' processed task: '{task}'{img_info}")
487
-
488
- # # Create a few Agent instances.
489
- # agents = [
490
- # Agent(
491
- # agent_name=f"Financial-Analysis-Agent_parallel_swarm{i}",
492
- # max_loops=1,
493
- # model_name="gpt-4o-mini",
494
- # )
495
- # for i in range(3)
496
- # ]
497
-
498
- # task = "How can I establish a ROTH IRA to buy stocks and get a tax break? What are the criteria"
499
- # outputs = run_agents_process_pool(agents, tasks=[task])
500
-
501
- # for i, output in enumerate(outputs):
502
- # print(f"Output from agent {i+1}:\n{output}")
503
-
504
- # # Example usage:
505
- # if __name__ == '__main__':
506
- # # A sample agent class with a run method.
507
- # class SampleAgent:
508
- # def __init__(self, name):
509
- # self.name = name
510
-
511
- # def run(self, task, device, device_id, no_clusterops):
512
- # # Simulate some processing.
513
- # return (f"Agent {self.name} processed task '{task}' on {device} "
514
- # f"(device_id={device_id}), no_clusterops={no_clusterops}")
515
-
516
- # # Create a list of sample agents.
517
- # agents = [SampleAgent(f"Agent_{i}") for i in range(5)]
518
- # # Define tasks; if fewer tasks than agents, the first task will be reused.
519
- # tasks = ["task1", "task2", "task3"]
520
-
521
- # outputs = run_agents_with_tasks_concurrently(
522
- # agents=agents,
523
- # tasks=tasks,
524
- # max_workers=4,
525
- # device="cpu",
526
- # device_id=1,
527
- # all_cores=True,
528
- # no_clusterops=False
529
- # )
530
-
531
- # for output in outputs:
532
- # print(output)
533
-
534
-
535
- # # Example usage:
536
- # if __name__ == "__main__":
537
- # # Initialize your agents (for example, 3 agents)
538
- # agents = [
539
- # Agent(
540
- # agent_name=f"Financial-Analysis-Agent_parallel_swarm{i}",
541
- # max_loops=1,
542
- # model_name="gpt-4o-mini",
543
- # )
544
- # for i in range(3)
545
- # ]
546
-
547
- # # Generate a list of tasks.
548
- # tasks = [
549
- # "How can I establish a ROTH IRA to buy stocks and get a tax break?",
550
- # "What are the criteria for establishing a ROTH IRA?",
551
- # "What are the tax benefits of a ROTH IRA?",
552
- # "How to buy stocks using a ROTH IRA?",
553
- # "What are the limitations of a ROTH IRA?",
554
- # ]
555
- # outputs = run_agents_joblib(agents, tasks)
418
+ def get_swarms_info(swarms: List[Callable]) -> str:
419
+ """
420
+ Fetches and formats information about all available swarms in the system.
421
+
422
+ Returns:
423
+ str: A formatted string containing names and descriptions of all swarms.
424
+ """
425
+ if not swarms:
426
+ return "No swarms currently available in the system."
427
+
428
+ swarm_info = [
429
+ "Available Swarms:",
430
+ "",
431
+ ] # Empty string for line spacing
432
+
433
+ for idx, swarm in enumerate(swarms, 1):
434
+ swarm_info.extend(
435
+ [
436
+ f"[Swarm {idx}]",
437
+ f"Name: {swarm.name}",
438
+ f"Description: {swarm.description}",
439
+ f"Length of Agents: {len(swarm.agents)}",
440
+ f"Swarm Type: {swarm.swarm_type}",
441
+ "", # Empty string for line spacing between swarms
442
+ ]
443
+ )
444
+
445
+ return "\n".join(swarm_info).strip()
446
+
447
+
448
+ def get_agents_info(
449
+ agents: List[Union[Agent, Callable]], team_name: str = None
450
+ ) -> str:
451
+ """
452
+ Fetches and formats information about all available agents in the system.
453
+
454
+ Returns:
455
+ str: A formatted string containing names and descriptions of all swarms.
456
+ """
457
+ if not agents:
458
+ return "No agents currently available in the system."
459
+
460
+ agent_info = [
461
+ f"Available Agents for Team: {team_name}",
462
+ "",
463
+ ] # Empty string for line spacing
464
+
465
+ for idx, agent in enumerate(agents, 1):
466
+ agent_info.extend(
467
+ [
468
+ "\n",
469
+ f"[Agent {idx}]",
470
+ f"Name: {agent.agent_name}",
471
+ f"Description: {agent.agent_description}",
472
+ f"Role: {agent.role}",
473
+ f"Model: {agent.model_name}",
474
+ f"Max Loops: {agent.max_loops}",
475
+ "\n",
476
+ ]
477
+ )
478
+
479
+ return "\n".join(agent_info).strip()