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,13 +1,10 @@
1
+ import concurrent.futures
1
2
  import os
2
- import time
3
- from concurrent.futures import ThreadPoolExecutor
4
- from functools import lru_cache
5
- from typing import Any, Callable, Dict, List, Optional, Union
3
+ from typing import Callable, List, Optional, Union
6
4
 
7
5
  from swarms.structs.agent import Agent
8
6
  from swarms.structs.base_swarm import BaseSwarm
9
7
  from swarms.structs.conversation import Conversation
10
- from swarms.utils.formatter import formatter
11
8
  from swarms.utils.history_output_formatter import (
12
9
  history_output_formatter,
13
10
  )
@@ -35,9 +32,7 @@ class ConcurrentWorkflow(BaseSwarm):
35
32
  return_str_on (bool): Flag indicating whether to return the output as a string. Defaults to False.
36
33
  auto_generate_prompts (bool): Flag indicating whether to auto-generate prompts for agents. Defaults to False.
37
34
  return_entire_history (bool): Flag indicating whether to return the entire conversation history. Defaults to False.
38
- cache_size (int): The size of the cache. Defaults to 100.
39
- max_retries (int): The maximum number of retry attempts. Defaults to 3.
40
- retry_delay (float): The delay between retry attempts in seconds. Defaults to 1.0.
35
+
41
36
 
42
37
  Raises:
43
38
  ValueError: If the list of agents is empty or if the description is empty.
@@ -50,13 +45,7 @@ class ConcurrentWorkflow(BaseSwarm):
50
45
  auto_save (bool): Flag indicating whether to automatically save the metadata.
51
46
  output_type (str): The type of output format.
52
47
  max_loops (int): The maximum number of loops for each agent.
53
- return_str_on (bool): Flag indicating whether to return the output as a string.
54
48
  auto_generate_prompts (bool): Flag indicating whether to auto-generate prompts for agents.
