swarms 7.8.7__py3-none-any.whl → 7.8.9__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/structs/agent.py +104 -60
- swarms/structs/conversation.py +6 -0
- swarms/structs/csv_to_agent.py +1 -3
- swarms/telemetry/__init__.py +0 -4
- swarms/telemetry/main.py +24 -122
- swarms/tools/__init__.py +10 -0
- swarms/tools/base_tool.py +9 -5
- swarms/tools/mcp_client_call.py +508 -0
- swarms/tools/py_func_to_openai_func_str.py +0 -1
- swarms/utils/auto_download_check_packages.py +4 -3
- swarms/utils/history_output_formatter.py +2 -0
- swarms/utils/output_types.py +1 -1
- {swarms-7.8.7.dist-info → swarms-7.8.9.dist-info}/METADATA +1 -1
- {swarms-7.8.7.dist-info → swarms-7.8.9.dist-info}/RECORD +17 -17
- {swarms-7.8.7.dist-info → swarms-7.8.9.dist-info}/LICENSE +0 -0
- {swarms-7.8.7.dist-info → swarms-7.8.9.dist-info}/WHEEL +0 -0
- {swarms-7.8.7.dist-info → swarms-7.8.9.dist-info}/entry_points.txt +0 -0
swarms/structs/agent.py
CHANGED
@@ -72,8 +72,10 @@ from swarms.prompts.max_loop_prompt import generate_reasoning_prompt
|
|
72
72
|
from swarms.prompts.safety_prompt import SAFETY_PROMPT
|
73
73
|
from swarms.structs.ma_utils import set_random_models_for_agents
|
74
74
|
from swarms.tools.mcp_client_call import (
|
75
|
+
execute_multiple_tools_on_multiple_mcp_servers_sync,
|
75
76
|
execute_tool_call_simple,
|
76
77
|
get_mcp_tools_sync,
|
78
|
+
get_tools_for_multiple_mcp_servers,
|
77
79
|
)
|
78
80
|
from swarms.schemas.mcp_schemas import (
|
79
81
|
MCPConnection,
|
@@ -81,7 +83,6 @@ from swarms.schemas.mcp_schemas import (
|
|
81
83
|
from swarms.utils.index import (
|
82
84
|
exists,
|
83
85
|
format_data_structure,
|
84
|
-
format_dict_to_string,
|
85
86
|
)
|
86
87
|
from swarms.schemas.conversation_schema import ConversationSchema
|
87
88
|
from swarms.utils.output_types import OutputType
|
@@ -417,7 +418,8 @@ class Agent:
|
|
417
418
|
llm_base_url: Optional[str] = None,
|
418
419
|
llm_api_key: Optional[str] = None,
|
419
420
|
rag_config: Optional[RAGConfig] = None,
|
420
|
-
|
421
|
+
tool_call_summary: bool = True,
|
422
|
+
output_raw_json_from_tool_call: bool = False,
|
421
423
|
*args,
|
422
424
|
**kwargs,
|
423
425
|
):
|
@@ -446,7 +448,10 @@ class Agent:
|
|
446
448
|
self.system_prompt = system_prompt
|
447
449
|
self.agent_name = agent_name
|
448
450
|
self.agent_description = agent_description
|
449
|
-
self.saved_state_path = f"{self.agent_name}_{generate_api_key(prefix='agent-')}_state.json"
|
451
|
+
# self.saved_state_path = f"{self.agent_name}_{generate_api_key(prefix='agent-')}_state.json"
|
452
|
+
self.saved_state_path = (
|
453
|
+
f"{generate_api_key(prefix='agent-')}_state.json"
|
454
|
+
)
|
450
455
|
self.autosave = autosave
|
451
456
|
self.response_filters = []
|
452
457
|
self.self_healing_enabled = self_healing_enabled
|
@@ -549,7 +554,10 @@ class Agent:
|
|
549
554
|
self.llm_base_url = llm_base_url
|
550
555
|
self.llm_api_key = llm_api_key
|
551
556
|
self.rag_config = rag_config
|
552
|
-
self.
|
557
|
+
self.tool_call_summary = tool_call_summary
|
558
|
+
self.output_raw_json_from_tool_call = (
|
559
|
+
output_raw_json_from_tool_call
|
560
|
+
)
|
553
561
|
|
554
562
|
# self.short_memory = self.short_memory_init()
|
555
563
|
|
@@ -623,7 +631,7 @@ class Agent:
|
|
623
631
|
|
624
632
|
self.short_memory.add(
|
625
633
|
role=f"{self.agent_name}",
|
626
|
-
content=
|
634
|
+
content=self.tools_list_dictionary,
|
627
635
|
)
|
628
636
|
|
629
637
|
def short_memory_init(self):
|
@@ -692,6 +700,10 @@ class Agent:
|
|
692
700
|
|
693
701
|
if exists(self.tools) and len(self.tools) >= 2:
|
694
702
|
parallel_tool_calls = True
|
703
|
+
elif exists(self.mcp_url) or exists(self.mcp_urls):
|
704
|
+
parallel_tool_calls = True
|
705
|
+
elif exists(self.mcp_config):
|
706
|
+
parallel_tool_calls = True
|
695
707
|
else:
|
696
708
|
parallel_tool_calls = False
|
697
709
|
|
@@ -714,7 +726,7 @@ class Agent:
|
|
714
726
|
parallel_tool_calls=parallel_tool_calls,
|
715
727
|
)
|
716
728
|
|
717
|
-
elif self.mcp_url
|
729
|
+
elif exists(self.mcp_url) or exists(self.mcp_urls):
|
718
730
|
self.llm = LiteLLM(
|
719
731
|
**common_args,
|
720
732
|
tools_list_dictionary=self.add_mcp_tools_to_memory(),
|
@@ -752,15 +764,27 @@ class Agent:
|
|
752
764
|
tools = get_mcp_tools_sync(server_path=self.mcp_url)
|
753
765
|
elif exists(self.mcp_config):
|
754
766
|
tools = get_mcp_tools_sync(connection=self.mcp_config)
|
755
|
-
logger.info(f"Tools: {tools}")
|
767
|
+
# logger.info(f"Tools: {tools}")
|
768
|
+
elif exists(self.mcp_urls):
|
769
|
+
tools = get_tools_for_multiple_mcp_servers(
|
770
|
+
urls=self.mcp_urls,
|
771
|
+
output_type="str",
|
772
|
+
)
|
773
|
+
# print(f"Tools: {tools} for {self.mcp_urls}")
|
756
774
|
else:
|
757
775
|
raise AgentMCPConnectionError(
|
758
776
|
"mcp_url must be either a string URL or MCPConnection object"
|
759
777
|
)
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
778
|
+
|
779
|
+
if (
|
780
|
+
exists(self.mcp_url)
|
781
|
+
or exists(self.mcp_urls)
|
782
|
+
or exists(self.mcp_config)
|
783
|
+
):
|
784
|
+
self.pretty_print(
|
785
|
+
f"✨ [SYSTEM] Successfully integrated {len(tools)} MCP tools into agent: {self.agent_name} | Status: ONLINE | Time: {time.strftime('%H:%M:%S')} ✨",
|
786
|
+
loop_count=0,
|
787
|
+
)
|
764
788
|
|
765
789
|
return tools
|
766
790
|
except AgentMCPConnectionError as e:
|
@@ -939,7 +963,7 @@ class Agent:
|
|
939
963
|
|
940
964
|
self.short_memory.add(role=self.user_name, content=task)
|
941
965
|
|
942
|
-
if self.plan_enabled:
|
966
|
+
if self.plan_enabled or self.planning_prompt is not None:
|
943
967
|
self.plan(task)
|
944
968
|
|
945
969
|
# Set the loop count
|
@@ -1006,55 +1030,51 @@ class Agent:
|
|
1006
1030
|
)
|
1007
1031
|
self.memory_query(task_prompt)
|
1008
1032
|
|
1009
|
-
# # Generate response using LLM
|
1010
|
-
# response_args = (
|
1011
|
-
# (task_prompt, *args)
|
1012
|
-
# if img is None
|
1013
|
-
# else (task_prompt, img, *args)
|
1014
|
-
# )
|
1015
|
-
|
1016
|
-
# # Call the LLM
|
1017
|
-
# response = self.call_llm(
|
1018
|
-
# *response_args, **kwargs
|
1019
|
-
# )
|
1020
|
-
|
1021
1033
|
response = self.call_llm(
|
1022
1034
|
task=task_prompt, img=img, *args, **kwargs
|
1023
1035
|
)
|
1024
1036
|
|
1037
|
+
print(f"Response: {response}")
|
1038
|
+
|
1025
1039
|
if exists(self.tools_list_dictionary):
|
1026
1040
|
if isinstance(response, BaseModel):
|
1027
1041
|
response = response.model_dump()
|
1028
1042
|
|
1029
|
-
#
|
1030
|
-
# if self.mcp_url is None or self.tools is None:
|
1043
|
+
# Parse the response from the agent with the output type
|
1031
1044
|
response = self.parse_llm_output(response)
|
1032
1045
|
|
1033
1046
|
self.short_memory.add(
|
1034
1047
|
role=self.agent_name,
|
1035
|
-
content=
|
1048
|
+
content=response,
|
1036
1049
|
)
|
1037
1050
|
|
1038
1051
|
# Print
|
1039
1052
|
self.pretty_print(response, loop_count)
|
1040
1053
|
|
1041
|
-
#
|
1042
|
-
# self.output_cleaner_op(response)
|
1043
|
-
|
1044
|
-
# Check and execute tools
|
1054
|
+
# Check and execute callable tools
|
1045
1055
|
if exists(self.tools):
|
1046
1056
|
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
)
|
1057
|
+
if (
|
1058
|
+
self.output_raw_json_from_tool_call
|
1059
|
+
is True
|
1060
|
+
):
|
1061
|
+
print(type(response))
|
1062
|
+
response = response
|
1063
|
+
else:
|
1064
|
+
self.execute_tools(
|
1065
|
+
response=response,
|
1066
|
+
loop_count=loop_count,
|
1067
|
+
)
|
1051
1068
|
|
1052
1069
|
# Handle MCP tools
|
1053
|
-
if
|
1054
|
-
self.
|
1070
|
+
if (
|
1071
|
+
exists(self.mcp_url)
|
1072
|
+
or exists(self.mcp_config)
|
1073
|
+
or exists(self.mcp_urls)
|
1055
1074
|
):
|
1056
1075
|
self.mcp_tool_handling(
|
1057
|
-
response,
|
1076
|
+
response=response,
|
1077
|
+
current_loop=loop_count,
|
1058
1078
|
)
|
1059
1079
|
|
1060
1080
|
self.sentiment_and_evaluator(response)
|
@@ -1298,26 +1318,47 @@ class Agent:
|
|
1298
1318
|
|
1299
1319
|
def plan(self, task: str, *args, **kwargs) -> None:
|
1300
1320
|
"""
|
1301
|
-
|
1321
|
+
Create a strategic plan for executing the given task.
|
1322
|
+
|
1323
|
+
This method generates a step-by-step plan by combining the conversation
|
1324
|
+
history, planning prompt, and current task. The plan is then added to
|
1325
|
+
the agent's short-term memory for reference during execution.
|
1302
1326
|
|
1303
1327
|
Args:
|
1304
|
-
task (str): The task to plan
|
1328
|
+
task (str): The task to create a plan for
|
1329
|
+
*args: Additional positional arguments passed to the LLM
|
1330
|
+
**kwargs: Additional keyword arguments passed to the LLM
|
1331
|
+
|
1332
|
+
Returns:
|
1333
|
+
None: The plan is stored in memory rather than returned
|
1334
|
+
|
1335
|
+
Raises:
|
1336
|
+
Exception: If planning fails, the original exception is re-raised
|
1305
1337
|
"""
|
1306
1338
|
try:
|
1307
|
-
|
1308
|
-
|
1309
|
-
planning_prompt = f"{self.planning_prompt} {task}"
|
1310
|
-
plan = self.llm(planning_prompt, *args, **kwargs)
|
1311
|
-
logger.info(f"Plan: {plan}")
|
1339
|
+
# Get the current conversation history
|
1340
|
+
history = self.short_memory.get_str()
|
1312
1341
|
|
1313
|
-
#
|
1314
|
-
|
1315
|
-
|
1342
|
+
# Construct the planning prompt by combining history, planning prompt, and task
|
1343
|
+
planning_prompt = (
|
1344
|
+
f"{history}\n\n{self.planning_prompt}\n\nTask: {task}"
|
1316
1345
|
)
|
1317
1346
|
|
1347
|
+
# Generate the plan using the LLM
|
1348
|
+
plan = self.llm.run(task=planning_prompt, *args, **kwargs)
|
1349
|
+
|
1350
|
+
# Store the generated plan in short-term memory
|
1351
|
+
self.short_memory.add(role=self.agent_name, content=plan)
|
1352
|
+
|
1353
|
+
logger.info(
|
1354
|
+
f"Successfully created plan for task: {task[:50]}..."
|
1355
|
+
)
|
1318
1356
|
return None
|
1357
|
+
|
1319
1358
|
except Exception as error:
|
1320
|
-
logger.error(
|
1359
|
+
logger.error(
|
1360
|
+
f"Failed to create plan for task '{task}': {error}"
|
1361
|
+
)
|
1321
1362
|
raise error
|
1322
1363
|
|
1323
1364
|
async def run_concurrent(self, task: str, *args, **kwargs):
|
@@ -1440,7 +1481,6 @@ class Agent:
|
|
1440
1481
|
raise AgentInitializationError(
|
1441
1482
|
f"Max tokens is set to {self.max_tokens}, but the model '{self.model_name}' only supports {get_max_tokens(self.model_name)} tokens. Please set max tokens to {get_max_tokens(self.model_name)} or less."
|
1442
1483
|
)
|
1443
|
-
|
1444
1484
|
|
1445
1485
|
if self.model_name not in model_list:
|
1446
1486
|
logger.warning(
|
@@ -2664,7 +2704,7 @@ class Agent:
|
|
2664
2704
|
) # Convert other dicts to string
|
2665
2705
|
|
2666
2706
|
elif isinstance(response, BaseModel):
|
2667
|
-
|
2707
|
+
response = response.model_dump()
|
2668
2708
|
|
2669
2709
|
# Handle List[BaseModel] responses
|
2670
2710
|
elif (
|
@@ -2674,14 +2714,9 @@ class Agent:
|
|
2674
2714
|
):
|
2675
2715
|
return [item.model_dump() for item in response]
|
2676
2716
|
|
2677
|
-
|
2678
|
-
out = format_data_structure(response)
|
2679
|
-
else:
|
2680
|
-
out = str(response)
|
2717
|
+
return response
|
2681
2718
|
|
2682
|
-
|
2683
|
-
|
2684
|
-
except Exception as e:
|
2719
|
+
except AgentChatCompletionResponse as e:
|
2685
2720
|
logger.error(f"Error parsing LLM output: {e}")
|
2686
2721
|
raise ValueError(
|
2687
2722
|
f"Failed to parse LLM output: {type(response)}"
|
@@ -2738,6 +2773,15 @@ class Agent:
|
|
2738
2773
|
connection=self.mcp_config,
|
2739
2774
|
)
|
2740
2775
|
)
|
2776
|
+
elif exists(self.mcp_urls):
|
2777
|
+
tool_response = execute_multiple_tools_on_multiple_mcp_servers_sync(
|
2778
|
+
responses=response,
|
2779
|
+
urls=self.mcp_urls,
|
2780
|
+
output_type="json",
|
2781
|
+
)
|
2782
|
+
# tool_response = format_data_structure(tool_response)
|
2783
|
+
|
2784
|
+
print(f"Multiple MCP Tool Response: {tool_response}")
|
2741
2785
|
else:
|
2742
2786
|
raise AgentMCPConnectionError(
|
2743
2787
|
"mcp_url must be either a string URL or MCPConnection object"
|
@@ -2745,7 +2789,7 @@ class Agent:
|
|
2745
2789
|
|
2746
2790
|
# Get the text content from the tool response
|
2747
2791
|
# execute_tool_call_simple returns a string directly, not an object with content attribute
|
2748
|
-
text_content = f"MCP Tool Response: \n{json.dumps(tool_response, indent=2)}"
|
2792
|
+
text_content = f"MCP Tool Response: \n\n {json.dumps(tool_response, indent=2)}"
|
2749
2793
|
|
2750
2794
|
if self.no_print is False:
|
2751
2795
|
formatter.print_panel(
|
@@ -2818,7 +2862,7 @@ class Agent:
|
|
2818
2862
|
# Now run the LLM again without tools - create a temporary LLM instance
|
2819
2863
|
# instead of modifying the cached one
|
2820
2864
|
# Create a temporary LLM instance without tools for the follow-up call
|
2821
|
-
if self.
|
2865
|
+
if self.tool_call_summary is True:
|
2822
2866
|
temp_llm = self.temp_llm_instance_for_tool_summary()
|
2823
2867
|
|
2824
2868
|
tool_response = temp_llm.run(
|
swarms/structs/conversation.py
CHANGED
@@ -1326,6 +1326,12 @@ class Conversation(BaseStructure):
|
|
1326
1326
|
self.conversation_history[-1]["content"],
|
1327
1327
|
)
|
1328
1328
|
|
1329
|
+
def return_list_final(self):
|
1330
|
+
"""Return the final message as a list."""
|
1331
|
+
return [
|
1332
|
+
self.conversation_history[-1]["content"],
|
1333
|
+
]
|
1334
|
+
|
1329
1335
|
@classmethod
|
1330
1336
|
def list_conversations(
|
1331
1337
|
cls, conversations_dir: Optional[str] = None
|
swarms/structs/csv_to_agent.py
CHANGED
@@ -104,9 +104,7 @@ class AgentValidator:
|
|
104
104
|
model_name in model["model_name"]
|
105
105
|
for model in model_list
|
106
106
|
):
|
107
|
-
|
108
|
-
model["model_name"] for model in model_list
|
109
|
-
]
|
107
|
+
[model["model_name"] for model in model_list]
|
110
108
|
raise AgentValidationError(
|
111
109
|
"Invalid model name. Must be one of the supported litellm models",
|
112
110
|
"model_name",
|
swarms/telemetry/__init__.py
CHANGED
@@ -4,11 +4,9 @@ from swarms.telemetry.main import (
|
|
4
4
|
get_cpu_info,
|
5
5
|
get_machine_id,
|
6
6
|
get_os_version,
|
7
|
-
get_package_mismatches,
|
8
7
|
get_pip_version,
|
9
8
|
get_python_version,
|
10
9
|
get_ram_info,
|
11
|
-
get_swarms_verison,
|
12
10
|
get_system_info,
|
13
11
|
get_user_device_data,
|
14
12
|
system_info,
|
@@ -21,11 +19,9 @@ __all__ = [
|
|
21
19
|
"generate_unique_identifier",
|
22
20
|
"get_python_version",
|
23
21
|
"get_pip_version",
|
24
|
-
"get_swarms_verison",
|
25
22
|
"get_os_version",
|
26
23
|
"get_cpu_info",
|
27
24
|
"get_ram_info",
|
28
|
-
"get_package_mismatches",
|
29
25
|
"system_info",
|
30
26
|
"get_user_device_data",
|
31
27
|
]
|
swarms/telemetry/main.py
CHANGED
@@ -1,24 +1,16 @@
|
|
1
|
-
import
|
2
|
-
|
3
|
-
|
1
|
+
import os
|
4
2
|
import datetime
|
5
3
|
import hashlib
|
6
4
|
import platform
|
7
5
|
import socket
|
8
6
|
import subprocess
|
9
7
|
import uuid
|
10
|
-
from concurrent.futures import ThreadPoolExecutor
|
11
|
-
from functools import lru_cache
|
12
|
-
from threading import Lock
|
13
8
|
from typing import Dict
|
14
9
|
|
15
|
-
import aiohttp
|
16
10
|
import pkg_resources
|
17
11
|
import psutil
|
12
|
+
import requests
|
18
13
|
import toml
|
19
|
-
from requests import Session
|
20
|
-
from requests.adapters import HTTPAdapter
|
21
|
-
from urllib3.util.retry import Retry
|
22
14
|
|
23
15
|
|
24
16
|
# Helper functions
|
@@ -263,134 +255,44 @@ def capture_system_data() -> Dict[str, str]:
|
|
263
255
|
print(f"Failed to capture system data: {e}")
|
264
256
|
|
265
257
|
|
266
|
-
|
267
|
-
|
268
|
-
_session_lock = Lock()
|
269
|
-
_executor = ThreadPoolExecutor(max_workers=10)
|
270
|
-
_aiohttp_session = None
|
271
|
-
|
272
|
-
|
273
|
-
def get_session() -> Session:
|
274
|
-
"""Thread-safe session getter with optimized connection pooling"""
|
275
|
-
global _session
|
276
|
-
if _session is None:
|
277
|
-
with _session_lock:
|
278
|
-
if _session is None: # Double-check pattern
|
279
|
-
_session = Session()
|
280
|
-
adapter = HTTPAdapter(
|
281
|
-
pool_connections=1000, # Increased pool size
|
282
|
-
pool_maxsize=1000, # Increased max size
|
283
|
-
max_retries=Retry(
|
284
|
-
total=3,
|
285
|
-
backoff_factor=0.1,
|
286
|
-
status_forcelist=[500, 502, 503, 504],
|
287
|
-
),
|
288
|
-
pool_block=False, # Non-blocking pool
|
289
|
-
)
|
290
|
-
_session.mount("http://", adapter)
|
291
|
-
_session.mount("https://", adapter)
|
292
|
-
_session.headers.update(
|
293
|
-
{
|
294
|
-
"Content-Type": "application/json",
|
295
|
-
"Authorization": "Bearer sk-33979fd9a4e8e6b670090e4900a33dbe7452a15ccc705745f4eca2a70c88ea24",
|
296
|
-
"Connection": "keep-alive", # Enable keep-alive
|
297
|
-
}
|
298
|
-
)
|
299
|
-
return _session
|
300
|
-
|
301
|
-
|
302
|
-
@lru_cache(maxsize=2048, typed=True)
|
303
|
-
def get_user_device_data_cached():
|
304
|
-
"""Cached version with increased cache size"""
|
305
|
-
return get_user_device_data()
|
306
|
-
|
307
|
-
|
308
|
-
async def get_aiohttp_session():
|
309
|
-
"""Get or create aiohttp session for async requests"""
|
310
|
-
global _aiohttp_session
|
311
|
-
if _aiohttp_session is None or _aiohttp_session.closed:
|
312
|
-
timeout = aiohttp.ClientTimeout(total=10)
|
313
|
-
connector = aiohttp.TCPConnector(
|
314
|
-
limit=1000, # Connection limit
|
315
|
-
ttl_dns_cache=300, # DNS cache TTL
|
316
|
-
use_dns_cache=True, # Enable DNS caching
|
317
|
-
keepalive_timeout=60, # Keep-alive timeout
|
318
|
-
)
|
319
|
-
_aiohttp_session = aiohttp.ClientSession(
|
320
|
-
timeout=timeout,
|
321
|
-
connector=connector,
|
322
|
-
headers={
|
323
|
-
"Content-Type": "application/json",
|
324
|
-
"Authorization": "Bearer sk-33979fd9a4e8e6b670090e4900a33dbe7452a15ccc705745f4eca2a70c88ea24",
|
325
|
-
},
|
326
|
-
)
|
327
|
-
return _aiohttp_session
|
328
|
-
|
329
|
-
|
330
|
-
async def log_agent_data_async(data_dict: dict):
|
331
|
-
"""Asynchronous version of log_agent_data"""
|
258
|
+
def _log_agent_data(data_dict: dict):
|
259
|
+
"""Simple function to log agent data using requests library"""
|
332
260
|
if not data_dict:
|
333
|
-
return
|
261
|
+
return
|
334
262
|
|
335
263
|
url = "https://swarms.world/api/get-agents/log-agents"
|
336
264
|
payload = {
|
337
265
|
"data": data_dict,
|
338
|
-
"system_data":
|
266
|
+
"system_data": get_user_device_data(),
|
339
267
|
"timestamp": datetime.datetime.now(
|
340
268
|
datetime.timezone.utc
|
341
269
|
).isoformat(),
|
342
270
|
}
|
343
271
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
return await response.json()
|
349
|
-
except Exception:
|
350
|
-
return None
|
351
|
-
|
352
|
-
|
353
|
-
def _log_agent_data(data_dict: dict):
|
354
|
-
"""
|
355
|
-
Enhanced log_agent_data with both sync and async capabilities
|
356
|
-
"""
|
357
|
-
if not data_dict:
|
358
|
-
return None
|
359
|
-
|
360
|
-
# If running in an event loop, use async version
|
361
|
-
try:
|
362
|
-
loop = asyncio.get_event_loop()
|
363
|
-
if loop.is_running():
|
364
|
-
return asyncio.create_task(
|
365
|
-
log_agent_data_async(data_dict)
|
366
|
-
)
|
367
|
-
except RuntimeError:
|
368
|
-
pass
|
272
|
+
key = (
|
273
|
+
os.getenv("SWARMS_API_KEY")
|
274
|
+
or "Bearer sk-33979fd9a4e8e6b670090e4900a33dbe7452a15ccc705745f4eca2a70c88ea24"
|
275
|
+
)
|
369
276
|
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
"data": data_dict,
|
374
|
-
"system_data": get_user_device_data_cached(),
|
375
|
-
"timestamp": datetime.datetime.now(
|
376
|
-
datetime.timezone.utc
|
377
|
-
).isoformat(),
|
277
|
+
headers = {
|
278
|
+
"Content-Type": "application/json",
|
279
|
+
"Authorization": key,
|
378
280
|
}
|
379
281
|
|
380
282
|
try:
|
381
|
-
|
382
|
-
|
383
|
-
url,
|
384
|
-
json=payload,
|
385
|
-
timeout=10,
|
386
|
-
stream=False, # Disable streaming for faster response
|
283
|
+
response = requests.post(
|
284
|
+
url, json=payload, headers=headers, timeout=10
|
387
285
|
)
|
388
|
-
if response.
|
389
|
-
return
|
286
|
+
if response.status_code == 200:
|
287
|
+
return
|
390
288
|
except Exception:
|
391
|
-
return
|
289
|
+
return
|
290
|
+
|
291
|
+
return
|
392
292
|
|
393
293
|
|
394
294
|
def log_agent_data(data_dict: dict):
|
395
|
-
|
396
|
-
|
295
|
+
try:
|
296
|
+
_log_agent_data(data_dict)
|
297
|
+
except Exception:
|
298
|
+
pass
|
swarms/tools/__init__.py
CHANGED
@@ -33,6 +33,11 @@ from swarms.tools.mcp_client_call import (
|
|
33
33
|
get_tools_for_multiple_mcp_servers,
|
34
34
|
get_mcp_tools_sync,
|
35
35
|
aget_mcp_tools,
|
36
|
+
execute_multiple_tools_on_multiple_mcp_servers,
|
37
|
+
execute_multiple_tools_on_multiple_mcp_servers_sync,
|
38
|
+
_create_server_tool_mapping,
|
39
|
+
_create_server_tool_mapping_async,
|
40
|
+
_execute_tool_on_server,
|
36
41
|
)
|
37
42
|
|
38
43
|
|
@@ -62,4 +67,9 @@ __all__ = [
|
|
62
67
|
"get_tools_for_multiple_mcp_servers",
|
63
68
|
"get_mcp_tools_sync",
|
64
69
|
"aget_mcp_tools",
|
70
|
+
"execute_multiple_tools_on_multiple_mcp_servers",
|
71
|
+
"execute_multiple_tools_on_multiple_mcp_servers_sync",
|
72
|
+
"_create_server_tool_mapping",
|
73
|
+
"_create_server_tool_mapping_async",
|
74
|
+
"_execute_tool_on_server",
|
65
75
|
]
|
swarms/tools/base_tool.py
CHANGED
@@ -2256,14 +2256,18 @@ class BaseTool(BaseModel):
|
|
2256
2256
|
try:
|
2257
2257
|
api_response = json.loads(api_response)
|
2258
2258
|
except json.JSONDecodeError as e:
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2259
|
+
self._log_if_verbose(
|
2260
|
+
"error",
|
2261
|
+
f"Failed to parse JSON from API response: {e}. Response: '{api_response[:100]}...'"
|
2262
|
+
)
|
2263
|
+
return []
|
2262
2264
|
|
2263
2265
|
if not isinstance(api_response, dict):
|
2264
|
-
|
2265
|
-
"
|
2266
|
+
self._log_if_verbose(
|
2267
|
+
"warning",
|
2268
|
+
f"API response is not a dictionary (type: {type(api_response)}), returning empty list"
|
2266
2269
|
)
|
2270
|
+
return []
|
2267
2271
|
|
2268
2272
|
# Extract function calls from dictionary response
|
2269
2273
|
function_calls = (
|
swarms/tools/mcp_client_call.py
CHANGED
@@ -505,3 +505,511 @@ async def execute_tool_call_simple(
|
|
505
505
|
*args,
|
506
506
|
**kwargs,
|
507
507
|
)
|
508
|
+
|
509
|
+
|
510
|
+
def _create_server_tool_mapping(
|
511
|
+
urls: List[str],
|
512
|
+
connections: List[MCPConnection] = None,
|
513
|
+
format: str = "openai",
|
514
|
+
) -> Dict[str, Dict[str, Any]]:
|
515
|
+
"""
|
516
|
+
Create a mapping of function names to server information for all MCP servers.
|
517
|
+
|
518
|
+
Args:
|
519
|
+
urls: List of server URLs
|
520
|
+
connections: Optional list of MCPConnection objects
|
521
|
+
format: Format to fetch tools in
|
522
|
+
|
523
|
+
Returns:
|
524
|
+
Dict mapping function names to server info (url, connection, tool)
|
525
|
+
"""
|
526
|
+
server_tool_mapping = {}
|
527
|
+
|
528
|
+
for i, url in enumerate(urls):
|
529
|
+
connection = (
|
530
|
+
connections[i]
|
531
|
+
if connections and i < len(connections)
|
532
|
+
else None
|
533
|
+
)
|
534
|
+
|
535
|
+
try:
|
536
|
+
# Get tools for this server
|
537
|
+
tools = get_mcp_tools_sync(
|
538
|
+
server_path=url,
|
539
|
+
connection=connection,
|
540
|
+
format=format,
|
541
|
+
)
|
542
|
+
|
543
|
+
# Create mapping for each tool
|
544
|
+
for tool in tools:
|
545
|
+
if isinstance(tool, dict) and "function" in tool:
|
546
|
+
function_name = tool["function"]["name"]
|
547
|
+
server_tool_mapping[function_name] = {
|
548
|
+
"url": url,
|
549
|
+
"connection": connection,
|
550
|
+
"tool": tool,
|
551
|
+
"server_index": i,
|
552
|
+
}
|
553
|
+
elif hasattr(tool, "name"):
|
554
|
+
# Handle MCPTool objects
|
555
|
+
server_tool_mapping[tool.name] = {
|
556
|
+
"url": url,
|
557
|
+
"connection": connection,
|
558
|
+
"tool": tool,
|
559
|
+
"server_index": i,
|
560
|
+
}
|
561
|
+
|
562
|
+
except Exception as e:
|
563
|
+
logger.warning(
|
564
|
+
f"Failed to fetch tools from server {url}: {str(e)}"
|
565
|
+
)
|
566
|
+
continue
|
567
|
+
|
568
|
+
return server_tool_mapping
|
569
|
+
|
570
|
+
|
571
|
+
async def _create_server_tool_mapping_async(
|
572
|
+
urls: List[str],
|
573
|
+
connections: List[MCPConnection] = None,
|
574
|
+
format: str = "openai",
|
575
|
+
) -> Dict[str, Dict[str, Any]]:
|
576
|
+
"""
|
577
|
+
Async version: Create a mapping of function names to server information for all MCP servers.
|
578
|
+
|
579
|
+
Args:
|
580
|
+
urls: List of server URLs
|
581
|
+
connections: Optional list of MCPConnection objects
|
582
|
+
format: Format to fetch tools in
|
583
|
+
|
584
|
+
Returns:
|
585
|
+
Dict mapping function names to server info (url, connection, tool)
|
586
|
+
"""
|
587
|
+
server_tool_mapping = {}
|
588
|
+
|
589
|
+
for i, url in enumerate(urls):
|
590
|
+
connection = (
|
591
|
+
connections[i]
|
592
|
+
if connections and i < len(connections)
|
593
|
+
else None
|
594
|
+
)
|
595
|
+
|
596
|
+
try:
|
597
|
+
# Get tools for this server using async function
|
598
|
+
tools = await aget_mcp_tools(
|
599
|
+
server_path=url,
|
600
|
+
connection=connection,
|
601
|
+
format=format,
|
602
|
+
)
|
603
|
+
|
604
|
+
# Create mapping for each tool
|
605
|
+
for tool in tools:
|
606
|
+
if isinstance(tool, dict) and "function" in tool:
|
607
|
+
function_name = tool["function"]["name"]
|
608
|
+
server_tool_mapping[function_name] = {
|
609
|
+
"url": url,
|
610
|
+
"connection": connection,
|
611
|
+
"tool": tool,
|
612
|
+
"server_index": i,
|
613
|
+
}
|
614
|
+
elif hasattr(tool, "name"):
|
615
|
+
# Handle MCPTool objects
|
616
|
+
server_tool_mapping[tool.name] = {
|
617
|
+
"url": url,
|
618
|
+
"connection": connection,
|
619
|
+
"tool": tool,
|
620
|
+
"server_index": i,
|
621
|
+
}
|
622
|
+
|
623
|
+
except Exception as e:
|
624
|
+
logger.warning(
|
625
|
+
f"Failed to fetch tools from server {url}: {str(e)}"
|
626
|
+
)
|
627
|
+
continue
|
628
|
+
|
629
|
+
return server_tool_mapping
|
630
|
+
|
631
|
+
|
632
|
+
async def _execute_tool_on_server(
|
633
|
+
tool_call: Dict[str, Any],
|
634
|
+
server_info: Dict[str, Any],
|
635
|
+
output_type: Literal["json", "dict", "str", "formatted"] = "str",
|
636
|
+
) -> Dict[str, Any]:
|
637
|
+
"""
|
638
|
+
Execute a single tool call on a specific server.
|
639
|
+
|
640
|
+
Args:
|
641
|
+
tool_call: The tool call to execute
|
642
|
+
server_info: Server information from the mapping
|
643
|
+
output_type: Output format type
|
644
|
+
|
645
|
+
Returns:
|
646
|
+
Execution result with server metadata
|
647
|
+
"""
|
648
|
+
try:
|
649
|
+
result = await _execute_tool_call_simple(
|
650
|
+
response=tool_call,
|
651
|
+
server_path=server_info["url"],
|
652
|
+
connection=server_info["connection"],
|
653
|
+
output_type=output_type,
|
654
|
+
)
|
655
|
+
|
656
|
+
return {
|
657
|
+
"server_url": server_info["url"],
|
658
|
+
"server_index": server_info["server_index"],
|
659
|
+
"function_name": tool_call.get("function", {}).get(
|
660
|
+
"name", "unknown"
|
661
|
+
),
|
662
|
+
"result": result,
|
663
|
+
"status": "success",
|
664
|
+
}
|
665
|
+
|
666
|
+
except Exception as e:
|
667
|
+
logger.error(
|
668
|
+
f"Failed to execute tool on server {server_info['url']}: {str(e)}"
|
669
|
+
)
|
670
|
+
return {
|
671
|
+
"server_url": server_info["url"],
|
672
|
+
"server_index": server_info["server_index"],
|
673
|
+
"function_name": tool_call.get("function", {}).get(
|
674
|
+
"name", "unknown"
|
675
|
+
),
|
676
|
+
"result": None,
|
677
|
+
"error": str(e),
|
678
|
+
"status": "error",
|
679
|
+
}
|
680
|
+
|
681
|
+
|
682
|
+
async def execute_multiple_tools_on_multiple_mcp_servers(
|
683
|
+
responses: List[Dict[str, Any]],
|
684
|
+
urls: List[str],
|
685
|
+
connections: List[MCPConnection] = None,
|
686
|
+
output_type: Literal["json", "dict", "str", "formatted"] = "str",
|
687
|
+
max_concurrent: Optional[int] = None,
|
688
|
+
*args,
|
689
|
+
**kwargs,
|
690
|
+
) -> List[Dict[str, Any]]:
|
691
|
+
"""
|
692
|
+
Execute multiple tool calls across multiple MCP servers.
|
693
|
+
|
694
|
+
This function creates a mapping of function names to servers, then for each response
|
695
|
+
that contains tool calls, it finds the appropriate server for each function and
|
696
|
+
executes the calls concurrently.
|
697
|
+
|
698
|
+
Args:
|
699
|
+
responses: List of responses containing tool calls (OpenAI format)
|
700
|
+
urls: List of MCP server URLs
|
701
|
+
connections: Optional list of MCPConnection objects corresponding to each URL
|
702
|
+
output_type: Output format type for results
|
703
|
+
max_concurrent: Maximum number of concurrent executions (default: len(responses))
|
704
|
+
|
705
|
+
Returns:
|
706
|
+
List of execution results with server metadata
|
707
|
+
|
708
|
+
Example:
|
709
|
+
# Example responses format:
|
710
|
+
responses = [
|
711
|
+
{
|
712
|
+
"function": {
|
713
|
+
"name": "search_web",
|
714
|
+
"arguments": {"query": "python programming"}
|
715
|
+
}
|
716
|
+
},
|
717
|
+
{
|
718
|
+
"function": {
|
719
|
+
"name": "search_database",
|
720
|
+
"arguments": {"table": "users", "id": 123}
|
721
|
+
}
|
722
|
+
}
|
723
|
+
]
|
724
|
+
|
725
|
+
urls = ["http://server1:8000", "http://server2:8000"]
|
726
|
+
|
727
|
+
results = await execute_multiple_tools_on_multiple_mcp_servers(
|
728
|
+
responses=responses,
|
729
|
+
urls=urls
|
730
|
+
)
|
731
|
+
"""
|
732
|
+
if not responses:
|
733
|
+
logger.warning("No responses provided for execution")
|
734
|
+
return []
|
735
|
+
|
736
|
+
if not urls:
|
737
|
+
raise MCPValidationError("No server URLs provided")
|
738
|
+
|
739
|
+
# Create mapping of function names to servers using async version
|
740
|
+
logger.info(f"Creating tool mapping for {len(urls)} servers")
|
741
|
+
server_tool_mapping = await _create_server_tool_mapping_async(
|
742
|
+
urls=urls, connections=connections, format="openai"
|
743
|
+
)
|
744
|
+
|
745
|
+
if not server_tool_mapping:
|
746
|
+
raise MCPExecutionError(
|
747
|
+
"No tools found on any of the provided servers"
|
748
|
+
)
|
749
|
+
|
750
|
+
logger.info(
|
751
|
+
f"Found {len(server_tool_mapping)} unique functions across all servers"
|
752
|
+
)
|
753
|
+
|
754
|
+
# Extract all tool calls from responses
|
755
|
+
all_tool_calls = []
|
756
|
+
logger.info(
|
757
|
+
f"Processing {len(responses)} responses for tool call extraction"
|
758
|
+
)
|
759
|
+
|
760
|
+
# Check if responses are individual characters that need to be reconstructed
|
761
|
+
if len(responses) > 10 and all(
|
762
|
+
isinstance(r, str) and len(r) == 1 for r in responses
|
763
|
+
):
|
764
|
+
logger.info(
|
765
|
+
"Detected character-by-character response, reconstructing JSON string"
|
766
|
+
)
|
767
|
+
try:
|
768
|
+
reconstructed_response = "".join(responses)
|
769
|
+
logger.info(
|
770
|
+
f"Reconstructed response length: {len(reconstructed_response)}"
|
771
|
+
)
|
772
|
+
logger.debug(
|
773
|
+
f"Reconstructed response: {reconstructed_response}"
|
774
|
+
)
|
775
|
+
|
776
|
+
# Try to parse the reconstructed response to validate it
|
777
|
+
try:
|
778
|
+
json.loads(reconstructed_response)
|
779
|
+
logger.info(
|
780
|
+
"Successfully validated reconstructed JSON response"
|
781
|
+
)
|
782
|
+
except json.JSONDecodeError as e:
|
783
|
+
logger.warning(
|
784
|
+
f"Reconstructed response is not valid JSON: {str(e)}"
|
785
|
+
)
|
786
|
+
logger.debug(
|
787
|
+
f"First 100 chars: {reconstructed_response[:100]}"
|
788
|
+
)
|
789
|
+
logger.debug(
|
790
|
+
f"Last 100 chars: {reconstructed_response[-100:]}"
|
791
|
+
)
|
792
|
+
|
793
|
+
responses = [reconstructed_response]
|
794
|
+
except Exception as e:
|
795
|
+
logger.warning(
|
796
|
+
f"Failed to reconstruct response from characters: {str(e)}"
|
797
|
+
)
|
798
|
+
|
799
|
+
for i, response in enumerate(responses):
|
800
|
+
logger.debug(
|
801
|
+
f"Processing response {i}: {type(response)} - {response}"
|
802
|
+
)
|
803
|
+
|
804
|
+
# Handle JSON string responses
|
805
|
+
if isinstance(response, str):
|
806
|
+
try:
|
807
|
+
response = json.loads(response)
|
808
|
+
logger.debug(
|
809
|
+
f"Parsed JSON string response {i}: {response}"
|
810
|
+
)
|
811
|
+
except json.JSONDecodeError:
|
812
|
+
logger.warning(
|
813
|
+
f"Failed to parse JSON response at index {i}: {response}"
|
814
|
+
)
|
815
|
+
continue
|
816
|
+
|
817
|
+
if isinstance(response, dict):
|
818
|
+
# Single tool call
|
819
|
+
if "function" in response:
|
820
|
+
logger.debug(
|
821
|
+
f"Found single tool call in response {i}: {response['function']}"
|
822
|
+
)
|
823
|
+
# Parse arguments if they're a JSON string
|
824
|
+
if isinstance(
|
825
|
+
response["function"].get("arguments"), str
|
826
|
+
):
|
827
|
+
try:
|
828
|
+
response["function"]["arguments"] = (
|
829
|
+
json.loads(
|
830
|
+
response["function"]["arguments"]
|
831
|
+
)
|
832
|
+
)
|
833
|
+
logger.debug(
|
834
|
+
f"Parsed function arguments: {response['function']['arguments']}"
|
835
|
+
)
|
836
|
+
except json.JSONDecodeError:
|
837
|
+
logger.warning(
|
838
|
+
f"Failed to parse function arguments: {response['function']['arguments']}"
|
839
|
+
)
|
840
|
+
|
841
|
+
all_tool_calls.append((i, response))
|
842
|
+
# Multiple tool calls
|
843
|
+
elif "tool_calls" in response:
|
844
|
+
logger.debug(
|
845
|
+
f"Found multiple tool calls in response {i}: {len(response['tool_calls'])} calls"
|
846
|
+
)
|
847
|
+
for tool_call in response["tool_calls"]:
|
848
|
+
# Parse arguments if they're a JSON string
|
849
|
+
if isinstance(
|
850
|
+
tool_call.get("function", {}).get(
|
851
|
+
"arguments"
|
852
|
+
),
|
853
|
+
str,
|
854
|
+
):
|
855
|
+
try:
|
856
|
+
tool_call["function"]["arguments"] = (
|
857
|
+
json.loads(
|
858
|
+
tool_call["function"]["arguments"]
|
859
|
+
)
|
860
|
+
)
|
861
|
+
logger.debug(
|
862
|
+
f"Parsed tool call arguments: {tool_call['function']['arguments']}"
|
863
|
+
)
|
864
|
+
except json.JSONDecodeError:
|
865
|
+
logger.warning(
|
866
|
+
f"Failed to parse tool call arguments: {tool_call['function']['arguments']}"
|
867
|
+
)
|
868
|
+
|
869
|
+
all_tool_calls.append((i, tool_call))
|
870
|
+
# Direct tool call
|
871
|
+
elif "name" in response and "arguments" in response:
|
872
|
+
logger.debug(
|
873
|
+
f"Found direct tool call in response {i}: {response}"
|
874
|
+
)
|
875
|
+
# Parse arguments if they're a JSON string
|
876
|
+
if isinstance(response.get("arguments"), str):
|
877
|
+
try:
|
878
|
+
response["arguments"] = json.loads(
|
879
|
+
response["arguments"]
|
880
|
+
)
|
881
|
+
logger.debug(
|
882
|
+
f"Parsed direct tool call arguments: {response['arguments']}"
|
883
|
+
)
|
884
|
+
except json.JSONDecodeError:
|
885
|
+
logger.warning(
|
886
|
+
f"Failed to parse direct tool call arguments: {response['arguments']}"
|
887
|
+
)
|
888
|
+
|
889
|
+
all_tool_calls.append((i, {"function": response}))
|
890
|
+
else:
|
891
|
+
logger.debug(
|
892
|
+
f"Response {i} is a dict but doesn't match expected tool call formats: {list(response.keys())}"
|
893
|
+
)
|
894
|
+
else:
|
895
|
+
logger.warning(
|
896
|
+
f"Unsupported response type at index {i}: {type(response)}"
|
897
|
+
)
|
898
|
+
continue
|
899
|
+
|
900
|
+
if not all_tool_calls:
|
901
|
+
logger.warning("No tool calls found in responses")
|
902
|
+
return []
|
903
|
+
|
904
|
+
logger.info(f"Found {len(all_tool_calls)} tool calls to execute")
|
905
|
+
|
906
|
+
# Execute tool calls concurrently
|
907
|
+
max_concurrent = max_concurrent or len(all_tool_calls)
|
908
|
+
semaphore = asyncio.Semaphore(max_concurrent)
|
909
|
+
|
910
|
+
async def execute_with_semaphore(tool_call_info):
|
911
|
+
async with semaphore:
|
912
|
+
response_index, tool_call = tool_call_info
|
913
|
+
function_name = tool_call.get("function", {}).get(
|
914
|
+
"name", "unknown"
|
915
|
+
)
|
916
|
+
|
917
|
+
if function_name not in server_tool_mapping:
|
918
|
+
logger.warning(
|
919
|
+
f"Function '{function_name}' not found on any server"
|
920
|
+
)
|
921
|
+
return {
|
922
|
+
"response_index": response_index,
|
923
|
+
"function_name": function_name,
|
924
|
+
"result": None,
|
925
|
+
"error": f"Function '{function_name}' not available on any server",
|
926
|
+
"status": "not_found",
|
927
|
+
}
|
928
|
+
|
929
|
+
server_info = server_tool_mapping[function_name]
|
930
|
+
result = await _execute_tool_on_server(
|
931
|
+
tool_call=tool_call,
|
932
|
+
server_info=server_info,
|
933
|
+
output_type=output_type,
|
934
|
+
)
|
935
|
+
result["response_index"] = response_index
|
936
|
+
return result
|
937
|
+
|
938
|
+
# Execute all tool calls concurrently
|
939
|
+
tasks = [
|
940
|
+
execute_with_semaphore(tool_call_info)
|
941
|
+
for tool_call_info in all_tool_calls
|
942
|
+
]
|
943
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
944
|
+
|
945
|
+
# Process results and handle exceptions
|
946
|
+
processed_results = []
|
947
|
+
for i, result in enumerate(results):
|
948
|
+
if isinstance(result, Exception):
|
949
|
+
logger.error(
|
950
|
+
f"Task {i} failed with exception: {str(result)}"
|
951
|
+
)
|
952
|
+
processed_results.append(
|
953
|
+
{
|
954
|
+
"response_index": (
|
955
|
+
all_tool_calls[i][0]
|
956
|
+
if i < len(all_tool_calls)
|
957
|
+
else -1
|
958
|
+
),
|
959
|
+
"function_name": "unknown",
|
960
|
+
"result": None,
|
961
|
+
"error": str(result),
|
962
|
+
"status": "exception",
|
963
|
+
}
|
964
|
+
)
|
965
|
+
else:
|
966
|
+
processed_results.append(result)
|
967
|
+
|
968
|
+
logger.info(
|
969
|
+
f"Completed execution of {len(processed_results)} tool calls"
|
970
|
+
)
|
971
|
+
return processed_results
|
972
|
+
|
973
|
+
|
974
|
+
def execute_multiple_tools_on_multiple_mcp_servers_sync(
|
975
|
+
responses: List[Dict[str, Any]],
|
976
|
+
urls: List[str],
|
977
|
+
connections: List[MCPConnection] = None,
|
978
|
+
output_type: Literal["json", "dict", "str", "formatted"] = "str",
|
979
|
+
max_concurrent: Optional[int] = None,
|
980
|
+
*args,
|
981
|
+
**kwargs,
|
982
|
+
) -> List[Dict[str, Any]]:
|
983
|
+
"""
|
984
|
+
Synchronous version of execute_multiple_tools_on_multiple_mcp_servers.
|
985
|
+
|
986
|
+
Args:
|
987
|
+
responses: List of responses containing tool calls (OpenAI format)
|
988
|
+
urls: List of MCP server URLs
|
989
|
+
connections: Optional list of MCPConnection objects corresponding to each URL
|
990
|
+
output_type: Output format type for results
|
991
|
+
max_concurrent: Maximum number of concurrent executions
|
992
|
+
|
993
|
+
Returns:
|
994
|
+
List of execution results with server metadata
|
995
|
+
"""
|
996
|
+
with get_or_create_event_loop() as loop:
|
997
|
+
try:
|
998
|
+
return loop.run_until_complete(
|
999
|
+
execute_multiple_tools_on_multiple_mcp_servers(
|
1000
|
+
responses=responses,
|
1001
|
+
urls=urls,
|
1002
|
+
connections=connections,
|
1003
|
+
output_type=output_type,
|
1004
|
+
max_concurrent=max_concurrent,
|
1005
|
+
*args,
|
1006
|
+
**kwargs,
|
1007
|
+
)
|
1008
|
+
)
|
1009
|
+
except Exception as e:
|
1010
|
+
logger.error(
|
1011
|
+
f"Error in execute_multiple_tools_on_multiple_mcp_servers_sync: {str(e)}"
|
1012
|
+
)
|
1013
|
+
raise MCPExecutionError(
|
1014
|
+
f"Failed to execute multiple tools sync: {str(e)}"
|
1015
|
+
)
|
@@ -492,7 +492,6 @@ def convert_multiple_functions_to_openai_function_schema(
|
|
492
492
|
# ]
|
493
493
|
# Use 40% of cpu cores
|
494
494
|
max_workers = int(os.cpu_count() * 0.8)
|
495
|
-
print(f"max_workers: {max_workers}")
|
496
495
|
|
497
496
|
with concurrent.futures.ThreadPoolExecutor(
|
498
497
|
max_workers=max_workers
|
@@ -8,9 +8,10 @@ import subprocess
|
|
8
8
|
import sys
|
9
9
|
from typing import Literal, Optional, Union
|
10
10
|
from swarms.utils.loguru_logger import initialize_logger
|
11
|
-
import pkg_resources
|
12
11
|
|
13
12
|
|
13
|
+
from importlib.metadata import distribution, PackageNotFoundError
|
14
|
+
|
14
15
|
logger = initialize_logger("autocheckpackages")
|
15
16
|
|
16
17
|
|
@@ -39,13 +40,13 @@ def check_and_install_package(
|
|
39
40
|
# Check if package exists
|
40
41
|
if package_manager == "pip":
|
41
42
|
try:
|
42
|
-
|
43
|
+
distribution(package_name)
|
43
44
|
if not upgrade:
|
44
45
|
logger.info(
|
45
46
|
f"Package {package_name} is already installed"
|
46
47
|
)
|
47
48
|
return True
|
48
|
-
except
|
49
|
+
except PackageNotFoundError:
|
49
50
|
pass
|
50
51
|
|
51
52
|
# Construct installation command
|
@@ -23,6 +23,8 @@ def history_output_formatter(
|
|
23
23
|
return yaml.safe_dump(conversation.to_dict(), sort_keys=False)
|
24
24
|
elif type == "dict-all-except-first":
|
25
25
|
return conversation.return_all_except_first()
|
26
|
+
elif type == "list-final":
|
27
|
+
return conversation.return_list_final()
|
26
28
|
elif type == "str-all-except-first":
|
27
29
|
return conversation.return_all_except_first_string()
|
28
30
|
elif type == "dict-final":
|
swarms/utils/output_types.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: swarms
|
3
|
-
Version: 7.8.
|
3
|
+
Version: 7.8.9
|
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
|
@@ -105,7 +105,7 @@ swarms/schemas/mcp_schemas.py,sha256=XZJ4HyiY_cv8Gvj-53ddjzXuqT9hBU2f0cHbhIKs_jY
|
|
105
105
|
swarms/schemas/swarms_api_schemas.py,sha256=uKqleW_7hNpqHi06yoba9jS2i9yzZp-SBV944MnkN68,6233
|
106
106
|
swarms/schemas/tool_schema_base_model.py,sha256=0biTGIoibsPPP3fOrkC6WvNU5vXaalyccVKC1fpO_eg,1409
|
107
107
|
swarms/structs/__init__.py,sha256=XbUK6MSivL9qHNFlcoGcAVRYPupQssD3dG_-yAqrycw,4345
|
108
|
-
swarms/structs/agent.py,sha256=
|
108
|
+
swarms/structs/agent.py,sha256=azNp6iejRn9GnRcWxkPy3hO38TWPvJTYlZysgfV056Q,104857
|
109
109
|
swarms/structs/agent_builder.py,sha256=tYNpfO4_8cgfMHfgA5DAOWffHnt70p6CLt59esqfVCY,12133
|
110
110
|
swarms/structs/agent_rag_handler.py,sha256=g17YRrNmf16TLvyFCCcsitVk3d-QNZmck_XYmjSN_YM,21372
|
111
111
|
swarms/structs/agent_registry.py,sha256=il507cO1NF-d4ChyANVLuWrN8bXsEAi8_7bLJ_sTU6A,12112
|
@@ -119,9 +119,9 @@ swarms/structs/base_workflow.py,sha256=DTfFwX3AdFYxACDYwUDqhsbcDZnITlg5TeEYyxmJB
|
|
119
119
|
swarms/structs/batch_agent_execution.py,sha256=d85DzeCq4uTbbPqLhAXFqFx_cxXUS5yRnJ1-gJkwU5w,1871
|
120
120
|
swarms/structs/concat.py,sha256=utezSxNyh1mIwXgdf8-dJ803NDPyEy79WE8zJHuooGk,732
|
121
121
|
swarms/structs/concurrent_workflow.py,sha256=OqXI-X-9a0hG2a7aLzobwd7CVF2ez0rgLj3ZHqri5bg,12952
|
122
|
-
swarms/structs/conversation.py,sha256=
|
122
|
+
swarms/structs/conversation.py,sha256=06CY2vHXbKr9egwPsQMxZbanxEvuMnuLI-8MXz7v5Bo,49390
|
123
123
|
swarms/structs/council_judge.py,sha256=siYDKiHMvFmShUTXxdo4R6vXiQhKt7bEBI205oC3kU4,19639
|
124
|
-
swarms/structs/csv_to_agent.py,sha256=
|
124
|
+
swarms/structs/csv_to_agent.py,sha256=Zv41sjeWA50msq-paGHESzlxZyMU78DYDLNNKZtNfoI,11125
|
125
125
|
swarms/structs/de_hallucination_swarm.py,sha256=9cC0rSSXGwYu6SRDwpeMbCcQ40C1WI1RE9SNapKRLOQ,10309
|
126
126
|
swarms/structs/deep_research_swarm.py,sha256=cNLDI7_lT47q4bCRlMJO4IAO3Fu_iF3nPi3jgtg6CJQ,17026
|
127
127
|
swarms/structs/dynamic_conversational_swarm.py,sha256=xm8x_0OCI4ijatgVt8dzHhLNUyMzqG2U_XQL14kcIS8,8354
|
@@ -160,11 +160,11 @@ swarms/structs/swarming_architectures.py,sha256=guNQU2N7Ofuk01fZbU3tmBJymnZ9zdGU
|
|
160
160
|
swarms/structs/tree_swarm.py,sha256=AnIxrt0KhWxAQN8uGjfCcOq-XCmsuTJiH8Ex4mXy8V8,12500
|
161
161
|
swarms/structs/utils.py,sha256=Mo6wHQYOB8baWZUKnAJN5Dsgubpo81umNwJIEDitb2A,1873
|
162
162
|
swarms/structs/various_alt_swarms.py,sha256=qdBuOF31UjatlKRu-9bxwyRQzIjohRhTv_63YoUeYEY,27866
|
163
|
-
swarms/telemetry/__init__.py,sha256=
|
163
|
+
swarms/telemetry/__init__.py,sha256=NfKiXtyEtQJuQ74qiFFy2MxMX5KdgUbU7c3cpnHvhmQ,553
|
164
164
|
swarms/telemetry/bootup.py,sha256=0leCNCy5rhzL19EsOsqHWSDI85KVcWO6_5hLDS0h4sY,1155
|
165
|
-
swarms/telemetry/main.py,sha256=
|
166
|
-
swarms/tools/__init__.py,sha256=
|
167
|
-
swarms/tools/base_tool.py,sha256=
|
165
|
+
swarms/telemetry/main.py,sha256=Yvc12fUErOdQgUl4B7blBLALLMnvQ4qGh-vgRZDBewQ,7877
|
166
|
+
swarms/tools/__init__.py,sha256=6j4tc28dxLrlHG88Yot90PHuNZxhJFb27xqJ8GjwN-0,2268
|
167
|
+
swarms/tools/base_tool.py,sha256=gxGqsXNjxmBxLQ4zX3jsGsXYPtWS6autaahAOAvJrfY,107174
|
168
168
|
swarms/tools/cohere_func_call_schema.py,sha256=XJ6_yBMXCrV9KjN7v9Bk1iFj69TRlGIWYKsUTA1oGiQ,600
|
169
169
|
swarms/tools/create_agent_tool.py,sha256=YsiBgrR9gkn2Jenu_mIFXOMJCWb_Hdw4gBYPQN5HEQk,3467
|
170
170
|
swarms/tools/func_calling_utils.py,sha256=PhHHZRHN-vRHA_h21ELRjXIhSRIrsT4UhU5-1Bhy-iU,3542
|
@@ -173,10 +173,10 @@ swarms/tools/function_util.py,sha256=DAnAPO0Ik__TAqL7IJzFmkukHnhpsW_QtALl3yj837g
|
|
173
173
|
swarms/tools/json_former.py,sha256=4ugLQ_EZpghhuhFsVKsy-ehin9K64pqVE2gLU7BTO_M,14376
|
174
174
|
swarms/tools/json_utils.py,sha256=WKMZjcJ0Vt6lgIjiTBenslcfjgRSLX4UWs4uDkKFMQI,1316
|
175
175
|
swarms/tools/logits_processor.py,sha256=NifZZ5w9yemWGJAJ5nHFrphtZVX1XlyesgvYZTxK1GM,2965
|
176
|
-
swarms/tools/mcp_client_call.py,sha256=
|
176
|
+
swarms/tools/mcp_client_call.py,sha256=DKivM3OVLeoOjx0hiorSyj3m3Z4MWWwqe4LT6KNi8HU,33007
|
177
177
|
swarms/tools/openai_func_calling_schema_pydantic.py,sha256=6BAH9kuaVTvJIbjgSSJ5XvHhWvWszPxgarkfUuE5Ads,978
|
178
178
|
swarms/tools/openai_tool_creator_decorator.py,sha256=SYZjHnARjWvnH9cBdj7Kc_Yy1muvNxMT3RQz8KkA2SE,2578
|
179
|
-
swarms/tools/py_func_to_openai_func_str.py,sha256=
|
179
|
+
swarms/tools/py_func_to_openai_func_str.py,sha256=MoNVYMiLN_aeexEmzwEktCLEouSmjU8j4XMNcSwU_LU,17050
|
180
180
|
swarms/tools/pydantic_to_json.py,sha256=sd5uWwjSHsu7M8wCBrPv0uje05-K4xcfbvKQ_zOaww8,3399
|
181
181
|
swarms/tools/tool_parse_exec.py,sha256=FW5XzkuNEs2YrroybjKChbCzDvaCs7ypknSDpYhfkd4,8717
|
182
182
|
swarms/tools/tool_registry.py,sha256=ULZmIKBTx9XRCJRD9hwXfY3iQw9v94arw-VV6jcuftY,7992
|
@@ -184,7 +184,7 @@ swarms/tools/tool_utils.py,sha256=yXzzqG7Ytd8ybB8bsjNUNLaXIuIp9JbbpUKCiHxQqo8,28
|
|
184
184
|
swarms/utils/__init__.py,sha256=tpbhE-BTISDMXemSRSRJz-Rz7m_C05Q5Pre3d9H9SN4,1195
|
185
185
|
swarms/utils/any_to_str.py,sha256=Qi4N9ed6LYnCs2AeFYo1zwEfYhOKUesGVFUmVUz54KI,2936
|
186
186
|
swarms/utils/audio_processing.py,sha256=Y3KaWG9WJrgquWCeaty20HWPIXfeuPAhcJFzoSBIQjE,9893
|
187
|
-
swarms/utils/auto_download_check_packages.py,sha256=
|
187
|
+
swarms/utils/auto_download_check_packages.py,sha256=nnEPfUr2zy_Y6vPZF56zyWLamz0e2gskS4ecKSw30ww,4594
|
188
188
|
swarms/utils/calculate_func_metrics.py,sha256=Nb5r7rWf809m5F7mWIYXZ0H_WeyGr78A2UZD2GHtJkM,5007
|
189
189
|
swarms/utils/check_all_model_max_tokens.py,sha256=ZHIKlrU-L-OM2IJAbYkCoVyBKe2d0JrGhDC9QNppGIs,1519
|
190
190
|
swarms/utils/data_to_text.py,sha256=1PUoWokylp7MOrGNk1cmO3cJlfskdAIiImGk9ECwsKU,3427
|
@@ -193,20 +193,20 @@ swarms/utils/file_processing.py,sha256=QjQCIPTcwicQlfy656BXBYpIzMR0s2343E7ftnok5
|
|
193
193
|
swarms/utils/formatter.py,sha256=e15FsyTIIkyRreMUApkkZCzJC1Sm67w5Zd6EQcUkMwA,4533
|
194
194
|
swarms/utils/function_caller_model.py,sha256=ZfgCMzOizNnuZipYLclTziECNHszH9p8RQcUq7VNr4Q,4156
|
195
195
|
swarms/utils/generate_keys.py,sha256=o5zp_8rwu5sgQnItWS1xAuIIRIkahwm02qy1vsV6DSQ,997
|
196
|
-
swarms/utils/history_output_formatter.py,sha256=
|
196
|
+
swarms/utils/history_output_formatter.py,sha256=whjfk4N5SjMo3mtrafNny_alNiAhQcGza3l1dCyg7Nw,1388
|
197
197
|
swarms/utils/index.py,sha256=iYVlMiuSpBuKHF34uSrxDUuSYmS26bbYoAqyz_VIyvY,6902
|
198
198
|
swarms/utils/litellm_tokenizer.py,sha256=PqzAY4C5lJ3P-K9SL-dCNtxmHHlZvAw1UohT-ob9lxY,3389
|
199
199
|
swarms/utils/litellm_wrapper.py,sha256=moSaVYIViCSib2r1kS2h1BqltQKcysVJCeI6cAXp2bU,19566
|
200
200
|
swarms/utils/loguru_logger.py,sha256=hIoSK3NHLpe7eAmjHRURrEYzNXYC2gbR7_Vv63Yaydk,685
|
201
|
-
swarms/utils/output_types.py,sha256=
|
201
|
+
swarms/utils/output_types.py,sha256=jPOiRr2s114Dw2ngG8DTtxHIWZikMfNtXkyLtgghj9w,404
|
202
202
|
swarms/utils/parse_code.py,sha256=XFOLymbdP3HzMZuqsj7pwUyisvUmTm0ev9iThR_ambI,1987
|
203
203
|
swarms/utils/pdf_to_text.py,sha256=nkySOS_sJ4Jf4RP5SoDpMB5WfjJ_GGc5z8gJfn2cxOM,1311
|
204
204
|
swarms/utils/str_to_dict.py,sha256=T3Jsdjz87WIlkSo7jAW6BB80sv0Ns49WT1qXlOrdEoE,874
|
205
205
|
swarms/utils/try_except_wrapper.py,sha256=uvDZDZJcH986EF0Ej6zZBLcqHJ58NHizPsAH5olrE7Q,3919
|
206
206
|
swarms/utils/vllm_wrapper.py,sha256=sNkm4EbeMrqqmHidnvq5zTnofQAaARy3HIrNBu11lKs,5072
|
207
207
|
swarms/utils/xml_utils.py,sha256=D4nEdo1nkHqSoTKrWylXBXjcHFhGaOYvvfGNQQoYV5o,2514
|
208
|
-
swarms-7.8.
|
209
|
-
swarms-7.8.
|
210
|
-
swarms-7.8.
|
211
|
-
swarms-7.8.
|
212
|
-
swarms-7.8.
|
208
|
+
swarms-7.8.9.dist-info/LICENSE,sha256=jwRtEmTWjLrEsvFB6QFdYs2cEeZPRMdj-UMOFkPF8_0,11363
|
209
|
+
swarms-7.8.9.dist-info/METADATA,sha256=-jF_6uuBLf4o-NOrosFWpsPN84oaSLBHa7hw_loIipo,94363
|
210
|
+
swarms-7.8.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
211
|
+
swarms-7.8.9.dist-info/entry_points.txt,sha256=2K0rTtfO1X1WaO-waJlXIKw5Voa_EpAL_yU0HXE2Jgc,47
|
212
|
+
swarms-7.8.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|