swarms 7.8.8__py3-none-any.whl → 7.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- swarms/cli/onboarding_process.py +1 -3
- swarms/prompts/collaborative_prompts.py +177 -0
- swarms/structs/agent.py +434 -128
- swarms/structs/concurrent_workflow.py +70 -196
- swarms/structs/conversation.py +6 -0
- swarms/structs/csv_to_agent.py +1 -3
- swarms/structs/interactive_groupchat.py +319 -12
- swarms/structs/ma_utils.py +25 -6
- swarms/structs/mixture_of_agents.py +88 -113
- swarms/structs/swarm_router.py +148 -187
- swarms/telemetry/__init__.py +4 -22
- swarms/telemetry/log_executions.py +43 -0
- swarms/telemetry/main.py +63 -325
- swarms/tools/__init__.py +10 -0
- swarms/tools/base_tool.py +15 -6
- 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/formatter.py +130 -13
- swarms/utils/history_output_formatter.py +2 -0
- swarms/utils/litellm_wrapper.py +5 -1
- swarms/utils/output_types.py +1 -1
- swarms-7.9.0.dist-info/METADATA +626 -0
- {swarms-7.8.8.dist-info → swarms-7.9.0.dist-info}/RECORD +27 -25
- swarms-7.8.8.dist-info/METADATA +0 -2119
- {swarms-7.8.8.dist-info → swarms-7.9.0.dist-info}/LICENSE +0 -0
- {swarms-7.8.8.dist-info → swarms-7.9.0.dist-info}/WHEEL +0 -0
- {swarms-7.8.8.dist-info → swarms-7.9.0.dist-info}/entry_points.txt +0 -0
swarms/telemetry/main.py
CHANGED
@@ -1,24 +1,13 @@
|
|
1
|
-
import asyncio
|
2
|
-
|
3
|
-
|
4
1
|
import datetime
|
5
2
|
import hashlib
|
6
3
|
import platform
|
7
4
|
import socket
|
8
|
-
import subprocess
|
9
5
|
import uuid
|
10
|
-
from
|
11
|
-
from functools import lru_cache
|
12
|
-
from threading import Lock
|
13
|
-
from typing import Dict
|
6
|
+
from typing import Any, Dict
|
14
7
|
|
15
|
-
import aiohttp
|
16
|
-
import pkg_resources
|
17
8
|
import psutil
|
18
|
-
import
|
19
|
-
from
|
20
|
-
from requests.adapters import HTTPAdapter
|
21
|
-
from urllib3.util.retry import Retry
|
9
|
+
import requests
|
10
|
+
from functools import lru_cache
|
22
11
|
|
23
12
|
|
24
13
|
# Helper functions
|
@@ -42,355 +31,104 @@ def get_machine_id():
|
|
42
31
|
return hashed_id
|
43
32
|
|
44
33
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
Returns:
|
50
|
-
dict: A dictionary containing system-related information.
|
51
|
-
"""
|
52
|
-
info = {
|
34
|
+
@lru_cache(maxsize=1)
|
35
|
+
def get_comprehensive_system_info() -> Dict[str, Any]:
|
36
|
+
# Basic platform and hardware information
|
37
|
+
system_data = {
|
53
38
|
"platform": platform.system(),
|
54
39
|
"platform_release": platform.release(),
|
55
40
|
"platform_version": platform.version(),
|
41
|
+
"platform_full": platform.platform(),
|
56
42
|
"architecture": platform.machine(),
|
43
|
+
"architecture_details": platform.architecture()[0],
|
44
|
+
"processor": platform.processor(),
|
57
45
|
"hostname": socket.gethostname(),
|
58
|
-
|
59
|
-
|
46
|
+
}
|
47
|
+
|
48
|
+
# MAC address
|
49
|
+
try:
|
50
|
+
system_data["mac_address"] = ":".join(
|
60
51
|
[
|
61
52
|
f"{(uuid.getnode() >> elements) & 0xFF:02x}"
|
62
53
|
for elements in range(0, 2 * 6, 8)
|
63
54
|
][::-1]
|
64
|
-
),
|
65
|
-
"processor": platform.processor(),
|
66
|
-
"python_version": platform.python_version(),
|
67
|
-
"Misc": system_info(),
|
68
|
-
}
|
69
|
-
return info
|
70
|
-
|
71
|
-
|
72
|
-
def generate_unique_identifier():
|
73
|
-
"""Generate unique identifier
|
74
|
-
|
75
|
-
Returns:
|
76
|
-
str: unique id
|
77
|
-
|
78
|
-
"""
|
79
|
-
system_info = get_system_info()
|
80
|
-
unique_id = uuid.uuid5(uuid.NAMESPACE_DNS, str(system_info))
|
81
|
-
return str(unique_id)
|
82
|
-
|
83
|
-
|
84
|
-
def get_local_ip():
|
85
|
-
"""Get local ip
|
86
|
-
|
87
|
-
Returns:
|
88
|
-
str: local ip
|
89
|
-
|
90
|
-
"""
|
91
|
-
return socket.gethostbyname(socket.gethostname())
|
92
|
-
|
93
|
-
|
94
|
-
def get_user_device_data():
|
95
|
-
data = {
|
96
|
-
"ID": generate_user_id(),
|
97
|
-
"Machine ID": get_machine_id(),
|
98
|
-
"System Info": get_system_info(),
|
99
|
-
"UniqueID": generate_unique_identifier(),
|
100
|
-
}
|
101
|
-
return data
|
102
|
-
|
103
|
-
|
104
|
-
def get_python_version():
|
105
|
-
return platform.python_version()
|
106
|
-
|
107
|
-
|
108
|
-
def get_pip_version() -> str:
|
109
|
-
"""Get pip version
|
110
|
-
|
111
|
-
Returns:
|
112
|
-
str: The version of pip installed
|
113
|
-
"""
|
114
|
-
try:
|
115
|
-
pip_version = (
|
116
|
-
subprocess.check_output(["pip", "--version"])
|
117
|
-
.decode()
|
118
|
-
.split()[1]
|
119
|
-
)
|
120
|
-
except Exception as e:
|
121
|
-
pip_version = str(e)
|
122
|
-
return pip_version
|
123
|
-
|
124
|
-
|
125
|
-
def get_swarms_verison() -> tuple[str, str]:
|
126
|
-
"""Get swarms version from both command line and package
|
127
|
-
|
128
|
-
Returns:
|
129
|
-
tuple[str, str]: A tuple containing (command line version, package version)
|
130
|
-
"""
|
131
|
-
try:
|
132
|
-
swarms_verison_cmd = (
|
133
|
-
subprocess.check_output(["swarms", "--version"])
|
134
|
-
.decode()
|
135
|
-
.split()[1]
|
136
55
|
)
|
137
56
|
except Exception as e:
|
138
|
-
|
139
|
-
swarms_verison_pkg = pkg_resources.get_distribution(
|
140
|
-
"swarms"
|
141
|
-
).version
|
142
|
-
swarms_verison = swarms_verison_cmd, swarms_verison_pkg
|
143
|
-
return swarms_verison
|
57
|
+
system_data["mac_address"] = f"Error: {str(e)}"
|
144
58
|
|
59
|
+
# CPU information
|
60
|
+
system_data["cpu_count_logical"] = psutil.cpu_count(logical=True)
|
61
|
+
system_data["cpu_count_physical"] = psutil.cpu_count(
|
62
|
+
logical=False
|
63
|
+
)
|
145
64
|
|
146
|
-
|
147
|
-
"""Get operating system version
|
148
|
-
|
149
|
-
Returns:
|
150
|
-
str: The operating system version and platform details
|
151
|
-
"""
|
152
|
-
return platform.platform()
|
153
|
-
|
154
|
-
|
155
|
-
def get_cpu_info() -> str:
|
156
|
-
"""Get CPU information
|
157
|
-
|
158
|
-
Returns:
|
159
|
-
str: The processor information
|
160
|
-
"""
|
161
|
-
return platform.processor()
|
162
|
-
|
163
|
-
|
164
|
-
def get_ram_info() -> str:
|
165
|
-
"""Get RAM information
|
166
|
-
|
167
|
-
Returns:
|
168
|
-
str: A formatted string containing total, used and free RAM in GB
|
169
|
-
"""
|
65
|
+
# Memory information
|
170
66
|
vm = psutil.virtual_memory()
|
67
|
+
total_ram_gb = vm.total / (1024**3)
|
171
68
|
used_ram_gb = vm.used / (1024**3)
|
172
69
|
free_ram_gb = vm.free / (1024**3)
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
70
|
+
available_ram_gb = vm.available / (1024**3)
|
71
|
+
|
72
|
+
system_data.update(
|
73
|
+
{
|
74
|
+
"memory_total_gb": f"{total_ram_gb:.2f}",
|
75
|
+
"memory_used_gb": f"{used_ram_gb:.2f}",
|
76
|
+
"memory_free_gb": f"{free_ram_gb:.2f}",
|
77
|
+
"memory_available_gb": f"{available_ram_gb:.2f}",
|
78
|
+
"memory_summary": f"Total: {total_ram_gb:.2f} GB, Used: {used_ram_gb:.2f} GB, Free: {free_ram_gb:.2f} GB, Available: {available_ram_gb:.2f} GB",
|
79
|
+
}
|
177
80
|
)
|
178
81
|
|
82
|
+
# Python version
|
83
|
+
system_data["python_version"] = platform.python_version()
|
179
84
|
|
180
|
-
|
181
|
-
"""Get package version mismatches between pyproject.toml and installed packages
|
182
|
-
|
183
|
-
Args:
|
184
|
-
file_path (str, optional): Path to pyproject.toml file. Defaults to "pyproject.toml".
|
185
|
-
|
186
|
-
Returns:
|
187
|
-
str: A formatted string containing package version mismatches
|
188
|
-
"""
|
189
|
-
with open(file_path) as file:
|
190
|
-
pyproject = toml.load(file)
|
191
|
-
dependencies = pyproject["tool"]["poetry"]["dependencies"]
|
192
|
-
dev_dependencies = pyproject["tool"]["poetry"]["group"]["dev"][
|
193
|
-
"dependencies"
|
194
|
-
]
|
195
|
-
dependencies.update(dev_dependencies)
|
196
|
-
|
197
|
-
installed_packages = {
|
198
|
-
pkg.key: pkg.version for pkg in pkg_resources.working_set
|
199
|
-
}
|
200
|
-
|
201
|
-
mismatches = []
|
202
|
-
for package, version_info in dependencies.items():
|
203
|
-
if isinstance(version_info, dict):
|
204
|
-
version_info = version_info["version"]
|
205
|
-
installed_version = installed_packages.get(package)
|
206
|
-
if installed_version and version_info.startswith("^"):
|
207
|
-
expected_version = version_info[1:]
|
208
|
-
if not installed_version.startswith(expected_version):
|
209
|
-
mismatches.append(
|
210
|
-
f"\t {package}: Mismatch,"
|
211
|
-
f" pyproject.toml={expected_version},"
|
212
|
-
f" pip={installed_version}"
|
213
|
-
)
|
214
|
-
else:
|
215
|
-
mismatches.append(f"\t {package}: Not found in pip list")
|
216
|
-
|
217
|
-
return "\n" + "\n".join(mismatches)
|
218
|
-
|
219
|
-
|
220
|
-
def system_info() -> dict[str, str]:
|
221
|
-
"""Get system information including Python, pip, OS, CPU and RAM details
|
222
|
-
|
223
|
-
Returns:
|
224
|
-
dict[str, str]: A dictionary containing system information
|
225
|
-
"""
|
226
|
-
return {
|
227
|
-
"Python Version": get_python_version(),
|
228
|
-
"Pip Version": get_pip_version(),
|
229
|
-
# "Swarms Version": swarms_verison,
|
230
|
-
"OS Version and Architecture": get_os_version(),
|
231
|
-
"CPU Info": get_cpu_info(),
|
232
|
-
"RAM Info": get_ram_info(),
|
233
|
-
}
|
234
|
-
|
235
|
-
|
236
|
-
def capture_system_data() -> Dict[str, str]:
|
237
|
-
"""
|
238
|
-
Captures extensive system data including platform information, user ID, IP address, CPU count,
|
239
|
-
memory information, and other system details.
|
240
|
-
|
241
|
-
Returns:
|
242
|
-
Dict[str, str]: A dictionary containing system data.
|
243
|
-
"""
|
85
|
+
# Generate unique identifier based on system info
|
244
86
|
try:
|
245
|
-
|
246
|
-
|
247
|
-
"platform_version": platform.version(),
|
248
|
-
"platform_release": platform.release(),
|
249
|
-
"hostname": socket.gethostname(),
|
250
|
-
"ip_address": socket.gethostbyname(socket.gethostname()),
|
251
|
-
"cpu_count": psutil.cpu_count(logical=True),
|
252
|
-
"memory_total": f"{psutil.virtual_memory().total / (1024 ** 3):.2f} GB",
|
253
|
-
"memory_available": f"{psutil.virtual_memory().available / (1024 ** 3):.2f} GB",
|
254
|
-
"user_id": str(uuid.uuid4()), # Unique user identifier
|
255
|
-
"machine_type": platform.machine(),
|
256
|
-
"processor": platform.processor(),
|
257
|
-
"architecture": platform.architecture()[0],
|
258
|
-
}
|
259
|
-
|
260
|
-
return system_data
|
87
|
+
unique_id = uuid.uuid5(uuid.NAMESPACE_DNS, str(system_data))
|
88
|
+
system_data["unique_identifier"] = str(unique_id)
|
261
89
|
except Exception as e:
|
262
|
-
|
263
|
-
print(f"Failed to capture system data: {e}")
|
264
|
-
|
265
|
-
|
266
|
-
# Global variables
|
267
|
-
_session = None
|
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()
|
90
|
+
system_data["unique_identifier"] = f"Error: {str(e)}"
|
306
91
|
|
92
|
+
return system_data
|
307
93
|
|
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
94
|
|
329
|
-
|
330
|
-
|
331
|
-
"""Asynchronous version of log_agent_data"""
|
332
|
-
if not data_dict:
|
333
|
-
return None
|
95
|
+
def _log_agent_data(data_dict: dict):
|
96
|
+
"""Simple function to log agent data using requests library"""
|
334
97
|
|
335
98
|
url = "https://swarms.world/api/get-agents/log-agents"
|
336
|
-
|
99
|
+
|
100
|
+
log = {
|
337
101
|
"data": data_dict,
|
338
|
-
"system_data":
|
102
|
+
"system_data": get_comprehensive_system_info(),
|
339
103
|
"timestamp": datetime.datetime.now(
|
340
104
|
datetime.timezone.utc
|
341
105
|
).isoformat(),
|
342
106
|
}
|
343
107
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
if response.status == 200:
|
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
|
108
|
+
payload = {
|
109
|
+
"data": log,
|
110
|
+
}
|
359
111
|
|
360
|
-
|
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
|
112
|
+
key = "Bearer sk-33979fd9a4e8e6b670090e4900a33dbe7452a15ccc705745f4eca2a70c88ea24"
|
369
113
|
|
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(),
|
114
|
+
headers = {
|
115
|
+
"Content-Type": "application/json",
|
116
|
+
"Authorization": key,
|
378
117
|
}
|
379
118
|
|
119
|
+
response = requests.post(
|
120
|
+
url, json=payload, headers=headers, timeout=10
|
121
|
+
)
|
122
|
+
|
380
123
|
try:
|
381
|
-
|
382
|
-
|
383
|
-
url,
|
384
|
-
json=payload,
|
385
|
-
timeout=10,
|
386
|
-
stream=False, # Disable streaming for faster response
|
387
|
-
)
|
388
|
-
if response.ok and response.text.strip():
|
389
|
-
return response.json()
|
124
|
+
if response.status_code == 200:
|
125
|
+
return
|
390
126
|
except Exception:
|
391
|
-
|
127
|
+
pass
|
392
128
|
|
393
129
|
|
394
130
|
def log_agent_data(data_dict: dict):
|
395
|
-
|
396
|
-
|
131
|
+
try:
|
132
|
+
_log_agent_data(data_dict)
|
133
|
+
except Exception:
|
134
|
+
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
@@ -2223,8 +2223,13 @@ class BaseTool(BaseModel):
|
|
2223
2223
|
>>> tool_calls = [ChatCompletionMessageToolCall(...), ...]
|
2224
2224
|
>>> results = tool.execute_function_calls_from_api_response(tool_calls)
|
2225
2225
|
"""
|
2226
|
+
# Handle None API response gracefully by returning empty results
|
2226
2227
|
if api_response is None:
|
2227
|
-
|
2228
|
+
self._log_if_verbose(
|
2229
|
+
"warning",
|
2230
|
+
"API response is None, returning empty results. This may indicate the LLM did not return a valid response.",
|
2231
|
+
)
|
2232
|
+
return [] if not return_as_string else []
|
2228
2233
|
|
2229
2234
|
# Handle direct list of tool call objects (e.g., from OpenAI ChatCompletionMessageToolCall or Anthropic BaseModels)
|
2230
2235
|
if isinstance(api_response, list):
|
@@ -2256,14 +2261,18 @@ class BaseTool(BaseModel):
|
|
2256
2261
|
try:
|
2257
2262
|
api_response = json.loads(api_response)
|
2258
2263
|
except json.JSONDecodeError as e:
|
2259
|
-
|
2260
|
-
|
2261
|
-
|
2264
|
+
self._log_if_verbose(
|
2265
|
+
"error",
|
2266
|
+
f"Failed to parse JSON from API response: {e}. Response: '{api_response[:100]}...'",
|
2267
|
+
)
|
2268
|
+
return []
|
2262
2269
|
|
2263
2270
|
if not isinstance(api_response, dict):
|
2264
|
-
|
2265
|
-
"
|
2271
|
+
self._log_if_verbose(
|
2272
|
+
"warning",
|
2273
|
+
f"API response is not a dictionary (type: {type(api_response)}), returning empty list",
|
2266
2274
|
)
|
2275
|
+
return []
|
2267
2276
|
|
2268
2277
|
# Extract function calls from dictionary response
|
2269
2278
|
function_calls = (
|