55
- return_entire_history (bool): Flag indicating whether to return the entire conversation history.
56
- cache_size (int): The size of the cache.
57
- max_retries (int): The maximum number of retry attempts.
58
- retry_delay (float): The delay between retry attempts in seconds.
59
- _cache (dict): The cache for storing agent outputs.
60
49
  """
61
50
 
62
51
  def __init__(
@@ -68,12 +57,7 @@ class ConcurrentWorkflow(BaseSwarm):
68
57
  auto_save: bool = True,
69
58
  output_type: str = "dict-all-except-first",
70
59
  max_loops: int = 1,
71
- return_str_on: bool = False,
72
60
  auto_generate_prompts: bool = False,
73
- return_entire_history: bool = False,
74
- cache_size: int = 100,
75
- max_retries: int = 3,
76
- retry_delay: float = 1.0,
77
61
  *args,
78
62
  **kwargs,
79
63
  ):
@@ -90,63 +74,31 @@ class ConcurrentWorkflow(BaseSwarm):
90
74
  self.metadata_output_path = metadata_output_path
91
75
  self.auto_save = auto_save
92
76
  self.max_loops = max_loops
93
- self.return_str_on = return_str_on
94
77
  self.auto_generate_prompts = auto_generate_prompts
95
- self.max_workers = os.cpu_count()
96
78
  self.output_type = output_type
97
- self.return_entire_history = return_entire_history
98
- self.tasks = [] # Initialize tasks list
99
- self.cache_size = cache_size
100
- self.max_retries = max_retries
101
- self.retry_delay = retry_delay
102
- self._cache = {}
103
79
 
104
80
  self.reliability_check()
105
81
  self.conversation = Conversation()
106
82
 
107
83
  def reliability_check(self):
108
84
  try:
109
- formatter.print_panel(
110
- content=f"\n 🏷️ Name: {self.name}\n 📝 Description: {self.description}\n 🤖 Agents: {len(self.agents)}\n 🔄 Max Loops: {self.max_loops}\n ",
111
- title="⚙️ Concurrent Workflow Settings",
112
- style="bold blue",
113
- )
114
- formatter.print_panel(
115
- content="🔍 Starting reliability checks",
116
- title="🔒 Reliability Checks",
117
- style="bold blue",
118
- )
119
-
120
- if self.name is None:
121
- logger.error("❌ A name is required for the swarm")
85
+ if self.agents is None:
122
86
  raise ValueError(
123
- " A name is required for the swarm"
87
+ "ConcurrentWorkflow: No agents provided"
124
88
  )
125
89
 
126
- if not self.agents or len(self.agents) <= 1:
127
- logger.error(
128
- "❌ The list of agents must not be empty."
129
- )
90
+ if len(self.agents) == 0:
130
91
  raise ValueError(
131
- " The list of agents must not be empty."
92
+ "ConcurrentWorkflow: No agents provided"
132
93
  )
133
94
 
134
- if not self.description:
135
- logger.error("❌ A description is required.")
136
- raise ValueError(" A description is required.")
137
-
138
- formatter.print_panel(
139
- content="✅ Reliability checks completed successfully",
140
- title="🎉 Reliability Checks",
141
- style="bold green",
142
- )
143
-
144
- except ValueError as e:
145
- logger.error(f"❌ Reliability check failed: {e}")
146
- raise
95
+ if len(self.agents) == 1:
96
+ logger.warning(
97
+ "ConcurrentWorkflow: Only one agent provided. With ConcurrentWorkflow, you should use at least 2+ agents."
98
+ )
147
99
  except Exception as e:
148
100
  logger.error(
149
- f"💥 An unexpected error occurred during reliability checks: {e}"
101
+ f"ConcurrentWorkflow: Reliability check failed: {e}"
150
102
  )
151
103
  raise
152
104
 
@@ -163,162 +115,84 @@ class ConcurrentWorkflow(BaseSwarm):
163
115
  for agent in self.agents:
164
116
  agent.auto_generate_prompt = True
165
117
 
166
- @lru_cache(maxsize=100)
167
- def _cached_run(self, task: str, agent_id: int) -> Any:
168
- """Cached version of agent execution to avoid redundant computations"""
169
- return self.agents[agent_id].run(task=task)
170
-
171
- def _validate_input(self, task: str) -> bool:
172
- """Validate input task"""
173
- if not isinstance(task, str):
174
- raise ValueError("Task must be a string")
175
- if not task.strip():
176
- raise ValueError("Task cannot be empty")
177
- return True
178
-
179
- def _run_with_retry(
180
- self, agent: Agent, task: str, img: str = None
181
- ) -> Any:
182
- """Run agent with retry mechanism"""
183
- for attempt in range(self.max_retries):
184
- try:
185
- output = agent.run(task=task, img=img)
186
- self.conversation.add(agent.agent_name, output)
187
- return output
188
- except Exception as e:
189
- if attempt == self.max_retries - 1:
190
- logger.error(
191
- f"Error running agent {agent.agent_name} after {self.max_retries} attempts: {e}"
192
- )
193
- raise
194
- logger.warning(
195
- f"Attempt {attempt + 1} failed for agent {agent.agent_name}: {e}"
196
- )
197
- time.sleep(
198
- self.retry_delay * (attempt + 1)
199
- ) # Exponential backoff
200
-
201
- def _process_agent(
202
- self, agent: Agent, task: str, img: str = None
203
- ) -> Any:
118
+ def run(
119
+ self,
120
+ task: str,
121
+ img: Optional[str] = None,
122
+ imgs: Optional[List[str]] = None,
123
+ ):
204
124
  """
205
- Process a single agent with caching and error handling.
125
+ Executes all agents in the workflow concurrently on the given task.
206
126
 
207
127
  Args:
208
- agent: The agent to process
209
- task: Task to execute
210
- img: Optional image input
128
+ task (str): The task to be executed by all agents.
129
+ img (Optional[str]): Optional image path for agents that support image input.
130
+ imgs (Optional[List[str]]): Optional list of image paths for agents that support multiple image inputs.
211
131
 
212
132
  Returns:
