swarms 7.7.9__py3-none-any.whl → 7.8.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.
@@ -0,0 +1,424 @@
1
+ import concurrent.futures
2
+ import os
3
+ from typing import Union, List
4
+ import PyPDF2
5
+ import markdown
6
+ from pathlib import Path
7
+ from swarms.utils.litellm_tokenizer import count_tokens
8
+ from swarms.structs.agent import Agent
9
+ from swarms.structs.conversation import Conversation
10
+ from swarms.utils.history_output_formatter import (
11
+ history_output_formatter,
12
+ )
13
+ from swarms.utils.formatter import formatter
14
+
15
+
16
+ class LongAgent:
17
+ """
18
+ A class to handle and process long-form content from various sources including PDFs,
19
+ markdown files, and large text documents.
20
+ """
21
+
22
+ def __init__(
23
+ self,
24
+ name: str = "LongAgent",
25
+ description: str = "A long-form content processing agent",
26
+ token_count_per_agent: int = 16000,
27
+ output_type: str = "final",
28
+ model_name: str = "gpt-4o-mini",
29
+ aggregator_model_name: str = "gpt-4o-mini",
30
+ ):
31
+ """Initialize the LongAgent."""
32
+ self.name = name
33
+ self.description = description
34
+ self.model_name = model_name
35
+ self.aggregator_model_name = aggregator_model_name
36
+ self.content = ""
37
+ self.metadata = {}
38
+ self.token_count_per_agent = token_count_per_agent
39
+ self.output_type = output_type
40
+ self.agents = []
41
+ self.conversation = Conversation()
42
+
43
+ def load_pdf(self, file_path: Union[str, Path]) -> str:
44
+ """
45
+ Load and extract text from a PDF file.
46
+
47
+ Args:
48
+ file_path (Union[str, Path]): Path to the PDF file
49
+
50
+ Returns:
51
+ str: Extracted text from the PDF
52
+ """
53
+ if not os.path.exists(file_path):
54
+ raise FileNotFoundError(
55
+ f"PDF file not found at {file_path}"
56
+ )
57
+
58
+ text = ""
59
+ with open(file_path, "rb") as file:
60
+ pdf_reader = PyPDF2.PdfReader(file)
61
+ for page in pdf_reader.pages:
62
+ text += page.extract_text()
63
+
64
+ self.content = text
65
+ self.metadata["source"] = "pdf"
66
+ self.metadata["file_path"] = str(file_path)
67
+ return text
68
+
69
+ def load_markdown(self, file_path: Union[str, Path]) -> str:
70
+ """
71
+ Load and process a markdown file.
72
+
73
+ Args:
74
+ file_path (Union[str, Path]): Path to the markdown file
75
+
76
+ Returns:
77
+ str: Processed markdown content
78
+ """
79
+ if not os.path.exists(file_path):
80
+ raise FileNotFoundError(
81
+ f"Markdown file not found at {file_path}"
82
+ )
83
+
84
+ with open(file_path, "r", encoding="utf-8") as file:
85
+ content = file.read()
86
+
87
+ # Convert markdown to HTML for processing
88
+ markdown.markdown(content)
89
+
90
+ self.content = content
91
+ self.metadata["source"] = "markdown"
92
+ self.metadata["file_path"] = str(file_path)
93
+ return content
94
+
95
+ def load_text(self, text: str) -> str:
96
+ """
97
+ Load and process a large text string.
98
+
99
+ Args:
100
+ text (str): The text content to process
101
+
102
+ Returns:
103
+ str: The processed text
104
+ """
105
+ self.content = text
106
+ self.metadata["source"] = "text"
107
+ return text
108
+
109
+ def get_content(self) -> str:
110
+ """
111
+ Get the current content being processed.
112
+
113
+ Returns:
114
+ str: The current content
115
+ """
116
+ return self.content
117
+
118
+ def get_metadata(self) -> dict:
119
+ """
120
+ Get the metadata associated with the current content.
121
+
122
+ Returns:
123
+ dict: The metadata dictionary
124
+ """
125
+ return self.metadata
126
+
127
+ def count_token_document(
128
+ self, file_path: Union[str, Path]
129
+ ) -> int:
130
+ """
131
+ Count the number of tokens in a document.
132
+
133
+ Args:
134
+ document (str): The document to count tokens for
135
+ """
136
+ if file_path.endswith(".pdf"):
137
+ count = count_tokens(self.load_pdf(file_path))
138
+ formatter.print_panel(
139
+ f"Token count for {file_path}: {count}",
140
+ title="Token Count",
141
+ )
142
+ print(f"Token count for {file_path}: {count}")
143
+ elif file_path.endswith(".md"):
144
+ count = count_tokens(self.load_markdown(file_path))
145
+ formatter.print_panel(
146
+ f"Token count for {file_path}: {count}",
147
+ title="Token Count",
148
+ )
149
+ print(f"Token count for {file_path}: {count}")
150
+ elif file_path.endswith(".txt"):
151
+ count = count_tokens(self.load_text(file_path))
152
+ formatter.print_panel(
153
+ f"Token count for {file_path}: {count}",
154
+ title="Token Count",
155
+ )
156
+ print(f"Token count for {file_path}: {count}")
157
+ else:
158
+ raise ValueError(f"Unsupported file type: {file_path}")
159
+ return count
160
+
161
+ def count_multiple_documents(
162
+ self, file_paths: List[Union[str, Path]]
163
+ ) -> int:
164
+ """
165
+ Count the number of tokens in multiple documents.
166
+
167
+ Args:
168
+ file_paths (List[Union[str, Path]]): The list of file paths to count tokens for
169
+
170
+ Returns:
171
+ int: Total token count across all documents
172
+ """
173
+ total_tokens = 0
174
+ # Calculate max_workers as 20% of CPU count
175
+ max_workers = max(1, int(os.cpu_count() * 0.2))
176
+
177
+ with concurrent.futures.ThreadPoolExecutor(
178
+ max_workers=max_workers
179
+ ) as executor:
180
+ futures = [
181
+ executor.submit(self.count_token_document, file_path)
182
+ for file_path in file_paths
183
+ ]
184
+ for future in concurrent.futures.as_completed(futures):
185
+ try:
186
+ total_tokens += future.result()
187
+ except Exception as e:
188
+ formatter.print_panel(
189
+ f"Error processing document: {str(e)}",
190
+ title="Error",
191
+ )
192
+ continue
193
+ return total_tokens
194
+
195
+ def create_agents_for_documents(
196
+ self, file_paths: List[Union[str, Path]]
197
+ ) -> List[Agent]:
198
+ """
199
+ Create agents for each document chunk and process them.
200
+
201
+ Args:
202
+ file_paths (List[Union[str, Path]]): The list of file paths to create agents for
203
+
204
+ Returns:
205
+ List[Agent]: List of created agents
206
+ """
207
+ for file_path in file_paths:
208
+ # Load the document content
209
+ if str(file_path).endswith(".pdf"):
210
+ content = self.load_pdf(file_path)
211
+ elif str(file_path).endswith(".md"):
212
+ content = self.load_markdown(file_path)
213
+ else:
214
+ content = self.load_text(str(file_path))
215
+
216
+ # Split content into chunks based on token count
217
+ chunks = self._split_into_chunks(content)
218
+
219
+ # Create an agent for each chunk
220
+ for i, chunk in enumerate(chunks):
221
+ agent = Agent(
222
+ agent_name=f"Document Analysis Agent - {Path(file_path).name} - Chunk {i+1}",
223
+ system_prompt="""
224
+ You are an expert document analysis and summarization agent specialized in processing and understanding complex documents. Your primary responsibilities include:
225
+
226
+ 1. Document Analysis:
227
+ - Thoroughly analyze the provided document chunk
228
+ - Identify key themes, main arguments, and important details
229
+ - Extract critical information and relationships between concepts
230
+
231
+ 2. Summarization Capabilities:
232
+ - Create concise yet comprehensive summaries
233
+ - Generate both high-level overviews and detailed breakdowns
234
+ - Highlight key points, findings, and conclusions
235
+ - Maintain context and relationships between different sections
236
+
237
+ 3. Information Extraction:
238
+ - Identify and extract important facts, figures, and data points
239
+ - Recognize and preserve technical terminology and domain-specific concepts
240
+ - Maintain accuracy in representing the original content
241
+
242
+ 4. Response Format:
243
+ - Provide clear, structured responses
244
+ - Use bullet points for key findings
245
+ - Include relevant quotes or references when necessary
246
+ - Maintain professional and academic tone
247
+
248
+ 5. Context Awareness:
249
+ - Consider the document's purpose and target audience
250
+ - Adapt your analysis based on the document type (academic, technical, general)
251
+ - Preserve the original meaning and intent
252
+
253
+ Your goal is to help users understand and extract value from this document chunk while maintaining accuracy and completeness in your analysis.
254
+ """,
255
+ model_name=self.model_name,
256
+ max_loops=1,
257
+ max_tokens=self.token_count_per_agent,
258
+ )
259
+
260
+ # Run the agent on the chunk
261
+ output = agent.run(
262
+ f"Please analyze and summarize the following document chunk:\n\n{chunk}"
263
+ )
264
+
265
+ # Add the output to the conversation
266
+ self.conversation.add(
267
+ role=agent.agent_name,
268
+ content=output,
269
+ )
270
+
271
+ self.agents.append(agent)
272
+
273
+ return self.agents
274
+
275
+ def _split_into_chunks(self, content: str) -> List[str]:
276
+ """
277
+ Split content into chunks based on token count.
278
+
279
+ Args:
280
+ content (str): The content to split
281
+
282
+ Returns:
283
+ List[str]: List of content chunks
284
+ """
285
+ chunks = []
286
+ current_chunk = ""
287
+ current_tokens = 0
288
+
289
+ # Split content into sentences (simple approach)
290
+ sentences = content.split(". ")
291
+
292
+ for sentence in sentences:
293
+ sentence_tokens = count_tokens(sentence)
294
+
295
+ if (
296
+ current_tokens + sentence_tokens
297
+ > self.token_count_per_agent
298
+ ):
299
+ if current_chunk:
300
+ chunks.append(current_chunk)
301
+ current_chunk = sentence
302
+ current_tokens = sentence_tokens
303
+ else:
304
+ current_chunk += (
305
+ ". " + sentence if current_chunk else sentence
306
+ )
307
+ current_tokens += sentence_tokens
308
+
309
+ if current_chunk:
310
+ chunks.append(current_chunk)
311
+
312
+ return chunks
313
+
314
+ def count_total_agents(self) -> int:
315
+ """
316
+ Count the total number of agents.
317
+ """
318
+ count = len(self.agents)
319
+ formatter.print_panel(f"Total agents created: {count}")
320
+ return count
321
+
322
+ def _create_aggregator_agent(self) -> Agent:
323
+ """
324
+ Create an aggregator agent for synthesizing document summaries.
325
+
326
+ Returns:
327
+ Agent: The configured aggregator agent
328
+ """
329
+ return Agent(
330
+ agent_name="Document Aggregator Agent",
331
+ system_prompt="""
332
+ You are an expert document synthesis agent specialized in creating comprehensive reports from multiple document summaries. Your responsibilities include:
333
+
334
+ 1. Synthesis and Integration:
335
+ - Combine multiple document summaries into a coherent narrative
336
+ - Identify and resolve any contradictions or inconsistencies
337
+ - Maintain logical flow and structure in the final report
338
+ - Preserve important details while eliminating redundancy
339
+
340
+ 2. Report Structure:
341
+ - Create a clear, hierarchical structure for the report
342
+ - Include an executive summary at the beginning
343
+ - Organize content into logical sections with clear headings
344
+ - Ensure smooth transitions between different topics
345
+
346
+ 3. Analysis and Insights:
347
+ - Identify overarching themes and patterns across summaries
348
+ - Draw meaningful conclusions from the combined information
349
+ - Highlight key findings and their implications
350
+ - Provide context and connections between different pieces of information
351
+
352
+ 4. Quality Assurance:
353
+ - Ensure factual accuracy and consistency
354
+ - Maintain professional and academic tone
355
+ - Verify that all important information is included
356
+ - Check for clarity and readability
357
+
358
+ Your goal is to create a comprehensive, well-structured report that effectively synthesizes all the provided document summaries into a single coherent document.
359
+ """,
360
+ model_name=self.aggregator_model_name,
361
+ max_loops=1,
362
+ max_tokens=self.token_count_per_agent,
363
+ )
364
+
365
+ def run(self, file_paths: List[Union[str, Path]]) -> str:
366
+ """
367
+ Run the document processing pipeline and generate a comprehensive report.
368
+
369
+ Args:
370
+ file_paths (List[Union[str, Path]]): The list of file paths to process
371
+
372
+ Returns:
373
+ str: The final comprehensive report
374
+ """
375
+ # Count total tokens
376
+ total_tokens = self.count_multiple_documents(file_paths)
377
+ formatter.print_panel(
378
+ f"Total tokens: {total_tokens}", title="Total Tokens"
379
+ )
380
+
381
+ total_amount_of_agents = (
382
+ total_tokens / self.token_count_per_agent
383
+ )
384
+ formatter.print_panel(
385
+ f"Total amount of agents: {total_amount_of_agents}",
386
+ title="Total Amount of Agents",
387
+ )
388
+
389
+ # First, process all documents and create chunk agents
390
+ self.create_agents_for_documents(file_paths)
391
+
392
+ # Format the number of agents
393
+ # formatter.print_panel(f"Number of agents: {len(self.agents)}", title="Number of Agents")
394
+
395
+ # Create aggregator agent and collect summaries
396
+ aggregator_agent = self._create_aggregator_agent()
397
+ combined_summaries = self.conversation.get_str()
398
+
399
+ # Generate the final comprehensive report
400
+ final_report = aggregator_agent.run(
401
+ f"""
402
+ Please create a comprehensive report by synthesizing the following document summaries:
403
+
404
+ {combined_summaries}
405
+
406
+ Please structure your response as follows:
407
+ 1. Executive Summary
408
+ 2. Main Findings and Analysis
409
+ 3. Key Themes and Patterns
410
+ 4. Detailed Breakdown by Topic
411
+ 5. Conclusions and Implications
412
+
413
+ Ensure the report is well-organized, comprehensive, and maintains a professional tone throughout.
414
+ """
415
+ )
416
+
417
+ # Add the final report to the conversation
418
+ self.conversation.add(
419
+ role="Document Aggregator Agent", content=final_report
420
+ )
421
+
422
+ return history_output_formatter(
423
+ conversation=self.conversation, type=self.output_type
424
+ )
@@ -1,10 +1,9 @@
1
- from swarms.structs.agent import Agent
2
- from typing import List, Any, Optional, Union
1
+ from typing import List, Any, Optional, Union, Callable
3
2
  import random
