swarms 7.8.9__py3-none-any.whl → 7.9.1__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.
@@ -1,6 +1,7 @@
1
+ import concurrent.futures
2
+ import json
1
3
  import os
2
- import uuid
3
- from datetime import datetime
4
+ import traceback
4
5
  from typing import Any, Callable, Dict, List, Literal, Optional, Union
5
6
 
6
7
  from pydantic import BaseModel, Field
@@ -20,13 +21,15 @@ from swarms.structs.rearrange import AgentRearrange
20
21
  from swarms.structs.sequential_workflow import SequentialWorkflow
21
22
  from swarms.structs.spreadsheet_swarm import SpreadSheetSwarm
22
23
  from swarms.structs.swarm_matcher import swarm_matcher
24
+ from swarms.telemetry.log_executions import log_execution
23
25
  from swarms.utils.output_types import OutputType
24
26
  from swarms.utils.loguru_logger import initialize_logger
25
27
  from swarms.structs.malt import MALT
26
28
  from swarms.structs.deep_research_swarm import DeepResearchSwarm
27
29
  from swarms.structs.council_judge import CouncilAsAJudge
28
30
  from swarms.structs.interactive_groupchat import InteractiveGroupChat
29
-
31
+ from swarms.structs.ma_utils import list_all_agents
32
+ from swarms.utils.generate_keys import generate_api_key
30
33
 
31
34
  logger = initialize_logger(log_folder="swarm_router")
32
35
 
@@ -54,25 +57,6 @@ class Document(BaseModel):
54
57
  data: str
55
58
 
56
59
 
57
- class SwarmLog(BaseModel):
58
- """
59
- A Pydantic model to capture log entries.
60
- """
61
-
62
- id: Optional[str] = Field(
63
- default_factory=lambda: str(uuid.uuid4())
64
- )
65
- timestamp: Optional[datetime] = Field(
66
- default_factory=datetime.utcnow
67
- )
68
- level: Optional[str] = None
69
- message: Optional[str] = None
70
- swarm_type: Optional[SwarmType] = None
71
- task: Optional[str] = ""
72
- metadata: Optional[Dict[str, Any]] = Field(default_factory=dict)
73
- documents: List[Document] = []
74
-
75
-
76
60
  class SwarmRouterConfig(BaseModel):
77
61
  """Configuration model for SwarmRouter."""
78
62
 
@@ -172,12 +156,11 @@ class SwarmRouter:
172
156
  concurrent_batch_run(tasks: List[str], *args, **kwargs) -> List[Any]:
173
157
  Executes multiple tasks concurrently
174
158
 
