swarms 7.6.0__py3-none-any.whl → 7.6.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.
@@ -177,9 +177,7 @@ class AgentsBuilder:
177
177
  The output from the swarm's execution
178
178
  """
179
179
  logger.info(f"Running swarm on task: {task}")
180
- agents = self._create_agents(
181
- task, image_url, *args, **kwargs
182
- )
180
+ agents = self._create_agents(task, image_url, *args, **kwargs)
183
181
 
184
182
  return agents
185
183
 
@@ -201,26 +199,26 @@ class AgentsBuilder:
201
199
  temperature=0.1,
202
200
  base_model=Agents,
203
201
  model_name=self.model_name,
204
- max_tokens = 8192,
202
+ max_tokens=8192,
205
203
  )
206
204
 
207
205
  agents_dictionary = model.run(task)
208
206
  print(agents_dictionary)
209
-
207
+
210
208
  print(type(agents_dictionary))
211
209
  logger.info("Agents successfully created")
212
210
  logger.info(f"Agents: {len(agents_dictionary.agents)}")
213
211
 
214
212
  if self.return_dictionary:
215
213
  logger.info("Returning dictionary")
216
-
214
+
217
215
  # Convert swarm config to dictionary
218
216
  agents_dictionary = agents_dictionary.model_dump()
219
217
  return agents_dictionary
220
218
  else:
221
219
  logger.info("Returning agents")
222
220
  return self.create_agents(agents_dictionary)
223
-
221
+
224
222
  def create_agents(self, agents_dictionary: Any):
225
223
  # Create agents from config
226
224
  agents = []
@@ -292,4 +290,4 @@ class AgentsBuilder:
292
290
  # builder = AgentsBuilder(model_name="gpt-4o")
293
291
  # agents = builder.run("Create a swarm that can write a book about the history of the world")
294
292
  # print(agents)
295
- # print(type(agents))
293
+ # print(type(agents))
@@ -0,0 +1,482 @@
1
+ import asyncio
2
+ import concurrent.futures
3
+ import os
4
+ from concurrent.futures import ThreadPoolExecutor, as_completed
5
+ from typing import Any, Dict, List, Tuple
6
+
7
+ import aiohttp
8
+ from dotenv import load_dotenv
9
+ from rich.console import Console
10
+
11
+ from swarms.agents.reasoning_duo import ReasoningDuo
12
+ from swarms.structs.agent import Agent
13
+ from swarms.structs.conversation import Conversation
14
+ from swarms.utils.any_to_str import any_to_str
15
+ from swarms.utils.formatter import formatter
16
+ from swarms.utils.history_output_formatter import (
17
+ history_output_formatter,
18
+ )
19
+ from swarms.utils.str_to_dict import str_to_dict
20
+
21
+ console = Console()
22
+ load_dotenv()
23
+
24
+ # Number of worker threads for concurrent operations
25
+ MAX_WORKERS = (
26
+ os.cpu_count() * 2
27
+ ) # Optimal number of workers based on CPU cores
28
+
29
+ ###############################################################################
30
+ # 1. System Prompts for Each Scientist Agent
31
+ ###############################################################################
32
+
33
+
34
+ def format_exa_results(json_data: Dict[str, Any]) -> str:
35
+ """Formats Exa.ai search results into structured text"""
36
+ if "error" in json_data:
37
+ return f"### Error\n{json_data['error']}\n"
38
+
39
+ # Pre-allocate formatted_text list with initial capacity
40
+ formatted_text = []
41
+
42
+ # Extract search metadata
43
+ search_params = json_data.get("effectiveFilters", {})
44
+ query = search_params.get("query", "General web search")
45
+ formatted_text.append(
46
+ f"### Exa Search Results for: '{query}'\n\n---\n"
47
+ )
48
+
49
+ # Process results
50
+ results = json_data.get("results", [])
51
+
52
+ if not results:
53
+ formatted_text.append("No results found.\n")
54
+ return "".join(formatted_text)
55
+
56
+ def process_result(
57
+ result: Dict[str, Any], index: int
58
+ ) -> List[str]:
59
+ """Process a single result in a thread-safe manner"""
60
+ title = result.get("title", "No title")
61
+ url = result.get("url", result.get("id", "No URL"))
62
+ published_date = result.get("publishedDate", "")
63
+
64
+ # Handle highlights efficiently
65
+ highlights = result.get("highlights", [])
66
+ highlight_text = (
67
+ "\n".join(
68
+ (
69
+ h.get("text", str(h))
70
+ if isinstance(h, dict)
71
+ else str(h)
72
+ )
73
+ for h in highlights[:3]
74
+ )
75
+ if highlights
76
+ else "No summary available"
77
+ )
78
+
79
+ return [
80
+ f"{index}. **{title}**\n",
81
+ f" - URL: {url}\n",
82
+ f" - Published: {published_date.split('T')[0] if published_date else 'Date unknown'}\n",
83
+ f" - Key Points:\n {highlight_text}\n\n",
84
+ ]
85
+
86
+ # Process results concurrently
87
+ with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
88
+ future_to_result = {
89
+ executor.submit(process_result, result, i + 1): i
90
+ for i, result in enumerate(results)
91
+ }
92
+
93
+ # Collect results in order
94
+ processed_results = [None] * len(results)
95
+ for future in as_completed(future_to_result):
96
+ idx = future_to_result[future]
97
+ try:
98
+ processed_results[idx] = future.result()
99
+ except Exception as e:
100
+ console.print(
101
+ f"[bold red]Error processing result {idx + 1}: {str(e)}[/bold red]"
102
+ )
103
+ processed_results[idx] = [
104
+ f"Error processing result {idx + 1}: {str(e)}\n"
105
+ ]
106
+
107
+ # Extend formatted text with processed results in correct order
108
+ for result_text in processed_results:
109
+ formatted_text.extend(result_text)
110
+
111
+ return "".join(formatted_text)
112
+
113
+
114
+ async def _async_exa_search(
115
+ query: str, **kwargs: Any
116
+ ) -> Dict[str, Any]:
117
+ """Asynchronous helper function for Exa.ai API requests"""
118
+ api_url = "https://api.exa.ai/search"
119
+ headers = {
120
+ "x-api-key": os.getenv("EXA_API_KEY"),
121
+ "Content-Type": "application/json",
122
+ }
123
+
124
+ payload = {
125
+ "query": query,
126
+ "useAutoprompt": True,
127
+ "numResults": kwargs.get("num_results", 10),
128
+ "contents": {
129
+ "text": True,
130
+ "highlights": {"numSentences": 2},
131
+ },
132
+ **kwargs,
133
+ }
134
+
135
+ try:
136
+ async with aiohttp.ClientSession() as session:
137
+ async with session.post(
138
+ api_url, json=payload, headers=headers
139
+ ) as response:
140
+ if response.status != 200:
141
+ return {
142
+ "error": f"HTTP {response.status}: {await response.text()}"
143
+ }
144
+ return await response.json()
145
+ except Exception as e:
146
+ return {"error": str(e)}
147
+
148
+
149
+ def exa_search(query: str, **kwargs: Any) -> str:
150
+ """Performs web search using Exa.ai API with concurrent processing"""
151
+ try:
152
+ # Run async search in the event loop
153
+ loop = asyncio.new_event_loop()
154
+ asyncio.set_event_loop(loop)
155
+ try:
156
+ response_json = loop.run_until_complete(
157
+ _async_exa_search(query, **kwargs)
158
+ )
159
+ finally:
160
+ loop.close()
161
+
162
+ # Format results concurrently
163
+ formatted_text = format_exa_results(response_json)
164
+
165
+ return formatted_text
166
+
167
+ except Exception as e:
168
+ error_msg = f"Unexpected error: {str(e)}"
169
+ console.print(f"[bold red]{error_msg}[/bold red]")
170
+ return error_msg
171
+
172
+
173
+ # Define the research tools schema
174
+ tools = [
175
+ {
176
+ "type": "function",
177
+ "function": {
178
+ "name": "search_topic",
179
+ "description": "Conduct an in-depth search on a specified topic or subtopic, generating a comprehensive array of highly detailed search queries tailored to the input parameters.",
180
+ "parameters": {
181
+ "type": "object",
182
+ "properties": {
183
+ "depth": {
184
+ "type": "integer",
185
+ "description": "Indicates the level of thoroughness for the search. Values range from 1 to 3, where 1 represents a superficial search and 3 signifies an exploration of the topic.",
186
+ },
187
+ "detailed_queries": {
188
+ "type": "array",
189
+ "description": "An array of highly specific search queries that are generated based on the input query and the specified depth. Each query should be designed to elicit detailed and relevant information from various sources.",
190
+ "items": {
191
+ "type": "string",
192
+ "description": "Each item in this array should represent a unique search query that targets a specific aspect of the main topic, ensuring a comprehensive exploration of the subject matter.",
193
+ },
194
+ },
195
+ },
196
+ "required": ["depth", "detailed_queries"],
197
+ },
198
+ },
199
+ },
200
+ ]
201
+
202
+ RESEARCH_AGENT_PROMPT = """
203
+ You are an advanced research agent specialized in conducting deep, comprehensive research across multiple domains.
204
+ Your task is to:
205
+
206
+ 1. Break down complex topics into searchable subtopics
207
+ 2. Generate diverse search queries to explore each subtopic thoroughly
208
+ 3. Identify connections and patterns across different areas of research
209
+ 4. Synthesize findings into coherent insights
210
+ 5. Identify gaps in current knowledge and suggest areas for further investigation
211
+
212
+ For each research task:
213
+ - Consider multiple perspectives and approaches
214
+ - Look for both supporting and contradicting evidence
215
+ - Evaluate the credibility and relevance of sources
216
+ - Track emerging trends and recent developments
217
+ - Consider cross-disciplinary implications
218
+
219
+ Output Format:
220
+ - Provide structured research plans
221
+ - Include specific search queries for each subtopic
222
+ - Prioritize queries based on relevance and potential impact
223
+ - Suggest follow-up areas for deeper investigation
224
+ """
225
+
226
+ SUMMARIZATION_AGENT_PROMPT = """
227
+ You are an expert information synthesis and summarization agent designed for producing clear, accurate, and insightful summaries of complex information. Your core capabilities include:
228
+
229
+
230
+ Core Capabilities:
231
+ - Identify and extract key concepts, themes, and insights from any given content
232
+ - Recognize patterns, relationships, and hierarchies within information
233
+ - Filter out noise while preserving crucial context and nuance
234
+ - Handle multiple sources and perspectives simultaneously
235
+
236
+ Summarization Strategy
237
+ 1. Multi-level Structure
238
+ - Provide an extensive summary
239
+ - Follow with key findings
240
+ - Include detailed insights with supporting evidence
241
+ - End with implications or next steps when relevant
242
+
243
+ 2. Quality Standards
244
+ - Maintain factual accuracy and precision
245
+ - Preserve important technical details and terminology
246
+ - Avoid oversimplification of complex concepts
247
+ - Include quantitative data when available
248
+ - Cite or reference specific sources when summarizing claims
249
+
250
+ 3. Clarity & Accessibility
251
+ - Use clear, concise language
252
+ - Define technical terms when necessary
253
+ - Structure information logically
254
+ - Use formatting to enhance readability
255
+ - Maintain appropriate level of technical depth for the audience
256
+
257
+ 4. Synthesis & Analysis
258
+ - Identify conflicting information or viewpoints
259
+ - Highlight consensus across sources
260
+ - Note gaps or limitations in the information
261
+ - Draw connections between related concepts
262
+ - Provide context for better understanding
263
+
264
+ OUTPUT REQUIREMENTS:
265
+ - Begin with a clear statement of the topic or question being addressed
266
+ - Use consistent formatting and structure
267
+ - Clearly separate different levels of detail
268
+ - Include confidence levels for conclusions when appropriate
269
+ - Note any areas requiring additional research or clarification
270
+
271
+ Remember: Your goal is to make complex information accessible while maintaining accuracy and depth. Prioritize clarity without sacrificing important nuance or detail."""
272
+
273
+
274
+ # Initialize the research agent
275
+ research_agent = Agent(
276
+ agent_name="Deep-Research-Agent",
277
+ agent_description="Specialized agent for conducting comprehensive research across multiple domains",
278
+ system_prompt=RESEARCH_AGENT_PROMPT,
279
+ max_loops=1, # Allow multiple iterations for thorough research
280
+ tools_list_dictionary=tools,
281
+ model_name="gpt-4o-mini",
282
+ )
283
+
284
+
285
+ reasoning_duo = ReasoningDuo(
286
+ system_prompt=SUMMARIZATION_AGENT_PROMPT, output_type="string"
287
+ )
288
+
289
+
290
+ class DeepResearchSwarm:
291
+ def __init__(
292
+ self,
293
+ name: str = "DeepResearchSwarm",
294
+ description: str = "A swarm that conducts comprehensive research across multiple domains",
295
+ research_agent: Agent = research_agent,
296
+ max_loops: int = 1,
297
+ nice_print: bool = True,
298
+ output_type: str = "json",
299
+ max_workers: int = os.cpu_count()
300
+ * 2, # Let the system decide optimal thread count
301
+ token_count: bool = False,
302
+ research_model_name: str = "gpt-4o-mini",
303
+ ):
304
+ self.name = name
305
+ self.description = description
306
+ self.research_agent = research_agent
307
+ self.max_loops = max_loops
308
+ self.nice_print = nice_print
309
+ self.output_type = output_type
310
+ self.max_workers = max_workers
311
+ self.research_model_name = research_model_name
312
+
313
+ self.reliability_check()
314
+ self.conversation = Conversation(token_count=token_count)
315
+
316
+ # Create a persistent ThreadPoolExecutor for the lifetime of the swarm
317
+ # This eliminates thread creation overhead on each query
318
+ self.executor = concurrent.futures.ThreadPoolExecutor(
319
+ max_workers=self.max_workers
320
+ )
321
+
322
+ def __del__(self):
323
+ """Clean up the executor on object destruction"""
324
+ self.executor.shutdown(wait=False)
325
+
326
+ def reliability_check(self):
327
+ """Check the reliability of the query"""
328
+ if self.max_loops < 1:
329
+ raise ValueError("max_loops must be greater than 0")
330
+
331
+ formatter.print_panel(
332
+ "DeepResearchSwarm is booting up...", "blue"
333
+ )
334
+ formatter.print_panel("Reliability check passed", "green")
335
+
336
+ def get_queries(self, query: str) -> List[str]:
337
+ """
338
+ Generate a list of detailed search queries based on the input query.
339
+
340
+ Args:
341
+ query (str): The main research query to explore
342
+
343
+ Returns:
344
+ List[str]: A list of detailed search queries
345
+ """
346
+ self.conversation.add(role="User", content=query)
347
+
348
+ # Get the agent's response
349
+ agent_output = self.research_agent.run(query)
350
+
351
+ self.conversation.add(
352
+ role=self.research_agent.agent_name, content=agent_output
353
+ )
354
+
355
+ # Convert the string output to dictionary
356
+ output_dict = str_to_dict(agent_output)
357
+
358
+ # Print the conversation history
359
+ if self.nice_print:
360
+ to_do_list = any_to_str(output_dict)
361
+ formatter.print_panel(to_do_list, "blue")
362
+
363
+ # Extract the detailed queries from the output
364
+ if (
365
+ isinstance(output_dict, dict)
366
+ and "detailed_queries" in output_dict
367
+ ):
368
+ queries = output_dict["detailed_queries"]
369
+ formatter.print_panel(
370
+ f"Generated {len(queries)} queries", "blue"
371
+ )
372
+ return queries
373
+
374
+ return []
375
+
376
+ def _process_query(self, query: str) -> Tuple[str, str]:
377
+ """
378
+ Process a single query with search and reasoning.
379
+ This function is designed to be run in a separate thread.
380
+
381
+ Args:
382
+ query (str): The query to process
383
+
384
+ Returns:
385
+ Tuple[str, str]: A tuple containing (search_results, reasoning_output)
386
+ """
387
+ # Run the search
388
+ results = exa_search(query)
389
+
390
+ # Run the reasoning on the search results
391
+ reasoning_output = reasoning_duo.run(results)
392
+
393
+ return (results, reasoning_output)
394
+
395
+ def step(self, query: str):
396
+ """
397
+ Execute a single research step with maximum parallelism.
398
+
399
+ Args:
400
+ query (str): The research query to process
401
+
402
+ Returns:
403
+ Formatted conversation history
404
+ """
405
+ # Get all the queries to process
406
+ queries = self.get_queries(query)
407
+
408
+ # Submit all queries for concurrent processing
409
+ # Using a list instead of generator for clearer debugging
410
+ futures = []
411
+ for q in queries:
412
+ future = self.executor.submit(self._process_query, q)
413
+ futures.append((q, future))
414
+
415
+ # Process results as they complete (no waiting for slower queries)
416
+ for q, future in futures:
417
+ try:
418
+ # Get results (blocks until this specific future is done)
419
+ results, reasoning_output = future.result()
420
+
421
+ # Add search results to conversation
422
+ self.conversation.add(
423
+ role="User",
424
+ content=f"Search results for {q}: \n {results}",
425
+ )
426
+
427
+ # Add reasoning output to conversation
428
+ self.conversation.add(
429
+ role=reasoning_duo.agent_name,
430
+ content=reasoning_output,
431
+ )
432
+ except Exception as e:
433
+ # Handle any errors in the thread
434
+ self.conversation.add(
435
+ role="System",
436
+ content=f"Error processing query '{q}': {str(e)}",
437
+ )
438
+
439
+ # Once all query processing is complete, generate the final summary
440
+ # This step runs after all queries to ensure it summarizes all results
441
+ final_summary = reasoning_duo.run(
442
+ f"Generate an extensive report of the following content: {self.conversation.get_str()}"
443
+ )
444
+
445
+ self.conversation.add(
446
+ role=reasoning_duo.agent_name,
447
+ content=final_summary,
448
+ )
449
+
450
+ return history_output_formatter(
451
+ self.conversation, type=self.output_type
452
+ )
453
+
454
+ def run(self, task: str):
455
+ return self.step(task)
456
+
457
+ def batched_run(self, tasks: List[str]):
458
+ """
459
+ Execute a list of research tasks in parallel.
460
+
461
+ Args:
462
+ tasks (List[str]): A list of research tasks to execute
463
+
464
+ Returns:
465
+ List[str]: A list of formatted conversation histories
466
+ """
467
+ futures = []
468
+ for task in tasks:
469
+ future = self.executor.submit(self.step, task)
470
+ futures.append((task, future))
471
+
472
+
473
+ # # Example usage
474
+ # if __name__ == "__main__":
475
+ # swarm = DeepResearchSwarm(
476
+ # output_type="json",
477
+ # )
478
+ # print(
479
+ # swarm.step(
480
+ # "What is the active tarrif situation with mexico? Only create 2 queries"
481
+ # )
482
+ # )
@@ -21,6 +21,7 @@ from swarms.structs.swarm_matcher import swarm_matcher
21
21
  from swarms.structs.output_type import OutputType
22
22
  from swarms.utils.loguru_logger import initialize_logger
23
23
  from swarms.structs.malt import MALT
24
+ from swarms.structs.deep_research_swarm import DeepResearchSwarm
24
25
 
25
26
  logger = initialize_logger(log_folder="swarm_router")
26
27
 
@@ -37,6 +38,7 @@ SwarmType = Literal[
37
38
  "auto",
38
39
  "MajorityVoting",
39
40
  "MALT",
41
+ "DeepResearchSwarm",
40
42
  ]
41
43
 
42
44
 
@@ -316,6 +318,15 @@ class SwarmRouter:
316
318
  preset_agents=True,
317
319
  )
318
320
 
321
+ elif self.swarm_type == "DeepResearchSwarm":
322
+ return DeepResearchSwarm(
323
+ name=self.name,
324
+ description=self.description,
325
+ agents=self.agents,
326
+ max_loops=self.max_loops,
327
+ output_type=self.output_type,
328
+ )
329
+
319
330
  elif self.swarm_type == "HiearchicalSwarm":
320
331
  return HierarchicalSwarm(
321
332
  name=self.name,
@@ -142,7 +142,7 @@ class SwarmsAPIClient:
142
142
  def __init__(
143
143
  self,
144
144
  api_key: Optional[str] = None,
145
- base_url: str = "https://swarms-api-285321057562.us-east1.run.app",
145
+ base_url: str = "https://api.swarms.world",
146
146
  timeout: int = 30,
147
147
  max_retries: int = 3,
148
148
  format_type: Literal["pydantic", "json", "dict"] = "pydantic",