4
3
 
5
4
 
6
5
  def list_all_agents(
7
- agents: List[Union[Agent, Any]],
6
+ agents: List[Union[Callable, Any]],
8
7
  conversation: Optional[Any] = None,
9
8
  name: str = "",
10
9
  add_to_conversation: bool = False,
@@ -74,17 +73,21 @@ models = [
74
73
 
75
74
 
76
75
  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.
76
+ agents: Optional[Union[List[Callable], Callable]] = None,
77
+ model_names: List[str] = models,
78
+ ) -> Union[List[Callable], Callable, str]:
79
+ """Sets random models for agents in the swarm or returns a random model name.
80
80
 
81
81
  Args:
82
- agents (Union[List[Agent], Agent]): Either a single agent or a list of agents
82
+ agents (Optional[Union[List[Agent], Agent]]): Either a single agent, list of agents, or None
83
83
  model_names (List[str], optional): List of model names to choose from. Defaults to models.
84
84
 
85
85
  Returns:
86
- Union[List[Agent], Agent]: The agent(s) with randomly assigned models
86
+ Union[List[Agent], Agent, str]: The agent(s) with randomly assigned models or a random model name
87
87
  """
88
+ if agents is None:
89
+ return random.choice(model_names)
90
+
88
91
  if isinstance(agents, list):
89
92
  return [
90
93
  setattr(agent, "model_name", random.choice(model_names))
swarms/structs/malt.py CHANGED
@@ -198,7 +198,7 @@ class MALT:
198
198
  system_prompt=proof_verifier_prompt,
199
199
  )
200
200
 
201
- majority_voting_agent = Agent(
201
+ Agent(
202
202
  agent_name="Majority-Voting-Agent",
203
203
  model_name="gpt-4o-mini",
204
204
  max_loops=1,
@@ -24,6 +24,7 @@ from swarms.structs.output_types import OutputType
24
24
  from swarms.utils.loguru_logger import initialize_logger
25
25
  from swarms.structs.malt import MALT
26
26
  from swarms.structs.deep_research_swarm import DeepResearchSwarm
27
+ from swarms.structs.council_judge import CouncilAsAJudge
27
28
 
28
29
  logger = initialize_logger(log_folder="swarm_router")
29
30
 
@@ -41,6 +42,7 @@ SwarmType = Literal[
41
42
  "MajorityVoting",
42
43
  "MALT",
43
44
  "DeepResearchSwarm",
45
+ "CouncilAsAJudge",
44
46
  ]
45
47
 
46
48
 
@@ -225,13 +227,7 @@ class SwarmRouter:
225
227
  csv_path=self.csv_file_path
226
228
  ).load_agents()
227
229
 
228
- # Log initialization
229
- self._log(
230
- "info",
231
- f"SwarmRouter initialized with swarm type: {swarm_type}",
232
- )
233
-
234
- # Handle Automated Prompt Engineering
230
+ def setup(self):
235
231
  if self.auto_generate_prompts is True:
236
232
  self.activate_ape()
237
233
 
@@ -289,18 +285,52 @@ class SwarmRouter:
289
285
  raise RuntimeError(error_msg) from e
290
286
 
291
287
  def reliability_check(self):
292
- logger.info("Initializing reliability checks")
288
+ """Perform reliability checks on swarm configuration.
293
289
 
294
- if not self.agents:
295
- raise ValueError("No agents provided for the swarm.")
290
+ Validates essential swarm parameters and configuration before execution.
291
+ Handles special case for CouncilAsAJudge which may not require agents.
292
+ """
293
+ logger.info(
294
+ "🔍 [SYSTEM] Initializing advanced swarm reliability diagnostics..."
295
+ )
296
+ logger.info(
297
+ "⚡ [SYSTEM] Running pre-flight checks and system validation..."
298
+ )
299
+
300
+ # Check swarm type first since it affects other validations
296
301
  if self.swarm_type is None:
302
+ logger.error(
303
+ "❌ [CRITICAL] Swarm type validation failed - type cannot be 'none'"
304
+ )
297
305
  raise ValueError("Swarm type cannot be 'none'.")
306
+
307
+ # Special handling for CouncilAsAJudge
308
+ if self.swarm_type == "CouncilAsAJudge":
309
+ if self.agents is not None:
310
+ logger.warning(
311
+ "⚠️ [ADVISORY] CouncilAsAJudge detected with agents - this is atypical"
312
+ )
313
+ elif not self.agents:
314
+ logger.error(
315
+ "❌ [CRITICAL] Agent validation failed - no agents detected in swarm"
316
+ )
317
+ raise ValueError("No agents provided for the swarm.")
318
+
319
+ # Validate max_loops
298
320
  if self.max_loops == 0:
321
+ logger.error(
322
+ "❌ [CRITICAL] Loop validation failed - max_loops cannot be 0"
323
+ )
299
324
  raise ValueError("max_loops cannot be 0.")
300
325
 
326
+ # Setup other functionality
327
+ logger.info("🔄 [SYSTEM] Initializing swarm subsystems...")
328
+ self.setup()
329
+
301
330
  logger.info(
302
- "Reliability checks completed your swarm is ready."
331
+ " [SYSTEM] All reliability checks passed successfully"
303
332
  )
333
+ logger.info("🚀 [SYSTEM] Swarm is ready for deployment")
304
334
 
305
335
  def _create_swarm(
306
336
  self, task: str = None, *args, **kwargs
@@ -358,6 +388,15 @@ class SwarmRouter:
358
388
  preset_agents=True,
359
389
  )
360
390
 
391
+ elif self.swarm_type == "CouncilAsAJudge":
392
+ return CouncilAsAJudge(
393
+ name=self.name,
394
+ description=self.description,
395
+ model_name=self.model_name,
396
+ output_type=self.output_type,
397
+ base_agent=self.agents[0] if self.agents else None,
398
+ )
399
+
361
400
  elif self.swarm_type == "DeepResearchSwarm":
362
401
  return DeepResearchSwarm(
363
402
  name=self.name,
@@ -496,7 +535,14 @@ class SwarmRouter:
496
535
  self.logs.append(log_entry)
497
536
  logger.log(level.upper(), message)
498
537
 
499
- def _run(self, task: str, img: str, *args, **kwargs) -> Any:
538
+ def _run(
539
+ self,
540
+ task: str,
541
+ img: str,
542
+ model_response: str,
543
+ *args,
544
+ **kwargs,
545
+ ) -> Any:
500
546
  """
501
547
  Dynamically run the specified task on the selected or matched swarm type.
502
548
 
@@ -520,7 +566,16 @@ class SwarmRouter:
520
566
  logger.info(
521
567
  f"Running task on {self.swarm_type} swarm with task: {task}"
522
568
  )
523
- result = self.swarm.run(task=task, *args, **kwargs)
569
+
570
+ if self.swarm_type == "CouncilAsAJudge":
571
+ result = self.swarm.run(
572
+ task=task,
573
+ model_response=model_response,
574
+ *args,
575
+ **kwargs,
576
+ )
577
+ else:
578
+ result = self.swarm.run(task=task, *args, **kwargs)
524
579
 
525
580
  logger.info("Swarm completed successfully")
526
581
  return result
swarms/tools/__init__.py CHANGED
@@ -27,6 +27,13 @@ from swarms.tools.cohere_func_call_schema import (
27
27
  )
28
28
  from swarms.tools.tool_registry import ToolStorage, tool_registry
29
29
  from swarms.tools.json_utils import base_model_to_json
30
+ from swarms.tools.mcp_client_call import (
31
+ execute_tool_call_simple,
32
+ _execute_tool_call_simple,
33
+ get_tools_for_multiple_mcp_servers,
34
+ get_mcp_tools_sync,
35
+ aget_mcp_tools,
36
+ )
30
37
 
31
38
 
32
39
  __all__ = [
@@ -50,4 +57,9 @@ __all__ = [
50
57
  "ToolStorage",
51
58
  "tool_registry",
52
59
  "base_model_to_json",
60
+ "execute_tool_call_simple",
61
+ "_execute_tool_call_simple",
62
+ "get_tools_for_multiple_mcp_servers",
63
+ "get_mcp_tools_sync",
64
+ "aget_mcp_tools",
53
65
  ]