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.
@@ -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,18 @@ 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,
200
185
  *args,
201
186
  **kwargs,
202
187
  ):
188
+ self.id = id
203
189
  self.name = name
204
190
  self.description = description
205
191
  self.max_loops = max_loops
@@ -213,13 +199,15 @@ class SwarmRouter:
213
199
  self.rules = rules
214
200
  self.documents = documents
215
201
  self.output_type = output_type
216
- self.no_cluster_ops = no_cluster_ops
217
202
  self.speaker_fn = speaker_fn
218
203
  self.logs = []
219
204
  self.load_agents_from_csv = load_agents_from_csv
220
205
  self.csv_file_path = csv_file_path
221
206
  self.return_entire_history = return_entire_history
222
207
  self.multi_agent_collab_prompt = multi_agent_collab_prompt
208
+ self.list_all_agents = list_all_agents
209
+ self.conversation = conversation
210
+ self.agents_config = agents_config
223
211
 
224
212
  # Reliability check
225
213
  self.reliability_check()
@@ -230,6 +218,8 @@ class SwarmRouter:
230
218
  csv_path=self.csv_file_path
231
219
  ).load_agents()
232
220
 
221
+ self.agent_config = self.agent_config()
222
+
233
223
  def setup(self):
234
224
  if self.auto_generate_prompts is True:
235
225
  self.activate_ape()
@@ -276,15 +266,12 @@ class SwarmRouter:
276
266
  logger.info(
277
267
  f"Successfully activated APE for {activated_count} agents"
278
268
  )
279
- self._log(
280
- "info",
281
- f"Activated automatic prompt engineering for {activated_count} agents",
282
- )
283
269
 
284
270
  except Exception as e:
285
271
  error_msg = f"Error activating automatic prompt engineering: {str(e)}"
286
- logger.error(error_msg)
287
- self._log("error", error_msg)
272
+ logger.error(
273
+ f"Error activating automatic prompt engineering in SwarmRouter: {str(e)}"
274
+ )
288
275
  raise RuntimeError(error_msg) from e
289
276
 
290
277
  def reliability_check(self):
@@ -293,48 +280,24 @@ class SwarmRouter:
293
280
  Validates essential swarm parameters and configuration before execution.
294
281
  Handles special case for CouncilAsAJudge which may not require agents.
295
282
  """
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
283
 
303
284
  # Check swarm type first since it affects other validations
304
285
  if self.swarm_type is None:
305
- logger.error(
306
- " [CRITICAL] Swarm type validation failed - type cannot be 'none'"
286
+ raise ValueError(
287
+ "SwarmRouter: Swarm type cannot be 'none'."
307
288
  )
308
- raise ValueError("Swarm type cannot be 'none'.")
309
289
 
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"
290
+ if self.agents is None:
291
+ raise ValueError(
292
+ "SwarmRouter: No agents provided for the swarm."
319
293
  )
320
- raise ValueError("No agents provided for the swarm.")
321
294
 
322
295
  # Validate max_loops
323
296
  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.")
297
+ raise ValueError("SwarmRouter: max_loops cannot be 0.")
328
298
 
329
- # Setup other functionality
330
- logger.info("🔄 [SYSTEM] Initializing swarm subsystems...")
331
299
  self.setup()
332
300
 
333
- logger.info(
334
- "✅ [SYSTEM] All reliability checks passed successfully"
335
- )
336
- logger.info("🚀 [SYSTEM] Swarm is ready for deployment")
337
-
338
301
  def _create_swarm(self, task: str = None, *args, **kwargs):
339
302
  """
340
303
  Dynamically create and return the specified swarm type or automatically match the best swarm type for a given task.
@@ -509,37 +472,19 @@ class SwarmRouter:
509
472
  for agent in self.agents
510
473
  ]
511
474
 
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.
475
+ def agent_config(self):
476
+ agent_config = {}
477
+ for agent in self.agents:
478
+ agent_config[agent.agent_name] = agent.to_dict()
521
479
 
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)
480
+ return agent_config
537
481
 
538
482
  def _run(
539
483
  self,
540
484
  task: str,
541
485
  img: Optional[str] = None,
542
486
  model_response: Optional[str] = None,
487
+ imgs: Optional[List[str]] = None,
543
488
  *args,
544
489
  **kwargs,
545
490
  ) -> Any:
