swarms 7.9.3__py3-none-any.whl → 7.9.5__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"
@@ -62,12 +127,27 @@ class Formatter:
62
127
  title: str = "",
63
128
  style: str = "bold blue",
64
129
  ) -> None:
65
- process_thread = threading.Thread(
66
- target=self._print_panel,
67
- args=(content, title, style),
68
- daemon=True,
69
- )
70
- process_thread.start()
130
+ """Print content in a panel with a title and style.
131
+
132
+ Args:
133
+ content (str): The content to display in the panel
134
+ title (str): The title of the panel
135
+ style (str): The style to apply to the panel
136
+ """
137
+ # Handle None content
138
+ if content is None:
139
+ content = "No content to display"
140
+
141
+ # Convert non-string content to string
142
+ if not isinstance(content, str):
143
+ content = str(content)
144
+
145
+ try:
146
+ self._print_panel(content, title, style)
147
+ except Exception as e:
148
+ # Fallback to basic printing if panel fails
149
+ print(f"\n{title}:")
150
+ print(content)
71
151
 
72
152
  def print_table(
73
153
  self, title: str, data: Dict[str, List[str]]
@@ -209,58 +289,155 @@ class Formatter:
209
289
  complete_response = ""
210
290
  chunks_collected = []
211
291
 
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
292
+ # Acquire the lock so that only one Live panel is active at a time.
293
+ # Other threads will wait here until the current streaming completes,
294
+ # avoiding Rich.Live concurrency errors.
295
+ with live_render_lock:
296
+ # TRUE streaming with Rich's automatic text wrapping
297
+ with Live(
298
+ create_streaming_panel(streaming_text),
299
+ console=self.console,
300
+ refresh_per_second=20,
301
+ ) as live:
302
+ try:
303
+ for part in streaming_response:
304
+ if (
305
+ hasattr(part, "choices")
306
+ and part.choices
307
+ and part.choices[0].delta.content
308
+ ):
309
+ # Add ONLY the new chunk to the Text object with random color style
310
+ chunk = part.choices[0].delta.content
311
+ streaming_text.append(
312
+ chunk, style=text_style
313
+ )
314
+ complete_response += chunk
315
+
316
+ # Collect chunks if requested
317
+ if collect_chunks:
318
+ chunks_collected.append(chunk)
319
+
320
+ # Call chunk callback if provided
321
+ if on_chunk_callback:
322
+ on_chunk_callback(chunk)
323
+
324
+ # Update display with new text - Rich handles all wrapping automatically
325
+ live.update(
326
+ create_streaming_panel(
327
+ streaming_text, is_complete=False
328
+ )
242
329
  )
330
+
331
+ # Final update to show completion
332
+ live.update(
333
+ create_streaming_panel(
334
+ streaming_text, is_complete=True
243
335
  )
336
+ )
244
337
 
245
- # Final update to show completion
246
- live.update(
247
- create_streaming_panel(
248
- streaming_text, is_complete=True
338
+ except Exception as e:
339
+ # Handle any streaming errors gracefully
340
+ streaming_text.append(
341
+ f"\n[Error: {str(e)}]", style="bold red"
249
342
  )
250
- )
343
+ live.update(
344
+ create_streaming_panel(
345
+ streaming_text, is_complete=True
346
+ )
347
+ )
348
+
349
+ return complete_response
350
+
351
+ def _create_dashboard_table(
352
+ self, agents_data: List[Dict[str, Any]], title: str
353
+ ) -> Panel:
354
+ """
355
+ Creates the dashboard table with the current agent statuses.
356
+ """
357
+ # Create main table
358
+ table = Table(
359
+ show_header=True,
360
+ header_style="bold magenta",
361
+ expand=True,
362
+ title=title,
363
+ title_style="bold cyan",
364
+ border_style="bright_blue",
365
+ show_lines=True, # Add lines between rows
366
+ )
367
+
368
+ # Add columns with adjusted widths
369
+ table.add_column(
370
+ "Agent Name", style="cyan", width=30, no_wrap=True
371
+ )
372
+ table.add_column(
373
+ "Status", style="green", width=20, no_wrap=True
374
+ ) # Increased width for loading animation
375
+ table.add_column(
376
+ "Output", style="white", width=100, overflow="fold"
377
+ ) # Allow text to wrap
378
+
379
+ # Add rows for each agent
380
+ for agent in agents_data:
381
+ name = Text(agent["name"], style="bold cyan")
382
+ status = self._get_status_with_loading(agent["status"])
383
+ output = Text(str(agent["output"]))
384
+ table.add_row(name, status, output)
385
+
386
+ # Create a panel to wrap the table
387
+ dashboard_panel = Panel(
388
+ table,
389
+ border_style="bright_blue",
390
+ padding=(1, 2),
391
+ title=f"[bold cyan]{title}[/bold cyan] - Total Agents: [bold green]{len(agents_data)}[/bold green]",
392
+ expand=True, # Make panel expand to full width
393
+ )
394
+
395
+ return dashboard_panel
251
396
 
252
- except Exception as e:
253
- # Handle any streaming errors gracefully
254
- streaming_text.append(
255
- f"\n[Error: {str(e)}]", style="bold red"
397
+ def print_agent_dashboard(
398
+ self,
399
+ agents_data: List[Dict[str, Any]],
400
+ title: str = "🤖 Agent Dashboard",
401
+ is_final: bool = False,
402
+ ) -> None:
403
+ """
404
+ Displays a beautiful dashboard showing agent information in a panel-like spreadsheet format.
405
+ Updates in place instead of printing multiple times.
406
+
407
+ Args:
408
+ agents_data (List[Dict[str, Any]]): List of dictionaries containing agent information.
409
+ Each dict should have: name, status, output
410
+ title (str): The title of the dashboard.
411
+ is_final (bool): Whether this is the final update of the dashboard.
412
+ """
413
+ with live_render_lock:
414
+ if self._dashboard_live is None:
415
+ # Create new Live display if none exists
416
+ self._dashboard_live = Live(
417
+ self._create_dashboard_table(agents_data, title),
418
+ console=self.console,
419
+ refresh_per_second=10, # Increased refresh rate
420
+ transient=False, # Make display persistent
256
421
  )
257
- live.update(
258
- create_streaming_panel(
259
- streaming_text, is_complete=True
260
- )
422
+ self._dashboard_live.start()
423
+ else:
424
+ # Update existing Live display
425
+ self._dashboard_live.update(
426
+ self._create_dashboard_table(agents_data, title)
261
427
  )
262
428
 
263
- return complete_response
429
+ # If this is the final update, add a newline to separate from future output
430
+ if is_final:
431
+ self.console.print() # Add blank line after final display
432
+
433
+ def stop_dashboard(self):
434
+ """
435
+ Stops and cleans up the dashboard display.
436
+ """
437
+ if self._dashboard_live is not None:
438
+ self._dashboard_live.stop()
439
+ self.console.print() # Add blank line after stopping
440
+ self._dashboard_live = None
264
441
 
265
442
 
266
443
  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.5
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=0yJkC-wxFA-HnxeDu06EvcDo9zC2f7Ee6C2H8ZxV1TQ,126709
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=ZPbQWykxhaL9inlqGw1lGxPn-PKJINyTh26GkwC7mnM,40412
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=FJCO-vjlkSb86lEJBC4081s6DsujeJkcZzkIMcQsdnk,15264
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.5.dist-info/LICENSE,sha256=jwRtEmTWjLrEsvFB6QFdYs2cEeZPRMdj-UMOFkPF8_0,11363
212
+ swarms-7.9.5.dist-info/METADATA,sha256=niWXO2hs9FG-OScaKtQPYdYZnu8VYCOvY0eZKoIugzk,32138
213
+ swarms-7.9.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
214
+ swarms-7.9.5.dist-info/entry_points.txt,sha256=2K0rTtfO1X1WaO-waJlXIKw5Voa_EpAL_yU0HXE2Jgc,47
215
+ swarms-7.9.5.dist-info/RECORD,,
File without changes