213
- The agent's output
214
- """
215
- try:
216
- # Fast path - check cache first
217
- cache_key = f"{task}_{agent.agent_name}"
218
- if cache_key in self._cache:
219
- output = self._cache[cache_key]
220
- else:
221
- # Slow path - run agent and update cache
222
- output = self._run_with_retry(agent, task, img)
223
-
224
- if len(self._cache) >= self.cache_size:
225
- self._cache.pop(next(iter(self._cache)))
133
+ The formatted output based on the configured output_type.
226
134
 
227
- self._cache[cache_key] = output
228
-
229
- return output
230
- except Exception as e:
231
- logger.error(
232
- f"Error running agent {agent.agent_name}: {e}"
233
- )
234
- raise
235
-
236
- def _run(
237
- self, task: str, img: str = None, *args, **kwargs
238
- ) -> Union[Dict[str, Any], str]:
239
- """
240
- Enhanced run method with parallel execution.
135
+ Example:
136
+ >>> workflow = ConcurrentWorkflow(agents=[agent1, agent2])
137
+ >>> result = workflow.run("Analyze this financial data")
138
+ >>> print(result)
241
139
  """
242
- # Fast validation
243
- self._validate_input(task)
244
- self.conversation.add("User", task)
245
-
246
- try:
247
- # Parallel execution with optimized thread pool
248
- with ThreadPoolExecutor(
249
- max_workers=self.max_workers
250
- ) as executor:
251
- futures = [
252
- executor.submit(
253
- self._process_agent, agent, task, img
254
- )
255
- for agent in self.agents
256
- ]
257
- # Wait for all futures to complete
258
- for future in futures:
259
- future.result()
260
-
261
- except Exception as e:
262
- logger.error(f"An error occurred during execution: {e}")
263
- raise e
140
+ self.conversation.add(role="User", content=task)
141
+
142
+ # Use 95% of available CPU cores for optimal performance
143
+ max_workers = int(os.cpu_count() * 0.95)
144
+
145
+ # Run agents concurrently using ThreadPoolExecutor
146
+ with concurrent.futures.ThreadPoolExecutor(
147
+ max_workers=max_workers
148
+ ) as executor:
149
+ # Submit all agent tasks and store with their index
150
+ future_to_agent = {
151
+ executor.submit(
152
+ agent.run, task=task, img=img, imgs=imgs
153
+ ): agent
154
+ for agent in self.agents
155
+ }
156
+
157
+ # Collect results and add to conversation in completion order
158
+ for future in concurrent.futures.as_completed(
159
+ future_to_agent
160
+ ):
161
+ agent = future_to_agent[future]
162
+ output = future.result()
163
+ self.conversation.add(role=agent.name, content=output)
264
164
 
265
165
  return history_output_formatter(
266
- self.conversation,
267
- type=self.output_type,
166
+ conversation=self.conversation,
167
+ output_type=self.output_type,
268
168
  )
269
169
 
270
- def run(
170
+ def batch_run(
271
171
  self,
272
- task: Optional[str] = None,
172
+ tasks: List[str],
273
173
  img: Optional[str] = None,
274
- *args,
275
- **kwargs,
276
- ) -> Any:
174
+ imgs: Optional[List[str]] = None,
175
+ ):
277
176
  """
278
- Executes the agent's run method with parallel execution.
177
+ Executes the workflow on multiple tasks sequentially.
279
178
 
280
179
  Args:
281
- task (Optional[str], optional): The task to be executed. Defaults to None.
282
- img (Optional[str], optional): The image to be processed. Defaults to None.
283
- *args: Additional positional arguments to be passed to the execution method.
284
- **kwargs: Additional keyword arguments to be passed to the execution method.
180
+ tasks (List[str]): List of tasks to be executed by all agents.
181
+ img (Optional[str]): Optional image path for agents that support image input.
182
+ imgs (Optional[List[str]]): Optional list of image paths for agents that support multiple image inputs.
285
183
 
286
184
  Returns:
287
- Any: The result of the execution.
288
-
289
- Raises:
290
- ValueError: If task validation fails.
291
- Exception: If any other error occurs during execution.
292
- """
293
- if task is not None:
294
- self.tasks.append(task)
295
-
296
- try:
297
- outputs = self._run(task, img, *args, **kwargs)
298
- return outputs
299
- except Exception as e:
300
- logger.error(f"An error occurred during execution: {e}")
301
- raise e
185
+ List of results, one for each task.
302
186
 
303
- def run_batched(self, tasks: List[str]) -> Any:
304
- """
305
- Enhanced batched execution
187
+ Example:
188
+ >>> workflow = ConcurrentWorkflow(agents=[agent1, agent2])
189
+ >>> tasks = ["Task 1", "Task 2", "Task 3"]
190
+ >>> results = workflow.batch_run(tasks)
191
+ >>> print(len(results)) # 3
306
192
  """
307
- if not tasks:
308
- raise ValueError("Tasks list cannot be empty")
309
-
310
- return [self.run(task) for task in tasks]
311
-
312
- def clear_cache(self):
313
- """Clear the task cache"""
314
- self._cache.clear()
315
-
316
- def get_cache_stats(self) -> Dict[str, int]:
317
- """Get cache statistics"""
318
- return {
319
- "cache_size": len(self._cache),
320
- "max_cache_size": self.cache_size,
321
- }
193
+ return [
194
+ self.run(task=task, img=img, imgs=imgs) for task in tasks
195
+ ]
322
196
 
323
197
 
324
198
  # if __name__ == "__main__":