@@ -559,17 +504,34 @@ class SwarmRouter:
559
504
  """
560
505
  self.swarm = self._create_swarm(task, *args, **kwargs)
561
506
 
507
+ self.conversation = self.swarm.conversation
508
+
509
+ if self.list_all_agents is True:
510
+ list_all_agents(
511
+ agents=self.agents,
512
+ conversation=self.swarm.conversation,
513
+ name=self.name,
514
+ description=self.description,
515
+ add_collaboration_prompt=True,
516
+ add_to_conversation=True,
517
+ )
518
+
562
519
  if self.multi_agent_collab_prompt is True:
563
520
  self.update_system_prompt_for_agent_in_swarm()
564
521
 
565
- try:
566
- logger.info(
567
- f"Running task on {self.swarm_type} swarm with task: {task}"
568
- )
522
+ log_execution(
523
+ swarm_id=self.id,
524
+ status="start",
525
+ swarm_config=self.to_dict(),
526
+ swarm_architecture="swarm_router",
527
+ )
569
528
 
529
+ try:
570
530
  if self.swarm_type == "CouncilAsAJudge":
571
531
  result = self.swarm.run(
572
532
  task=task,
533
+ img=img,
534
+ imgs=imgs,
573
535
  model_response=model_response,
574
536
  *args,
575
537
  **kwargs,
@@ -577,21 +539,24 @@ class SwarmRouter:
577
539
  else:
578
540
  result = self.swarm.run(task=task, *args, **kwargs)
579
541
 
580
- logger.info("Swarm completed successfully")
542
+ log_execution(
543
+ swarm_id=self.id,
544
+ status="completion",
545
+ swarm_config=self.to_dict(),
546
+ swarm_architecture="swarm_router",
547
+ )
548
+
581
549
  return result
582
550
  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)},
551
+ raise RuntimeError(
552
+ f"SwarmRouter: Error executing task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
588
553
  )
589
- raise
590
554
 
591
555
  def run(
592
556
  self,
593
557
  task: str,
594
558
  img: Optional[str] = None,
559
+ imgs: Optional[List[str]] = None,
595
560
  model_response: Optional[str] = None,
596
561
  *args,
597
562
  **kwargs,
@@ -617,15 +582,24 @@ class SwarmRouter:
617
582
  return self._run(
618
583
  task=task,
619
584
  img=img,
585
+ imgs=imgs,
620
586
  model_response=model_response,
621
587
  *args,
622
588
  **kwargs,
623
589
  )
624
590
  except Exception as e:
625
- logger.error(f"Error executing task on swarm: {str(e)}")
626
- raise
591
+ raise RuntimeError(
592
+ f"SwarmRouter: Error executing task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
593
+ )
627
594
 
628
- def __call__(self, task: str, *args, **kwargs) -> Any:
595
+ def __call__(
596
+ self,
597
+ task: str,
598
+ img: Optional[str] = None,
599
+ imgs: Optional[List[str]] = None,
600
+ *args,
601
+ **kwargs,
602
+ ) -> Any:
629
603
  """
630
604
  Make the SwarmRouter instance callable.
631
605
 
@@ -637,10 +611,17 @@ class SwarmRouter:
637
611
  Returns:
638
612
  Any: The result of the swarm's execution.
639
613
  """
640
- return self.run(task=task, *args, **kwargs)
614
+ return self.run(
615
+ task=task, img=img, imgs=imgs, *args, **kwargs
616
+ )
641
617
 
642
618
  def batch_run(
643
- self, tasks: List[str], *args, **kwargs
619
+ self,
620
+ tasks: List[str],
621
+ img: Optional[str] = None,
622
+ imgs: Optional[List[str]] = None,
623
+ *args,
624
+ **kwargs,
644
625
  ) -> List[Any]:
645
626
  """
646
627
  Execute a batch of tasks on the selected or matched swarm type.
@@ -659,21 +640,26 @@ class SwarmRouter:
659
640
  results = []
660
641
  for task in tasks:
661
642
  try:
