swarms 7.9.3__py3-none-any.whl → 7.9.4__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,8 +1,11 @@
1
- from typing import List, Any, Optional, Union, Callable
1
+ from typing import Dict, List, Any, Optional, Union, Callable
2
2
  import random
3
3
  from swarms.prompts.collaborative_prompts import (
4
4
  get_multi_agent_collaboration_prompt_one,
5
5
  )
6
+ from functools import lru_cache
7
+
8
+ from loguru import logger
6
9
 
7
10
 
8
11
  def list_all_agents(
@@ -116,3 +119,65 @@ def set_random_models_for_agents(
116
119
  else:
117
120
  setattr(agents, "model_name", random.choice(model_names))
118
121
  return agents
122
+
123
+
124
+ @lru_cache(maxsize=128)
125
+ def _create_agent_map_cached(
126
+ agent_tuple: tuple,
127
+ ) -> Dict[str, Union[Callable, Any]]:
128
+ """Internal cached version of create_agent_map that takes a tuple for hashability."""
129
+ try:
130
+ return {
131
+ (
132
+ agent.agent_name
133
+ if isinstance(agent, Callable)
134
+ else agent.__name__
135
+ ): agent
136
+ for agent in agent_tuple
137
+ }
138
+ except (AttributeError, TypeError) as e:
139
+ logger.error(f"Error creating agent map: {e}")
140
+ return {}
141
+
142
+
143
+ def create_agent_map(
144
+ agents: List[Union[Callable, Any]],
145
+ ) -> Dict[str, Union[Callable, Any]]:
146
+ """Creates a map of agent names to agents for fast lookup.
147
+
148
+ This function is optimized with LRU caching to avoid recreating maps for identical agent lists.
149
+ The cache stores up to 128 different agent map configurations.
150
+
151
+ Args:
152
+ agents (List[Union[Callable, Any]]): List of agents to create a map of. Each agent should either be:
153
+ - A callable with a __name__ attribute
154
+ - An object with an agent_name attribute
155
+
156
+ Returns:
157
+ Dict[str, Union[Callable, Any]]: Map of agent names to agents
158
+
159
+ Examples:
160
+ >>> def agent1(): pass
161
+ >>> def agent2(): pass
162
+ >>> agents = [agent1, agent2]
163
+ >>> agent_map = create_agent_map(agents)
164
+ >>> print(agent_map.keys())
165
+ dict_keys(['agent1', 'agent2'])
166
+
167
+ >>> class Agent:
168
+ ... def __init__(self, name):
169
+ ... self.agent_name = name
170
+ >>> agents = [Agent("bot1"), Agent("bot2")]
171
+ >>> agent_map = create_agent_map(agents)
172
+ >>> print(agent_map.keys())
173
+ dict_keys(['bot1', 'bot2'])
174
+
175
+ Raises:
176
+ ValueError: If agents list is empty
177
+ TypeError: If any agent lacks required name attributes
178
+ """
179
+ if not agents:
180
+ raise ValueError("Agents list cannot be empty")
181
+
182
+ # Convert list to tuple for hashability
183
+ return _create_agent_map_cached(tuple(agents))
swarms/utils/formatter.py CHANGED
@@ -5,9 +5,27 @@ from typing import Any, Callable, Dict, List, Optional
5
5
  from rich.console import Console
6
6
  from rich.live import Live
7
7
  from rich.panel import Panel
8
- from rich.progress import Progress, SpinnerColumn, TextColumn
8
+ from rich.progress import (
9
+ Progress,
10
+ SpinnerColumn,
11
+ TextColumn,
12
+ )
9
13
  from rich.table import Table
10
14
  from rich.text import Text
15
+ from rich.spinner import Spinner
16
+
17
+ # Global lock to ensure only a single Rich Live context is active at any moment.
18
+ # Rich's Live render is **not** thread-safe; concurrent Live contexts on the same
19
+ # console raise runtime errors. Using a module-level lock serialises access and
20
+ # prevents crashes when multiple agents stream simultaneously in different
21
+ # threads (e.g., in ConcurrentWorkflow).
22
+ live_render_lock = threading.Lock()
23
+
24
+ # Global Live display for the dashboard
25
+ dashboard_live = None
26
+
27
+ # Create a spinner for loading animation
28
+ spinner = Spinner("dots", style="yellow")
11
29
 
12
30
 
13
31
  def choose_random_color():
@@ -37,6 +55,53 @@ class Formatter:
37
55
  Initializes the Formatter with a Rich Console instance.
38
56
  """
39
57
  self.console = Console()
58
+ self._dashboard_live = None
59
+ self._spinner_frames = [
60
+ "⠋",
61
+ "⠙",
62
+ "⠹",
63
+ "⠸",
64
+ "⠼",
65
+ "⠴",
66
+ "⠦",
67
+ "⠧",
68
+ "⠇",
69
+ "⠏",
70
+ ]
71
+ self._spinner_idx = 0
72
+
73
+ def _get_status_with_loading(self, status: str) -> Text:
74
+ """
75
+ Creates a status text with loading animation for running status.
76
+ """
77
+ if status.lower() == "running":
78
+ # Create loading bar effect
79
+ self._spinner_idx = (self._spinner_idx + 1) % len(
80
+ self._spinner_frames
81
+ )
82
+ spinner_char = self._spinner_frames[self._spinner_idx]
83
+ progress_bar = "█" * (self._spinner_idx % 5) + "░" * (
84
+ 4 - (self._spinner_idx % 5)
85
+ )
86
+ return Text(
87
+ f"{spinner_char} {status} {progress_bar}",
88
+ style="bold yellow",
89
+ )
90
+
91
+ # Style other statuses
92
+ status_style = {
93
+ "completed": "bold green",
94
+ "pending": "bold red",
95
+ "error": "bold red",
96
+ }.get(status.lower(), "white")
97
+
98
+ status_symbol = {
99
+ "completed": "✓",
100
+ "pending": "○",
101
+ "error": "✗",
102
+ }.get(status.lower(), "•")
103
+
104
+ return Text(f"{status_symbol} {status}", style=status_style)
40
105
 
41
106
  def _print_panel(
42
107
  self, content: str, title: str = "", style: str = "bold blue"
@@ -209,58 +274,155 @@ class Formatter:
209
274
  complete_response = ""
210
275
  chunks_collected = []
211
276
 
212
- # TRUE streaming with Rich's automatic text wrapping
213
- with Live(
214
- create_streaming_panel(streaming_text),
215
- console=self.console,
216
- refresh_per_second=20,
217
- ) as live:
218
- try:
219
- for part in streaming_response:
220
- if (
221
- hasattr(part, "choices")
222
- and part.choices
223
- and part.choices[0].delta.content
224
- ):
225
- # Add ONLY the new chunk to the Text object with random color style
226
- chunk = part.choices[0].delta.content
227
- streaming_text.append(chunk, style=text_style)
228
- complete_response += chunk
229
-
230
- # Collect chunks if requested
231
- if collect_chunks:
232
- chunks_collected.append(chunk)
233
-
234
- # Call chunk callback if provided
235
- if on_chunk_callback:
236
- on_chunk_callback(chunk)
237
-
238
- # Update display with new text - Rich handles all wrapping automatically
239
- live.update(
240
- create_streaming_panel(
241
- streaming_text, is_complete=False
277
+ # Acquire the lock so that only one Live panel is active at a time.
278
+ # Other threads will wait here until the current streaming completes,
279
+ # avoiding Rich.Live concurrency errors.
280
+ with live_render_lock:
281
+ # TRUE streaming with Rich's automatic text wrapping
282
+ with Live(
283
+ create_streaming_panel(streaming_text),
284
+ console=self.console,
285
+ refresh_per_second=20,
286
+ ) as live:
287
+ try:
288
+ for part in streaming_response:
289
+ if (
290
+ hasattr(part, "choices")
291
+ and part.choices
292
+ and part.choices[0].delta.content
293
+ ):
294
+ # Add ONLY the new chunk to the Text object with random color style
295
+ chunk = part.choices[0].delta.content
296
+ streaming_text.append(
297
+ chunk, style=text_style
298
+ )
299
+ complete_response += chunk
300
+
301
+ # Collect chunks if requested
302
+ if collect_chunks:
303
+ chunks_collected.append(chunk)
304
+
305
+ # Call chunk callback if provided
306
+ if on_chunk_callback:
307
+ on_chunk_callback(chunk)
308
+
309
+ # Update display with new text - Rich handles all wrapping automatically
310
+ live.update(
311
+ create_streaming_panel(
312
+ streaming_text, is_complete=False
313
+ )
242
314
  )
315
+
316
+ # Final update to show completion
317
+ live.update(
318
+ create_streaming_panel(
319
+ streaming_text, is_complete=True
243
320
  )
321
+ )
244
322
 
245
- # Final update to show completion
246
- live.update(
247
- create_streaming_panel(
248
- streaming_text, is_complete=True
323
+ except Exception as e:
324
+ # Handle any streaming errors gracefully
325
+ streaming_text.append(
326
+ f"\n[Error: {str(e)}]", style="bold red"
249
327
  )
250
- )
328
+ live.update(
329
+ create_streaming_panel(
330
+ streaming_text, is_complete=True
331
+ )
332
+ )
333
+
334
+ return complete_response
335
+
336
+ def _create_dashboard_table(
337
+ self, agents_data: List[Dict[str, Any]], title: str
338
+ ) -> Panel:
339
+ """
340
+ Creates the dashboard table with the current agent statuses.
341
+ """
342
+ # Create main table
343
+ table = Table(
344
+ show_header=True,
345
+ header_style="bold magenta",
346
+ expand=True,
347
+ title=title,
348
+ title_style="bold cyan",
349
+ border_style="bright_blue",
350
+ show_lines=True, # Add lines between rows
351
+ )
352
+
353
+ # Add columns with adjusted widths
354
+ table.add_column(
355
+ "Agent Name", style="cyan", width=30, no_wrap=True
356
+ )
357
+ table.add_column(
358
+ "Status", style="green", width=20, no_wrap=True
359
+ ) # Increased width for loading animation
360
+ table.add_column(
361
+ "Output", style="white", width=100, overflow="fold"
362
+ ) # Allow text to wrap
363
+
364
+ # Add rows for each agent
365
+ for agent in agents_data:
366
+ name = Text(agent["name"], style="bold cyan")
367
+ status = self._get_status_with_loading(agent["status"])
368
+ output = Text(str(agent["output"]))
369
+ table.add_row(name, status, output)
370
+
371
+ # Create a panel to wrap the table
372
+ dashboard_panel = Panel(
373
+ table,
374
+ border_style="bright_blue",
375
+ padding=(1, 2),
376
+ title=f"[bold cyan]{title}[/bold cyan] - Total Agents: [bold green]{len(agents_data)}[/bold green]",
377
+ expand=True, # Make panel expand to full width
378
+ )
379
+
380
+ return dashboard_panel
251
381
 
252
- except Exception as e:
253
- # Handle any streaming errors gracefully
254
- streaming_text.append(
255
- f"\n[Error: {str(e)}]", style="bold red"
382
+ def print_agent_dashboard(
383
+ self,
384
+ agents_data: List[Dict[str, Any]],
385
+ title: str = "🤖 Agent Dashboard",
386
+ is_final: bool = False,
387
+ ) -> None:
388
+ """
389
+ Displays a beautiful dashboard showing agent information in a panel-like spreadsheet format.
390
+ Updates in place instead of printing multiple times.
391
+
392
+ Args:
393
+ agents_data (List[Dict[str, Any]]): List of dictionaries containing agent information.
394
+ Each dict should have: name, status, output
395
+ title (str): The title of the dashboard.
396
+ is_final (bool): Whether this is the final update of the dashboard.
397
+ """
398
+ with live_render_lock:
399
+ if self._dashboard_live is None:
400
+ # Create new Live display if none exists
401
+ self._dashboard_live = Live(
402
+ self._create_dashboard_table(agents_data, title),
403
+ console=self.console,
404
+ refresh_per_second=10, # Increased refresh rate
405
+ transient=False, # Make display persistent
256
406
  )
257
- live.update(
258
- create_streaming_panel(
259
- streaming_text, is_complete=True
260
- )
407
+ self._dashboard_live.start()
408
+ else:
409
+ # Update existing Live display
410
+ self._dashboard_live.update(
411
+ self._create_dashboard_table(agents_data, title)
261
412
  )
262
413
 
263
- return complete_response
414
+ # If this is the final update, add a newline to separate from future output
415
+ if is_final:
416
+ self.console.print() # Add blank line after final display
417
+
418
+ def stop_dashboard(self):
419
+ """
420
+ Stops and cleans up the dashboard display.
421
+ """
422
+ if self._dashboard_live is not None:
423
+ self._dashboard_live.stop()
424
+ self.console.print() # Add blank line after stopping
425
+ self._dashboard_live = None
264
426
 
265
427
 
266
428
  formatter = Formatter()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: swarms
3
- Version: 7.9.3
3
+ Version: 7.9.4
4
4
  Summary: Swarms - TGSC
5
5
  License: MIT
6
6
  Keywords: artificial intelligence,deep learning,optimizers,Prompt Engineering,swarms,agents,llms,transformers,multi-agent,swarms of agents,Enterprise-Grade Agents,Production-Grade Agents,Agents,Multi-Grade-Agents,Swarms,Transformers,LLMs,Prompt Engineering,Agents,Generative Agents,Generative AI,Agent Marketplace,Agent Store,quant,finance,algorithmic trading,portfolio optimization,risk management,financial modeling,machine learning for finance,natural language processing for finance
@@ -12,7 +12,7 @@ swarms/agents/gkp_agent.py,sha256=5Jms3zHQ2qwJ6-PHDh9X-cFtAlH4dSUoDgRqN-xZzog,21
12
12
  swarms/agents/i_agent.py,sha256=_kqGt3a4SGB21_GP-KcV8A5p_9wneShs6eJUJYFdluw,12274
13
13
  swarms/agents/openai_assistant.py,sha256=mTSEtj26J0mc5pCeWrmMY0EXzTRYQfyfw_BtOqtcCHc,11044
14
14
  swarms/agents/react_agent.py,sha256=yM8lQoRsqJZicqtmgBrC7RHv0aKEb5oZHh4q5aAA_xs,5804
15
- swarms/agents/reasoning_agents.py,sha256=Z_YETF-hzG7qL-h6WB1qNR6QU4g784M9NzO5C0_kWtA,9639
15
+ swarms/agents/reasoning_agents.py,sha256=0pdvNtRBtBJU-fGYeGfplxQkyxZGTqkZisqpAq1zGkU,8631
16
16
  swarms/agents/reasoning_duo.py,sha256=4qw9RtwmWkWnDWjEtJqYFVLh--EV9YQd24-fQa0lMfM,3873
17
17
  swarms/agents/self_agent_builder.py,sha256=bX7xSwak6HiyK901VdeE8OlT4yqE0n7jyHcWJrkMeew,1104
18
18
  swarms/agents/tool_agent.py,sha256=G7rhBACsHsGUMT4H9eF5aY7e3Gx-5jOmJkhCF1jm9mU,5087
@@ -106,7 +106,7 @@ swarms/schemas/mcp_schemas.py,sha256=XZJ4HyiY_cv8Gvj-53ddjzXuqT9hBU2f0cHbhIKs_jY
106
106
  swarms/schemas/swarms_api_schemas.py,sha256=uKqleW_7hNpqHi06yoba9jS2i9yzZp-SBV944MnkN68,6233
107
107
  swarms/schemas/tool_schema_base_model.py,sha256=0biTGIoibsPPP3fOrkC6WvNU5vXaalyccVKC1fpO_eg,1409
108
108
  swarms/structs/__init__.py,sha256=8_CG2mct9cS-TQlpKlC5-240kSjmoooy9bfYXRuB9oQ,4552
109
- swarms/structs/agent.py,sha256=3SL7DNr6W2WuHsHlkSb7xoM0cjqim0JYD0pVgFspksM,119647
109
+ swarms/structs/agent.py,sha256=hkFhZYZVjNrcHQv4nm5qkaRNHmjyf2LNsp5UZy1CXJk,127157
110
110
  swarms/structs/agent_builder.py,sha256=tYNpfO4_8cgfMHfgA5DAOWffHnt70p6CLt59esqfVCY,12133
111
111
  swarms/structs/agent_rag_handler.py,sha256=g17YRrNmf16TLvyFCCcsitVk3d-QNZmck_XYmjSN_YM,21372
112
112
  swarms/structs/agent_registry.py,sha256=il507cO1NF-d4ChyANVLuWrN8bXsEAi8_7bLJ_sTU6A,12112
@@ -119,7 +119,7 @@ swarms/structs/base_swarm.py,sha256=bO5olk16je7aJ_XmJxiD6y4nVlbvOySZpc5JX6Rdpvg,
119
119
  swarms/structs/base_workflow.py,sha256=DTfFwX3AdFYxACDYwUDqhsbcDZnITlg5TeEYyxmJBCc,11414
120
120
  swarms/structs/batch_agent_execution.py,sha256=d85DzeCq4uTbbPqLhAXFqFx_cxXUS5yRnJ1-gJkwU5w,1871
121
121
  swarms/structs/concat.py,sha256=utezSxNyh1mIwXgdf8-dJ803NDPyEy79WE8zJHuooGk,732
122
- swarms/structs/concurrent_workflow.py,sha256=SCyK-weYYpRb5EG3wZW4z7cF9isoIdpN7CvIVQaB7jg,8343
122
+ swarms/structs/concurrent_workflow.py,sha256=0Yo_eMKxdbLfqkVmEJpyL0_cjMUH5NrBu-r-EWltMwU,14477
123
123
  swarms/structs/conversation.py,sha256=mmsvR15zWxsJ4dgyxwwunIifvfcVO4gfJr5zl0RxaSA,52258
124
124
  swarms/structs/council_judge.py,sha256=siYDKiHMvFmShUTXxdo4R6vXiQhKt7bEBI205oC3kU4,19639
125
125
  swarms/structs/csv_to_agent.py,sha256=Zv41sjeWA50msq-paGHESzlxZyMU78DYDLNNKZtNfoI,11125
@@ -131,10 +131,10 @@ swarms/structs/groupchat.py,sha256=jjH0BqU9Nrd_3jl9QzrcvbSce527SFpUaepawaRiw2o,1
131
131
  swarms/structs/hiearchical_swarm.py,sha256=6JwYWlsmwsVtOr2xlzl2q0IRsGvr-RFzDG9ClIJWTQo,33400
132
132
  swarms/structs/hybrid_hiearchical_peer_swarm.py,sha256=0BrmzSVit-I_04DFfrs7onLblLA6PSPa0JE3-4j05FA,9316
133
133
  swarms/structs/image_batch_processor.py,sha256=31Z8vTVL4dw18QxGwb0Jg1nvp0YzX8lwVgGj_-KrkhY,8207
134
- swarms/structs/interactive_groupchat.py,sha256=tlI4BiDRKQK2YtHvbWvT-Z0rhfpfsywavj1CHfZc7F8,41169
134
+ swarms/structs/interactive_groupchat.py,sha256=zgumadD7YCFAqzNrh9CKMSWKe66aBkohIxPgQs3IU2k,40370
135
135
  swarms/structs/long_agent.py,sha256=KFjE2uUI8ONTkeJO43Sh3y5-Ec0kga28BECGklic-S4,15049
136
136
  swarms/structs/ma_blocks.py,sha256=04dF3DOSHXxNwrcl9QUloKgNDkI9yLRv6UbyyiKsIb0,4551
137
- swarms/structs/ma_utils.py,sha256=XJYxqWxFKBmdDekdX_BVGmmBxLbnz7hURMS2ZpVIcK8,3854
137
+ swarms/structs/ma_utils.py,sha256=-7wTLVq9PHbXxt3QP9ngFA4mHV8H4dIOmhrXml4Iwcc,5933
138
138
  swarms/structs/majority_voting.py,sha256=7RGMTNX-ulH5McV0LPkpGEOkwYgsBdzTzHk5G_g_YUY,10120
139
139
  swarms/structs/malt.py,sha256=uLofKBWHkP3uNhyCkkgEyE4Z7qnOHTtcg-OTiR19x_Y,19572
140
140
  swarms/structs/matrix_swarm.py,sha256=qHuhOYrTyOv6ujHMe8PrQT-h-WmaCPCfX4ghv5L8UFI,9765
@@ -192,7 +192,7 @@ swarms/utils/check_all_model_max_tokens.py,sha256=ZHIKlrU-L-OM2IJAbYkCoVyBKe2d0J
192
192
  swarms/utils/data_to_text.py,sha256=1PUoWokylp7MOrGNk1cmO3cJlfskdAIiImGk9ECwsKU,3427
193
193
  swarms/utils/disable_logging.py,sha256=KKPKQVfQqLPFgj03uveOoyeHOTlfEJt-yfLc3SA53Rk,2470
194
194
  swarms/utils/file_processing.py,sha256=QjQCIPTcwicQlfy656BXBYpIzMR0s2343E7ftnok5Uo,4865
195
- swarms/utils/formatter.py,sha256=PDtaj93jwySIaGy4kO7nEoHSAEaNs5CRiM6sKOBZEME,8813
195
+ swarms/utils/formatter.py,sha256=IpYrGlGfcBX1jGrsxqIDhO4szrMbk0jQqtKrTGZbMQY,14724
196
196
  swarms/utils/function_caller_model.py,sha256=ZfgCMzOizNnuZipYLclTziECNHszH9p8RQcUq7VNr4Q,4156
197
197
  swarms/utils/generate_keys.py,sha256=o5zp_8rwu5sgQnItWS1xAuIIRIkahwm02qy1vsV6DSQ,997
198
198
  swarms/utils/history_output_formatter.py,sha256=whjfk4N5SjMo3mtrafNny_alNiAhQcGza3l1dCyg7Nw,1388
@@ -208,8 +208,8 @@ swarms/utils/str_to_dict.py,sha256=T3Jsdjz87WIlkSo7jAW6BB80sv0Ns49WT1qXlOrdEoE,8
208
208
  swarms/utils/try_except_wrapper.py,sha256=uvDZDZJcH986EF0Ej6zZBLcqHJ58NHizPsAH5olrE7Q,3919
209
209
  swarms/utils/vllm_wrapper.py,sha256=sNkm4EbeMrqqmHidnvq5zTnofQAaARy3HIrNBu11lKs,5072
210
210
  swarms/utils/xml_utils.py,sha256=D4nEdo1nkHqSoTKrWylXBXjcHFhGaOYvvfGNQQoYV5o,2514
211
- swarms-7.9.3.dist-info/LICENSE,sha256=jwRtEmTWjLrEsvFB6QFdYs2cEeZPRMdj-UMOFkPF8_0,11363
212
- swarms-7.9.3.dist-info/METADATA,sha256=mWniYYlqAwYjIBrUriqn4yGxMezGA7h8zPUm-8VnntY,32138
213
- swarms-7.9.3.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
214
- swarms-7.9.3.dist-info/entry_points.txt,sha256=2K0rTtfO1X1WaO-waJlXIKw5Voa_EpAL_yU0HXE2Jgc,47
215
- swarms-7.9.3.dist-info/RECORD,,
211
+ swarms-7.9.4.dist-info/LICENSE,sha256=jwRtEmTWjLrEsvFB6QFdYs2cEeZPRMdj-UMOFkPF8_0,11363
212
+ swarms-7.9.4.dist-info/METADATA,sha256=8jzaBd4MWGhsDSfrmyblph_Qa5-Pz5_F3EG1QCmnNAc,32138
213
+ swarms-7.9.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
214
+ swarms-7.9.4.dist-info/entry_points.txt,sha256=2K0rTtfO1X1WaO-waJlXIKw5Voa_EpAL_yU0HXE2Jgc,47
215
+ swarms-7.9.4.dist-info/RECORD,,
File without changes