swarms 7.7.0__py3-none-any.whl → 7.7.2__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.
- swarms/prompts/paper_idea_agent.py +31 -0
- swarms/structs/__init__.py +1 -4
- swarms/structs/agent.py +176 -119
- swarms/structs/aop.py +557 -0
- swarms/structs/conversation.py +38 -7
- swarms/structs/swarm_router.py +1 -1
- swarms/telemetry/main.py +48 -26
- swarms/tools/mcp_client.py +90 -0
- swarms/utils/formatter.py +15 -1
- swarms/utils/history_output_formatter.py +29 -5
- swarms/utils/litellm_wrapper.py +26 -10
- {swarms-7.7.0.dist-info → swarms-7.7.2.dist-info}/METADATA +1 -1
- {swarms-7.7.0.dist-info → swarms-7.7.2.dist-info}/RECORD +17 -19
- swarms/structs/async_workflow.py +0 -818
- swarms/structs/octotools.py +0 -844
- swarms/structs/pulsar_swarm.py +0 -469
- swarms/structs/swarm_load_balancer.py +0 -344
- swarms/structs/talk_hier.py +0 -729
- /swarms/structs/{multi_agent_orchestrator.py → multi_agent_router.py} +0 -0
- {swarms-7.7.0.dist-info → swarms-7.7.2.dist-info}/LICENSE +0 -0
- {swarms-7.7.0.dist-info → swarms-7.7.2.dist-info}/WHEEL +0 -0
- {swarms-7.7.0.dist-info → swarms-7.7.2.dist-info}/entry_points.txt +0 -0
swarms/structs/aop.py
ADDED
@@ -0,0 +1,557 @@
|
|
1
|
+
import asyncio
|
2
|
+
import inspect
|
3
|
+
from concurrent.futures import ThreadPoolExecutor, as_completed
|
4
|
+
from functools import wraps
|
5
|
+
from typing import Any, Callable, Literal, Optional
|
6
|
+
|
7
|
+
from fastmcp import FastMCP, Client
|
8
|
+
from loguru import logger
|
9
|
+
from swarms.utils.any_to_str import any_to_str
|
10
|
+
|
11
|
+
|
12
|
+
class AOP:
|
13
|
+
"""
|
14
|
+
Agent-Orchestration Protocol (AOP) class for managing tools, agents, and swarms.
|
15
|
+
|
16
|
+
This class provides decorators and methods for registering and running various components
|
17
|
+
in a Swarms environment. It handles logging, metadata management, and execution control.
|
18
|
+
|
19
|
+
Attributes:
|
20
|
+
name (str): The name of the AOP instance
|
21
|
+
description (str): A description of the AOP instance
|
22
|
+
mcp (FastMCP): The underlying FastMCP instance for managing components
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
name: Optional[str] = None,
|
28
|
+
description: Optional[str] = None,
|
29
|
+
url: Optional[str] = "http://localhost:8000/sse",
|
30
|
+
*args,
|
31
|
+
**kwargs,
|
32
|
+
):
|
33
|
+
"""
|
34
|
+
Initialize the AOP instance.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
name (str): The name of the AOP instance
|
38
|
+
description (str): A description of the AOP instance
|
39
|
+
url (str): The URL of the MCP instance
|
40
|
+
*args: Additional positional arguments passed to FastMCP
|
41
|
+
**kwargs: Additional keyword arguments passed to FastMCP
|
42
|
+
"""
|
43
|
+
logger.info(f"[AOP] Initializing AOP instance: {name}")
|
44
|
+
self.name = name
|
45
|
+
self.description = description
|
46
|
+
self.url = url
|
47
|
+
|
48
|
+
self.tools = {}
|
49
|
+
self.swarms = {}
|
50
|
+
|
51
|
+
self.mcp = FastMCP(name=name, *args, **kwargs)
|
52
|
+
|
53
|
+
logger.success(
|
54
|
+
f"[AOP] Successfully initialized AOP instance: {name}"
|
55
|
+
)
|
56
|
+
|
57
|
+
def tool(
|
58
|
+
self,
|
59
|
+
name: Optional[str] = None,
|
60
|
+
description: Optional[str] = None,
|
61
|
+
):
|
62
|
+
"""
|
63
|
+
Decorator to register an MCP tool with optional metadata.
|
64
|
+
|
65
|
+
This decorator registers a function as a tool in the MCP system. It handles
|
66
|
+
logging, metadata management, and execution tracking.
|
67
|
+
|
68
|
+
Args:
|
69
|
+
name (Optional[str]): Custom name for the tool. If None, uses function name
|
70
|
+
description (Optional[str]): Custom description. If None, uses function docstring
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
Callable: A decorator function that registers the tool
|
74
|
+
"""
|
75
|
+
logger.debug(
|
76
|
+
f"[AOP] Creating tool decorator with name={name}, description={description}"
|
77
|
+
)
|
78
|
+
|
79
|
+
def decorator(func: Callable):
|
80
|
+
tool_name = name or func.__name__
|
81
|
+
tool_description = description or (
|
82
|
+
inspect.getdoc(func) or ""
|
83
|
+
)
|
84
|
+
|
85
|
+
logger.debug(
|
86
|
+
f"[AOP] Registering tool: {tool_name} - {tool_description}"
|
87
|
+
)
|
88
|
+
|
89
|
+
self.tools[tool_name] = {
|
90
|
+
"name": tool_name,
|
91
|
+
"description": tool_description,
|
92
|
+
"function": func,
|
93
|
+
}
|
94
|
+
|
95
|
+
@self.mcp.tool(
|
96
|
+
name=f"tool_{tool_name}", description=tool_description
|
97
|
+
)
|
98
|
+
@wraps(func)
|
99
|
+
async def wrapper(*args, **kwargs) -> Any:
|
100
|
+
logger.info(
|
101
|
+
f"[TOOL:{tool_name}] ➤ called with args={args}, kwargs={kwargs}"
|
102
|
+
)
|
103
|
+
try:
|
104
|
+
result = await func(*args, **kwargs)
|
105
|
+
logger.success(f"[TOOL:{tool_name}] ✅ completed")
|
106
|
+
return result
|
107
|
+
except Exception as e:
|
108
|
+
logger.error(
|
109
|
+
f"[TOOL:{tool_name}] ❌ failed with error: {str(e)}"
|
110
|
+
)
|
111
|
+
raise
|
112
|
+
|
113
|
+
return wrapper
|
114
|
+
|
115
|
+
return decorator
|
116
|
+
|
117
|
+
def agent(
|
118
|
+
self,
|
119
|
+
name: Optional[str] = None,
|
120
|
+
description: Optional[str] = None,
|
121
|
+
):
|
122
|
+
"""
|
123
|
+
Decorator to define an agent entry point.
|
124
|
+
|
125
|
+
This decorator registers a function as an agent in the MCP system. It handles
|
126
|
+
logging, metadata management, and execution tracking for agent operations.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
name (Optional[str]): Custom name for the agent. If None, uses 'agent_' + function name
|
130
|
+
description (Optional[str]): Custom description. If None, uses function docstring
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
Callable: A decorator function that registers the agent
|
134
|
+
"""
|
135
|
+
logger.debug(
|
136
|
+
f"[AOP] Creating agent decorator with name={name}, description={description}"
|
137
|
+
)
|
138
|
+
|
139
|
+
def decorator(func: Callable):
|
140
|
+
agent_name = name or f"agent_{func.__name__}"
|
141
|
+
agent_description = description or (
|
142
|
+
inspect.getdoc(func) or ""
|
143
|
+
)
|
144
|
+
|
145
|
+
@self.mcp.tool(
|
146
|
+
name=agent_name, description=agent_description
|
147
|
+
)
|
148
|
+
@wraps(func)
|
149
|
+
async def wrapper(*args, **kwargs):
|
150
|
+
logger.info(f"[AGENT:{agent_name}] 👤 Starting")
|
151
|
+
try:
|
152
|
+
result = await func(*args, **kwargs)
|
153
|
+
logger.success(
|
154
|
+
f"[AGENT:{agent_name}] ✅ Finished"
|
155
|
+
)
|
156
|
+
return result
|
157
|
+
except Exception as e:
|
158
|
+
logger.error(
|
159
|
+
f"[AGENT:{agent_name}] ❌ failed with error: {str(e)}"
|
160
|
+
)
|
161
|
+
raise
|
162
|
+
|
163
|
+
wrapper._is_agent = True
|
164
|
+
wrapper._agent_name = agent_name
|
165
|
+
wrapper._agent_description = agent_description
|
166
|
+
return wrapper
|
167
|
+
|
168
|
+
return decorator
|
169
|
+
|
170
|
+
def swarm(
|
171
|
+
self,
|
172
|
+
name: Optional[str] = None,
|
173
|
+
description: Optional[str] = None,
|
174
|
+
):
|
175
|
+
"""
|
176
|
+
Decorator to define a swarm controller.
|
177
|
+
|
178
|
+
This decorator registers a function as a swarm controller in the MCP system.
|
179
|
+
It handles logging, metadata management, and execution tracking for swarm operations.
|
180
|
+
|
181
|
+
Args:
|
182
|
+
name (Optional[str]): Custom name for the swarm. If None, uses 'swarm_' + function name
|
183
|
+
description (Optional[str]): Custom description. If None, uses function docstring
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
Callable: A decorator function that registers the swarm
|
187
|
+
"""
|
188
|
+
logger.debug(
|
189
|
+
f"[AOP] Creating swarm decorator with name={name}, description={description}"
|
190
|
+
)
|
191
|
+
|
192
|
+
def decorator(func: Callable):
|
193
|
+
swarm_name = name or f"swarm_{func.__name__}"
|
194
|
+
swarm_description = description or (
|
195
|
+
inspect.getdoc(func) or ""
|
196
|
+
)
|
197
|
+
|
198
|
+
@self.mcp.tool(
|
199
|
+
name=swarm_name, description=swarm_description
|
200
|
+
)
|
201
|
+
@wraps(func)
|
202
|
+
async def wrapper(*args, **kwargs):
|
203
|
+
logger.info(
|
204
|
+
f"[SWARM:{swarm_name}] 🐝 Spawning swarm..."
|
205
|
+
)
|
206
|
+
try:
|
207
|
+
result = await func(*args, **kwargs)
|
208
|
+
logger.success(
|
209
|
+
f"[SWARM:{swarm_name}] 🐝 Completed"
|
210
|
+
)
|
211
|
+
return result
|
212
|
+
except Exception as e:
|
213
|
+
logger.error(
|
214
|
+
f"[SWARM:{swarm_name}] ❌ failed with error: {str(e)}"
|
215
|
+
)
|
216
|
+
raise
|
217
|
+
|
218
|
+
wrapper._is_swarm = True
|
219
|
+
wrapper._swarm_name = swarm_name
|
220
|
+
wrapper._swarm_description = swarm_description
|
221
|
+
return wrapper
|
222
|
+
|
223
|
+
return decorator
|
224
|
+
|
225
|
+
def run(self, method: Literal["stdio", "sse"], *args, **kwargs):
|
226
|
+
"""
|
227
|
+
Run the MCP with the specified method.
|
228
|
+
|
229
|
+
Args:
|
230
|
+
method (Literal['stdio', 'sse']): The execution method to use
|
231
|
+
*args: Additional positional arguments for the run method
|
232
|
+
**kwargs: Additional keyword arguments for the run method
|
233
|
+
|
234
|
+
Returns:
|
235
|
+
Any: The result of the MCP run operation
|
236
|
+
"""
|
237
|
+
logger.info(f"[AOP] Running MCP with method: {method}")
|
238
|
+
try:
|
239
|
+
result = self.mcp.run(method, *args, **kwargs)
|
240
|
+
logger.success(
|
241
|
+
f"[AOP] Successfully ran MCP with method: {method}"
|
242
|
+
)
|
243
|
+
return result
|
244
|
+
except Exception as e:
|
245
|
+
logger.error(
|
246
|
+
f"[AOP] Failed to run MCP with method {method}: {str(e)}"
|
247
|
+
)
|
248
|
+
raise
|
249
|
+
|
250
|
+
def run_stdio(self, *args, **kwargs):
|
251
|
+
"""
|
252
|
+
Run the MCP using standard I/O method.
|
253
|
+
|
254
|
+
Args:
|
255
|
+
*args: Additional positional arguments for the run method
|
256
|
+
**kwargs: Additional keyword arguments for the run method
|
257
|
+
|
258
|
+
Returns:
|
259
|
+
Any: The result of the MCP run operation
|
260
|
+
"""
|
261
|
+
logger.info("[AOP] Running MCP with stdio method")
|
262
|
+
return self.run("stdio", *args, **kwargs)
|
263
|
+
|
264
|
+
def run_sse(self, *args, **kwargs):
|
265
|
+
"""
|
266
|
+
Run the MCP using Server-Sent Events method.
|
267
|
+
|
268
|
+
Args:
|
269
|
+
*args: Additional positional arguments for the run method
|
270
|
+
**kwargs: Additional keyword arguments for the run method
|
271
|
+
|
272
|
+
Returns:
|
273
|
+
Any: The result of the MCP run operation
|
274
|
+
"""
|
275
|
+
logger.info("[AOP] Running MCP with SSE method")
|
276
|
+
return self.run("sse", *args, **kwargs)
|
277
|
+
|
278
|
+
def list_available(
|
279
|
+
self, output_type: Literal["str", "list"] = "str"
|
280
|
+
):
|
281
|
+
"""
|
282
|
+
List all available tools in the MCP.
|
283
|
+
|
284
|
+
Returns:
|
285
|
+
list: A list of all registered tools
|
286
|
+
"""
|
287
|
+
if output_type == "str":
|
288
|
+
return any_to_str(self.mcp.list_tools())
|
289
|
+
elif output_type == "list":
|
290
|
+
return self.mcp.list_tools()
|
291
|
+
else:
|
292
|
+
raise ValueError(f"Invalid output type: {output_type}")
|
293
|
+
|
294
|
+
async def check_utility_exists(
|
295
|
+
self, url: str, name: str, *args, **kwargs
|
296
|
+
):
|
297
|
+
async with Client(url, *args, **kwargs) as client:
|
298
|
+
if any(tool.name == name for tool in client.list_tools()):
|
299
|
+
return True
|
300
|
+
else:
|
301
|
+
return False
|
302
|
+
|
303
|
+
async def _call_tool(
|
304
|
+
self, url: str, name: str, arguments: dict, *args, **kwargs
|
305
|
+
):
|
306
|
+
try:
|
307
|
+
async with Client(url, *args, **kwargs) as client:
|
308
|
+
result = await client.call_tool(name, arguments)
|
309
|
+
logger.info(
|
310
|
+
f"Client connected: {client.is_connected()}"
|
311
|
+
)
|
312
|
+
return result
|
313
|
+
except Exception as e:
|
314
|
+
logger.error(f"Error calling tool: {e}")
|
315
|
+
return None
|
316
|
+
|
317
|
+
def call_tool(
|
318
|
+
self,
|
319
|
+
url: str,
|
320
|
+
name: str,
|
321
|
+
arguments: dict,
|
322
|
+
*args,
|
323
|
+
**kwargs,
|
324
|
+
):
|
325
|
+
return asyncio.run(
|
326
|
+
self._call_tool(url, name, arguments, *args, **kwargs)
|
327
|
+
)
|
328
|
+
|
329
|
+
def call_tool_or_agent(
|
330
|
+
self,
|
331
|
+
url: str,
|
332
|
+
name: str,
|
333
|
+
arguments: dict,
|
334
|
+
output_type: Literal["str", "list"] = "str",
|
335
|
+
*args,
|
336
|
+
**kwargs,
|
337
|
+
):
|
338
|
+
"""
|
339
|
+
Execute a tool or agent by name.
|
340
|
+
|
341
|
+
Args:
|
342
|
+
name (str): The name of the tool or agent to execute
|
343
|
+
arguments (dict): The arguments to pass to the tool or agent
|
344
|
+
"""
|
345
|
+
if output_type == "str":
|
346
|
+
return any_to_str(
|
347
|
+
self.call_tool(
|
348
|
+
url=url, name=name, arguments=arguments
|
349
|
+
)
|
350
|
+
)
|
351
|
+
elif output_type == "list":
|
352
|
+
return self.call_tool(
|
353
|
+
url=url, name=name, arguments=arguments
|
354
|
+
)
|
355
|
+
else:
|
356
|
+
raise ValueError(f"Invalid output type: {output_type}")
|
357
|
+
|
358
|
+
def call_tool_or_agent_batched(
|
359
|
+
self,
|
360
|
+
url: str,
|
361
|
+
names: list[str],
|
362
|
+
arguments: list[dict],
|
363
|
+
output_type: Literal["str", "list"] = "str",
|
364
|
+
*args,
|
365
|
+
**kwargs,
|
366
|
+
):
|
367
|
+
"""
|
368
|
+
Execute a list of tools or agents by name.
|
369
|
+
|
370
|
+
Args:
|
371
|
+
names (list[str]): The names of the tools or agents to execute
|
372
|
+
"""
|
373
|
+
if output_type == "str":
|
374
|
+
return [
|
375
|
+
any_to_str(
|
376
|
+
self.call_tool_or_agent(
|
377
|
+
url=url,
|
378
|
+
name=name,
|
379
|
+
arguments=argument,
|
380
|
+
*args,
|
381
|
+
**kwargs,
|
382
|
+
)
|
383
|
+
)
|
384
|
+
for name, argument in zip(names, arguments)
|
385
|
+
]
|
386
|
+
elif output_type == "list":
|
387
|
+
return [
|
388
|
+
self.call_tool_or_agent(
|
389
|
+
url=url,
|
390
|
+
name=name,
|
391
|
+
arguments=argument,
|
392
|
+
*args,
|
393
|
+
**kwargs,
|
394
|
+
)
|
395
|
+
for name, argument in zip(names, arguments)
|
396
|
+
]
|
397
|
+
else:
|
398
|
+
raise ValueError(f"Invalid output type: {output_type}")
|
399
|
+
|
400
|
+
def call_tool_or_agent_concurrently(
|
401
|
+
self,
|
402
|
+
url: str,
|
403
|
+
names: list[str],
|
404
|
+
arguments: list[dict],
|
405
|
+
output_type: Literal["str", "list"] = "str",
|
406
|
+
*args,
|
407
|
+
**kwargs,
|
408
|
+
):
|
409
|
+
"""
|
410
|
+
Execute a list of tools or agents by name concurrently.
|
411
|
+
|
412
|
+
Args:
|
413
|
+
names (list[str]): The names of the tools or agents to execute
|
414
|
+
arguments (list[dict]): The arguments to pass to the tools or agents
|
415
|
+
"""
|
416
|
+
outputs = []
|
417
|
+
with ThreadPoolExecutor(max_workers=len(names)) as executor:
|
418
|
+
futures = [
|
419
|
+
executor.submit(
|
420
|
+
self.call_tool_or_agent,
|
421
|
+
url=url,
|
422
|
+
name=name,
|
423
|
+
arguments=argument,
|
424
|
+
*args,
|
425
|
+
**kwargs,
|
426
|
+
)
|
427
|
+
for name, argument in zip(names, arguments)
|
428
|
+
]
|
429
|
+
for future in as_completed(futures):
|
430
|
+
outputs.append(future.result())
|
431
|
+
|
432
|
+
if output_type == "str":
|
433
|
+
return any_to_str(outputs)
|
434
|
+
elif output_type == "list":
|
435
|
+
return outputs
|
436
|
+
else:
|
437
|
+
raise ValueError(f"Invalid output type: {output_type}")
|
438
|
+
|
439
|
+
def call_swarm(
|
440
|
+
self,
|
441
|
+
url: str,
|
442
|
+
name: str,
|
443
|
+
arguments: dict,
|
444
|
+
output_type: Literal["str", "list"] = "str",
|
445
|
+
*args,
|
446
|
+
**kwargs,
|
447
|
+
):
|
448
|
+
"""
|
449
|
+
Execute a swarm by name.
|
450
|
+
|
451
|
+
Args:
|
452
|
+
name (str): The name of the swarm to execute
|
453
|
+
"""
|
454
|
+
if output_type == "str":
|
455
|
+
return any_to_str(
|
456
|
+
asyncio.run(
|
457
|
+
self._call_tool(
|
458
|
+
url=url,
|
459
|
+
name=name,
|
460
|
+
arguments=arguments,
|
461
|
+
)
|
462
|
+
)
|
463
|
+
)
|
464
|
+
elif output_type == "list":
|
465
|
+
return asyncio.run(
|
466
|
+
self._call_tool(
|
467
|
+
url=url,
|
468
|
+
name=name,
|
469
|
+
arguments=arguments,
|
470
|
+
)
|
471
|
+
)
|
472
|
+
else:
|
473
|
+
raise ValueError(f"Invalid output type: {output_type}")
|
474
|
+
|
475
|
+
def list_agents(
|
476
|
+
self, output_type: Literal["str", "list"] = "str"
|
477
|
+
):
|
478
|
+
"""
|
479
|
+
List all available agents in the MCP.
|
480
|
+
|
481
|
+
Returns:
|
482
|
+
list: A list of all registered agents
|
483
|
+
"""
|
484
|
+
|
485
|
+
out = self.list_all()
|
486
|
+
agents = []
|
487
|
+
for item in out:
|
488
|
+
if "agent" in item["name"]:
|
489
|
+
agents.append(item)
|
490
|
+
return agents
|
491
|
+
|
492
|
+
def list_swarms(
|
493
|
+
self, output_type: Literal["str", "list"] = "str"
|
494
|
+
):
|
495
|
+
"""
|
496
|
+
List all available swarms in the MCP.
|
497
|
+
|
498
|
+
Returns:
|
499
|
+
list: A list of all registered swarms
|
500
|
+
"""
|
501
|
+
out = self.list_all()
|
502
|
+
agents = []
|
503
|
+
for item in out:
|
504
|
+
if "swarm" in item["name"]:
|
505
|
+
agents.append(item)
|
506
|
+
return agents
|
507
|
+
|
508
|
+
async def _list_all(self):
|
509
|
+
async with Client(self.url) as client:
|
510
|
+
return await client.list_tools()
|
511
|
+
|
512
|
+
def list_all(self):
|
513
|
+
out = asyncio.run(self._list_all())
|
514
|
+
|
515
|
+
outputs = []
|
516
|
+
for tool in out:
|
517
|
+
outputs.append(tool.model_dump())
|
518
|
+
|
519
|
+
return outputs
|
520
|
+
|
521
|
+
def list_tool_parameters(self, name: str):
|
522
|
+
out = self.list_all()
|
523
|
+
|
524
|
+
# Find the tool by name
|
525
|
+
for tool in out:
|
526
|
+
if tool["name"] == name:
|
527
|
+
return tool
|
528
|
+
return None
|
529
|
+
|
530
|
+
def search_if_tool_exists(self, name: str):
|
531
|
+
out = self.list_all()
|
532
|
+
for tool in out:
|
533
|
+
if tool["name"] == name:
|
534
|
+
return True
|
535
|
+
return False
|
536
|
+
|
537
|
+
def search(
|
538
|
+
self,
|
539
|
+
type: Literal["tool", "agent", "swarm"],
|
540
|
+
name: str,
|
541
|
+
output_type: Literal["str", "list"] = "str",
|
542
|
+
):
|
543
|
+
"""
|
544
|
+
Search for a tool, agent, or swarm by name.
|
545
|
+
|
546
|
+
Args:
|
547
|
+
type (Literal["tool", "agent", "swarm"]): The type of the item to search for
|
548
|
+
name (str): The name of the item to search for
|
549
|
+
|
550
|
+
Returns:
|
551
|
+
dict: The item if found, otherwise None
|
552
|
+
"""
|
553
|
+
all_items = self.list_all()
|
554
|
+
for item in all_items:
|
555
|
+
if item["name"] == name:
|
556
|
+
return item
|
557
|
+
return None
|
swarms/structs/conversation.py
CHANGED
@@ -105,7 +105,7 @@ class Conversation(BaseStructure):
|
|
105
105
|
if tokenizer is not None:
|
106
106
|
self.truncate_memory_with_tokenizer()
|
107
107
|
|
108
|
-
def
|
108
|
+
def _add(
|
109
109
|
self,
|
110
110
|
role: str,
|
111
111
|
content: Union[str, dict, list],
|
@@ -118,8 +118,6 @@ class Conversation(BaseStructure):
|
|
118
118
|
role (str): The role of the speaker (e.g., 'User', 'System').
|
119
119
|
content (Union[str, dict, list]): The content of the message to be added.
|
120
120
|
"""
|
121
|
-
now = datetime.datetime.now()
|
122
|
-
now.strftime("%Y-%m-%d %H:%M:%S")
|
123
121
|
|
124
122
|
# Base message with role
|
125
123
|
message = {
|
@@ -131,7 +129,7 @@ class Conversation(BaseStructure):
|
|
131
129
|
message["content"] = content
|
132
130
|
elif self.time_enabled:
|
133
131
|
message["content"] = (
|
134
|
-
f"Time: {now.strftime('%Y-%m-%d %H:%M:%S')} \n {content}"
|
132
|
+
f"Time: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} \n {content}"
|
135
133
|
)
|
136
134
|
else:
|
137
135
|
message["content"] = content
|
@@ -159,9 +157,21 @@ class Conversation(BaseStructure):
|
|
159
157
|
True # Make thread terminate when main program exits
|
160
158
|
)
|
161
159
|
token_thread.start()
|
162
|
-
|
163
|
-
|
164
|
-
|
160
|
+
|
161
|
+
def add(self, role: str, content: Union[str, dict, list]):
|
162
|
+
"""Add a message to the conversation history.
|
163
|
+
|
164
|
+
Args:
|
165
|
+
role (str): The role of the speaker (e.g., 'User', 'System').
|
166
|
+
content (Union[str, dict, list]): The content of the message to be added.
|
167
|
+
"""
|
168
|
+
process_thread = threading.Thread(
|
169
|
+
target=self._add,
|
170
|
+
args=(role, content),
|
171
|
+
daemon=True,
|
172
|
+
)
|
173
|
+
process_thread.start()
|
174
|
+
# process_thread.join()
|
165
175
|
|
166
176
|
def delete(self, index: str):
|
167
177
|
"""Delete a message from the conversation history.
|
@@ -524,6 +534,27 @@ class Conversation(BaseStructure):
|
|
524
534
|
# print(output)
|
525
535
|
return output
|
526
536
|
|
537
|
+
def return_all_except_first(self):
|
538
|
+
"""Return all messages except the first one.
|
539
|
+
|
540
|
+
Returns:
|
541
|
+
list: List of messages except the first one.
|
542
|
+
"""
|
543
|
+
return self.conversation_history[2:]
|
544
|
+
|
545
|
+
def return_all_except_first_string(self):
|
546
|
+
"""Return all messages except the first one as a string.
|
547
|
+
|
548
|
+
Returns:
|
549
|
+
str: All messages except the first one as a string.
|
550
|
+
"""
|
551
|
+
return "\n".join(
|
552
|
+
[
|
553
|
+
f"{msg['content']}"
|
554
|
+
for msg in self.conversation_history[2:]
|
555
|
+
]
|
556
|
+
)
|
557
|
+
|
527
558
|
|
528
559
|
# # Example usage
|
529
560
|
# # conversation = Conversation()
|
swarms/structs/swarm_router.py
CHANGED
@@ -13,7 +13,7 @@ from swarms.structs.groupchat import GroupChat
|
|
13
13
|
from swarms.structs.hiearchical_swarm import HierarchicalSwarm
|
14
14
|
from swarms.structs.majority_voting import MajorityVoting
|
15
15
|
from swarms.structs.mixture_of_agents import MixtureOfAgents
|
16
|
-
from swarms.structs.
|
16
|
+
from swarms.structs.multi_agent_router import MultiAgentRouter
|
17
17
|
from swarms.structs.rearrange import AgentRearrange
|
18
18
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
19
19
|
from swarms.structs.spreadsheet_swarm import SpreadSheetSwarm
|