662
- result = self.run(task, *args, **kwargs)
643
+ result = self.run(
644
+ task, img=img, imgs=imgs, *args, **kwargs
645
+ )
663
646
  results.append(result)
664
647
  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)},
648
+ raise RuntimeError(
649
+ f"SwarmRouter: Error executing batch task on swarm: {str(e)} Traceback: {traceback.format_exc()}"
670
650
  )
671
- raise
672
651
  return results
673
652
 
674
- def async_run(self, task: str, *args, **kwargs) -> Any:
653
+ def concurrent_run(
654
+ self,
655
+ task: str,
656
+ img: Optional[str] = None,
657
+ imgs: Optional[List[str]] = None,
658
+ *args,
659
+ **kwargs,
660
+ ) -> Any:
675
661
  """
676
- Execute a task on the selected or matched swarm type asynchronously.
662
+ Execute a task on the selected or matched swarm type concurrently.
677
663
 
678
664
  Args:
679
665
  task (str): The task to be executed by the swarm.
@@ -686,95 +672,70 @@ class SwarmRouter:
686
672
  Raises:
687
673
  Exception: If an error occurs during task execution.
688
674
  """
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
675
 
706
- return asyncio.run(run_async())
676
+ with concurrent.futures.ThreadPoolExecutor(
677
+ max_workers=os.cpu_count()
678
+ ) as executor:
679
+ future = executor.submit(
680
+ self.run, task, img=img, imgs=imgs, *args, **kwargs
681
+ )
682
+ result = future.result()
683
+ return result
707
684
 
708
- def get_logs(self) -> List[SwarmLog]:
685
+ def _serialize_callable(
686
+ self, attr_value: Callable
687
+ ) -> Dict[str, Any]:
709
688
  """
710
- Retrieve all logged entries.
689
+ Serializes callable attributes by extracting their name and docstring.
690
+
691
+ Args:
692
+ attr_value (Callable): The callable to serialize.
711
693
 
712
694
  Returns:
713
- List[SwarmLog]: A list of all log entries.
695
+ Dict[str, Any]: Dictionary with name and docstring of the callable.
714
696
  """
715
- return self.logs
716
-
717
- def concurrent_run(self, task: str, *args, **kwargs) -> Any:
697
+ return {
698
+ "name": getattr(
699
+ attr_value, "__name__", type(attr_value).__name__
700
+ ),
701
+ "doc": getattr(attr_value, "__doc__", None),
702
+ }
703
+
704
+ def _serialize_attr(self, attr_name: str, attr_value: Any) -> Any:
718
705
  """
719
- Execute a task on the selected or matched swarm type concurrently.
706
+ Serializes an individual attribute, handling non-serializable objects.
720
707
 
721
708
  Args:
722
- task (str): The task to be executed by the swarm.
723
- *args: Variable length argument list.
724
- **kwargs: Arbitrary keyword arguments.
709
+ attr_name (str): The name of the attribute.
710
+ attr_value (Any): The value of the attribute.
725
711
 
726
712
  Returns:
727
- Any: The result of the swarm's execution.
728
-
729
- Raises:
730
- Exception: If an error occurs during task execution.
713
+ Any: The serialized value of the attribute.
731
714
  """
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]:
715
+ try:
716
+ if callable(attr_value):
717
+ return self._serialize_callable(attr_value)
718
+ elif hasattr(attr_value, "to_dict"):
719
+ return (
720
+ attr_value.to_dict()
721
+ ) # Recursive serialization for nested objects
722
+ else:
723
+ json.dumps(
724
+ attr_value
725
+ ) # Attempt to serialize to catch non-serializable objects
726
+ return attr_value
727
+ except (TypeError, ValueError):
728
+ return f"<Non-serializable: {type(attr_value).__name__}>"
729
+
730
+ def to_dict(self) -> Dict[str, Any]:
744
731
  """
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.
732
+ Converts all attributes of the class, including callables, into a dictionary.
733
+ Handles non-serializable attributes by converting them or skipping them.
751
734
 
752
735
  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.
736
+ Dict[str, Any]: A dictionary representation of the class attributes.
757
737
  """
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
738
+ return {
739
+ attr_name: self._serialize_attr(attr_name, attr_value)
740
+ for attr_name, attr_value in self.__dict__.items()
741
+ }
@@ -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