175
- get_logs() -> List[SwarmLog]:
176
- Retrieves execution logs
177
159
  """
178
160
 
179
161
  def __init__(
180
162
  self,
163
+ id: str = generate_api_key(prefix="swarm-router"),
181
164
  name: str = "swarm-router",
182
165
  description: str = "Routes your task to the desired swarm",
183
166
  max_loops: int = 1,
@@ -191,15 +174,19 @@ class SwarmRouter:
191
174
  rules: str = None,
192
175
  documents: List[str] = [], # A list of docs file paths
193
176
  output_type: OutputType = "dict-all-except-first",
194
- no_cluster_ops: bool = False,
195
177
  speaker_fn: callable = None,
196
178
  load_agents_from_csv: bool = False,
197
179
  csv_file_path: str = None,
198
180
  return_entire_history: bool = True,
199
181
  multi_agent_collab_prompt: bool = True,
182
+ list_all_agents: bool = False,
183
+ conversation: Any = None,
184
+ agents_config: Optional[Dict[Any, Any]] = None,
185
+ speaker_function: str = None,
200
186
  *args,
201
187
  **kwargs,
202
188
  ):
189
+ self.id = id
203
190
  self.name = name
204
191
  self.description = description
205
192
  self.max_loops = max_loops
@@ -213,13 +200,16 @@ class SwarmRouter:
213
200
  self.rules = rules
214
201
  self.documents = documents
215
202
  self.output_type = output_type
216
- self.no_cluster_ops = no_cluster_ops
217
203
  self.speaker_fn = speaker_fn
218
204
  self.logs = []
219
205
  self.load_agents_from_csv = load_agents_from_csv
220
206
  self.csv_file_path = csv_file_path
221
207
  self.return_entire_history = return_entire_history
222
208
  self.multi_agent_collab_prompt = multi_agent_collab_prompt
209
+ self.list_all_agents = list_all_agents
210
+ self.conversation = conversation
211
+ self.agents_config = agents_config
212
+ self.speaker_function = speaker_function
223
213
 
224
214
  # Reliability check
225
215
  self.reliability_check()
@@ -230,6 +220,8 @@ class SwarmRouter:
230
220
  csv_path=self.csv_file_path
231
221
  ).load_agents()
232
222
 
223
+ self.agent_config = self.agent_config()
224
+
233
225
  def setup(self):
234
226
  if self.auto_generate_prompts is True:
235
227
  self.activate_ape()
@@ -276,15 +268,12 @@ class SwarmRouter:
276
268
  logger.info(
277
269
  f"Successfully activated APE for {activated_count} agents"
278
270
  )
279
- self._log(
280
- "info",
281
- f"Activated automatic prompt engineering for {activated_count} agents",
282
- )
283
271
 
284
272
  except Exception as e:
285
273
  error_msg = f"Error activating automatic prompt engineering: {str(e)}"
286
- logger.error(error_msg)
287
- self._log("error", error_msg)
274
+ logger.error(
275
+ f"Error activating automatic prompt engineering in SwarmRouter: {str(e)}"
276
+ )
288
277
  raise RuntimeError(error_msg) from e
289
278
 
290
279
  def reliability_check(self):
@@ -293,48 +282,24 @@ class SwarmRouter:
293
282
  Validates essential swarm parameters and configuration before execution.
294
283
  Handles special case for CouncilAsAJudge which may not require agents.
295
284
  """
296
- logger.info(
297
- "🔍 [SYSTEM] Initializing advanced swarm reliability diagnostics..."
298
- )
299
- logger.info(
300
- "⚡ [SYSTEM] Running pre-flight checks and system validation..."
301
- )
302
285
 
303
286
  # Check swarm type first since it affects other validations
304
287
  if self.swarm_type is None:
305
- logger.error(
306
- " [CRITICAL] Swarm type validation failed - type cannot be 'none'"
288
+ raise ValueError(
289
+ "SwarmRouter: Swarm type cannot be 'none'."
307
290
  )
308
- raise ValueError("Swarm type cannot be 'none'.")
309
291
 
310
- # Special handling for CouncilAsAJudge
311
- if self.swarm_type == "CouncilAsAJudge":
312
- if self.agents is not None:
313
- logger.warning(
314
- "⚠️ [ADVISORY] CouncilAsAJudge detected with agents - this is atypical"
315
- )
316
- elif not self.agents:
317
- logger.error(
318
- "❌ [CRITICAL] Agent validation failed - no agents detected in swarm"
292
+ if self.agents is None:
293
+ raise ValueError(
294
+ "SwarmRouter: No agents provided for the swarm."
319
295
  )
320
- raise ValueError("No agents provided for the swarm.")
321
296
 
322
297
  # Validate max_loops
323
298
  if self.max_loops == 0:
324
- logger.error(
325
- "❌ [CRITICAL] Loop validation failed - max_loops cannot be 0"
326
- )
327
- raise ValueError("max_loops cannot be 0.")
299
+ raise ValueError("SwarmRouter: max_loops cannot be 0.")
328
300
 
329
- # Setup other functionality
330
- logger.info("🔄 [SYSTEM] Initializing swarm subsystems...")
331
301
  self.setup()
332
302
 
333
- logger.info(
334
- "✅ [SYSTEM] All reliability checks passed successfully"
335
- )
336
- logger.info("🚀 [SYSTEM] Swarm is ready for deployment")
337
-
338
303
  def _create_swarm(self, task: str = None, *args, **kwargs):
339
304
  """
340
305
  Dynamically create and return the specified swarm type or automatically match the best swarm type for a given task.
@@ -395,6 +360,7 @@ class SwarmRouter:
395
360
  agents=self.agents,
396
361
  max_loops=self.max_loops,
397
362
  output_type=self.output_type,
363
+ speaker_function=self.speaker_function,
398
364
  )
399
365
 
400
366
  elif self.swarm_type == "DeepResearchSwarm":
@@ -500,46 +466,24 @@ class SwarmRouter:
500
466
 
501
467
  def update_system_prompt_for_agent_in_swarm(self):
502
468
  # Use list comprehension for faster iteration
503
- [
504
- setattr(
505
- agent,
506
- "system_prompt",
507
- agent.system_prompt + MULTI_AGENT_COLLAB_PROMPT_TWO,
508
- )
509
- for agent in self.agents
510
- ]
469
+ for agent in self.agents:
470
+ if agent.system_prompt is None:
471
+ agent.system_prompt = ""
472
+ agent.system_prompt += MULTI_AGENT_COLLAB_PROMPT_TWO
511
473
 
512
- def _log(
513
- self,
514
- level: str,
515
- message: str,
516
- task: str = "",
517
- metadata: Dict[str, Any] = None,
518
- ):
519
- """
520
- Create a log entry and add it to the logs list.
474
+ def agent_config(self):
475
+ agent_config = {}
476
+ for agent in self.agents:
477
+ agent_config[agent.agent_name] = agent.to_dict()
521
478
 
522
- Args:
523
- level (str): The log level (e.g., "info", "error").
524
- message (str): The log message.
525
- task (str, optional): The task being performed. Defaults to "".
526
- metadata (Dict[str, Any], optional): Additional metadata. Defaults to None.
527
- """
528
- log_entry = SwarmLog(
529
- level=level,
530
- message=message,
531
- swarm_type=self.swarm_type,
532
- task=task,
533
- metadata=metadata or {},
534
- )
535
- self.logs.append(log_entry)
536
- logger.log(level.upper(), message)
479
+ return agent_config
537
480
 
538
481
  def _run(
539
482
  self,
540
483
  task: str,
541
484
  img: Optional[str] = None,
542
485
  model_response: Optional[str] = None,
486
+ imgs: Optional[List[str]] = None,
543
487
  *args,
544
488
  **kwargs,
545
489
  ) -> Any:
@@ -559,17 +503,34 @@ class SwarmRouter:
559
503
  """
560
504
  self.swarm = self._create_swarm(task, *args, **kwargs)
561
505
 
506
+ self.conversation = self.swarm.conversation
507
+
508
+ if self.list_all_agents is True:
509
+ list_all_agents(
510
+ agents=self.agents,
511
+ conversation=self.swarm.conversation,
512
+ name=self.name,
513
+ description=self.description,
514
+ add_collaboration_prompt=True,
515
+ add_to_conversation=True,
516
+ )
517
+
562
518
  if self.multi_agent_collab_prompt is True:
563
519
  self.update_system_prompt_for_agent_in_swarm()
564
520
 
565
- try:
566
- logger.info(
567
- f"Running task on {self.swarm_type} swarm with task: {task}"
568
- )
521
+ log_execution(
522
+ swarm_id=self.id,
523
+ status="start",
524
+ swarm_config=self.to_dict(),
525
+ swarm_architecture="swarm_router",
526
+ )
569
527
 
528
+ try:
570
529
  if self.swarm_type == "CouncilAsAJudge":
571
530
  result = self.swarm.run(
572
531
  task=task,
532
+ img=img,
533
+ imgs=imgs,
573
534
  model_response=model_response,
574
535
  *args,
575
536
  **kwargs,
@@ -577,21 +538,24 @@ class SwarmRouter:
577
538
  else:
578
539
  result = self.swarm.run(task=task, *args, **kwargs)
579
540
 
580
- logger.info("Swarm completed successfully")
541
+ log_execution(
542
+ swarm_id=self.id,
543
+ status="completion",
544
+ swarm_config=self.to_dict(),
545
+ swarm_architecture="swarm_router",
546
+ )
547
+
581
548
  return result
582
549
  except Exception as e:
583
- self._log(
584
- "error",
585
- f"Error occurred while running task on {self.swarm_type} swarm: {str(e)}",
586
- task=task,
587
- metadata={"error": str(e)},
550
+ raise RuntimeError(
551
+ f"SwarmRouter: Error executing task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
588
552
  )
589
- raise
590
553
 
591
554
  def run(
592
555
  self,
593
556
  task: str,
594
557
  img: Optional[str] = None,
558
+ imgs: Optional[List[str]] = None,
595
559
  model_response: Optional[str] = None,
596
560
  *args,
597
561
  **kwargs,
@@ -617,15 +581,24 @@ class SwarmRouter:
617
581
  return self._run(
618
582
  task=task,
619
583
  img=img,
584
+ imgs=imgs,
620
585
  model_response=model_response,
621
586
  *args,
622
587
  **kwargs,
623
588
  )
624
589
  except Exception as e:
625
- logger.error(f"Error executing task on swarm: {str(e)}")
626
- raise
590
+ raise RuntimeError(
591
+ f"SwarmRouter: Error executing task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
592
+ )
627
593
 
628
- def __call__(self, task: str, *args, **kwargs) -> Any:
594
+ def __call__(
595
+ self,
596
+ task: str,
597
+ img: Optional[str] = None,
598
+ imgs: Optional[List[str]] = None,
599
+ *args,
600
+ **kwargs,
601
+ ) -> Any:
629
602
  """
630
603
  Make the SwarmRouter instance callable.
631
604
 
@@ -637,10 +610,17 @@ class SwarmRouter:
637
610
  Returns:
638
611
  Any: The result of the swarm's execution.
639
612
  """
640
- return self.run(task=task, *args, **kwargs)
613
+ return self.run(
614
+ task=task, img=img, imgs=imgs, *args, **kwargs
615
+ )
641
616
 
642
617
  def batch_run(
643
- self, tasks: List[str], *args, **kwargs
618
+ self,
619
+ tasks: List[str],
620
+ img: Optional[str] = None,
621
+ imgs: Optional[List[str]] = None,
622
+ *args,
623
+ **kwargs,
644
624
  ) -> List[Any]:
645
625
  """
646
626
  Execute a batch of tasks on the selected or matched swarm type.
@@ -659,21 +639,26 @@ class SwarmRouter:
659
639
  results = []
660
640
  for task in tasks:
661
641
  try:
662
- result = self.run(task, *args, **kwargs)
642
+ result = self.run(
643
+ task, img=img, imgs=imgs, *args, **kwargs
644
+ )
663
645
  results.append(result)
664
646
  except Exception as e:
665
- self._log(
666
- "error",
667
- f"Error occurred while running batch task on {self.swarm_type} swarm: {str(e)}",
668
- task=task,
669
- metadata={"error": str(e)},
647
+ raise RuntimeError(
648
+ f"SwarmRouter: Error executing batch task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
670
649
  )
671
- raise
672
650
  return results
673
651
 
674
- def async_run(self, task: str, *args, **kwargs) -> Any:
652
+ def concurrent_run(
653
+ self,
654
+ task: str,
655
+ img: Optional[str] = None,
656
+ imgs: Optional[List[str]] = None,
657
+ *args,
658
+ **kwargs,
659
+ ) -> Any:
675
660
  """
676
- Execute a task on the selected or matched swarm type asynchronously.
661
+ Execute a task on the selected or matched swarm type concurrently.
677
662
 
678
663
  Args:
679
664
  task (str): The task to be executed by the swarm.
@@ -686,95 +671,70 @@ class SwarmRouter:
686
671
  Raises:
687
672
  Exception: If an error occurs during task execution.
688
673
  """
689
- import asyncio
690
-
691
- async def run_async():
692
- try:
693
- result = await asyncio.to_thread(
694
- self.run, task, *args, **kwargs
695
- )
696
- return result
697
- except Exception as e:
698
- self._log(
699
- "error",
700
- f"Error occurred while running task asynchronously on {self.swarm_type} swarm: {str(e)}",
701
- task=task,
702
- metadata={"error": str(e)},
703
- )
704
- raise
705
674
 
706
- return asyncio.run(run_async())
675
+ with concurrent.futures.ThreadPoolExecutor(
676
+ max_workers=os.cpu_count()
677
+ ) as executor:
678
+ future = executor.submit(
679
+ self.run, task, img=img, imgs=imgs, *args, **kwargs
680
+ )
681
+ result = future.result()
682
+ return result
707
683
 
708
- def get_logs(self) -> List[SwarmLog]:
684
+ def _serialize_callable(
685
+ self, attr_value: Callable
686
+ ) -> Dict[str, Any]:
709
687
  """
710
- Retrieve all logged entries.
688
+ Serializes callable attributes by extracting their name and docstring.
689
+
690
+ Args:
691
+ attr_value (Callable): The callable to serialize.
711
692
 
712
693
  Returns:
713
- List[SwarmLog]: A list of all log entries.
694
+ Dict[str, Any]: Dictionary with name and docstring of the callable.
714
695
  """
715
- return self.logs
716
-
717
- def concurrent_run(self, task: str, *args, **kwargs) -> Any:
696
+ return {
697
+ "name": getattr(
698
+ attr_value, "__name__", type(attr_value).__name__
699
+ ),
700
+ "doc": getattr(attr_value, "__doc__", None),
701
+ }
702
+
703
+ def _serialize_attr(self, attr_name: str, attr_value: Any) -> Any:
718
704
  """
719
- Execute a task on the selected or matched swarm type concurrently.
705
+ Serializes an individual attribute, handling non-serializable objects.
720
706
 
721
707
  Args:
722
- task (str): The task to be executed by the swarm.
723
- *args: Variable length argument list.
724
- **kwargs: Arbitrary keyword arguments.
708
+ attr_name (str): The name of the attribute.
709
+ attr_value (Any): The value of the attribute.
725
710
 
726
711
  Returns:
727
- Any: The result of the swarm's execution.
728
-
729
- Raises:
730
- Exception: If an error occurs during task execution.
712
+ Any: The serialized value of the attribute.
731
713
  """
732
- from concurrent.futures import ThreadPoolExecutor
733
-
734
- with ThreadPoolExecutor(
735
- max_workers=os.cpu_count()
736
- ) as executor:
737
- future = executor.submit(self.run, task, *args, **kwargs)
738
- result = future.result()
739
- return result
740
-
741
- def concurrent_batch_run(
742
- self, tasks: List[str], *args, **kwargs
743
- ) -> List[Any]:
714
+ try:
715
+ if callable(attr_value):
716
+ return self._serialize_callable(attr_value)
717
+ elif hasattr(attr_value, "to_dict"):
718
+ return (
719
+ attr_value.to_dict()
720
+ ) # Recursive serialization for nested objects
721
+ else:
722
+ json.dumps(
723
+ attr_value
724
+ ) # Attempt to serialize to catch non-serializable objects
725
+ return attr_value
726
+ except (TypeError, ValueError):
727
+ return f"<Non-serializable: {type(attr_value).__name__}>"
728
+
729
+ def to_dict(self) -> Dict[str, Any]:
744
730
  """
745
- Execute a batch of tasks on the selected or matched swarm type concurrently.
746
-
747
- Args:
748
- tasks (List[str]): A list of tasks to be executed by the swarm.
749
- *args: Variable length argument list.
750
- **kwargs: Arbitrary keyword arguments.
731
+ Converts all attributes of the class, including callables, into a dictionary.
732
+ Handles non-serializable attributes by converting them or skipping them.
751
733
 
752
734
  Returns:
753
- List[Any]: A list of results from the swarm's execution.
754
-
755
- Raises:
756
- Exception: If an error occurs during task execution.
735
+ Dict[str, Any]: A dictionary representation of the class attributes.
757
736
  """
758
- from concurrent.futures import (
759
- ThreadPoolExecutor,
760
- as_completed,
761
- )
762
-
763
- results = []
764
- with ThreadPoolExecutor() as executor:
765
- # Submit all tasks to executor
766
- futures = [
767
- executor.submit(self.run, task, *args, **kwargs)
768
- for task in tasks
769
- ]
770
-
771
- # Process results as they complete rather than waiting for all
772
- for future in as_completed(futures):
773
- try:
774
- result = future.result()
775
- results.append(result)
776
- except Exception as e:
777
- logger.error(f"Task execution failed: {str(e)}")
778
- results.append(None)
779
-
780
- return results
737
+ return {
738
+ attr_name: self._serialize_attr(attr_name, attr_value)
739
+ for attr_name, attr_value in self.__dict__.items()
740
+ }
@@ -1,27 +1,13 @@
1
1
  from swarms.telemetry.main import (
2
- generate_unique_identifier,
3
2
  generate_user_id,
4
- get_cpu_info,
5
3
  get_machine_id,
6
- get_os_version,
7
- get_pip_version,
8
- get_python_version,
9
- get_ram_info,
10
- get_system_info,
11
- get_user_device_data,
12
- system_info,
4
+ get_comprehensive_system_info,
5
+ log_agent_data,
13
6
  )
14
7
 
15
8
  __all__ = [
16
9
  "generate_user_id",
17
10
  "get_machine_id",
18
- "get_system_info",
19
- "generate_unique_identifier",
20
- "get_python_version",
21
- "get_pip_version",
22
- "get_os_version",
23
- "get_cpu_info",
24
- "get_ram_info",
25
- "system_info",
26
- "get_user_device_data",
11
+ "get_comprehensive_system_info",
12
+ "log_agent_data",
27
13
  ]
@@ -0,0 +1,43 @@
1
+ from typing import Optional
2
+ from swarms.telemetry.main import log_agent_data
3
+
4
+
5
+ def log_execution(
6
+ swarm_id: Optional[str] = None,
7
+ status: Optional[str] = None,
8
+ swarm_config: Optional[dict] = None,
9
+ swarm_architecture: Optional[str] = None,
10
+ ):
11
+ """
12
+ Log execution data for a swarm router instance.
13
+
14
+ This function logs telemetry data about swarm router executions, including
15
+ the swarm ID, execution status, and configuration details. It silently
16
+ handles any logging errors to prevent execution interruption.
17
+
18
+ Args:
19
+ swarm_id (str): Unique identifier for the swarm router instance
20
+ status (str): Current status of the execution (e.g., "start", "completion", "error")
21
+ swarm_config (dict): Configuration dictionary containing swarm router settings
22
+ swarm_architecture (str): Name of the swarm architecture used
23
+ Returns:
24
+ None
25
+
26
+ Example:
27
+ >>> log_execution(
28
+ ... swarm_id="swarm-router-abc123",
29
+ ... status="start",
30
+ ... swarm_config={"name": "my-swarm", "swarm_type": "SequentialWorkflow"}
31
+ ... )
32
+ """
33
+ try:
34
+ log_agent_data(
35
+ data_dict={
36
+ "swarm_router_id": swarm_id,
37
+ "status": status,
38
+ "swarm_router_config": swarm_config,
39
+ "swarm_architecture": swarm_architecture,
40
+ }
41
+ )
42
+ except Exception:
43
+ pass