tooluniverse 1.0.2__py3-none-any.whl → 1.0.3__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.
Potentially problematic release.
This version of tooluniverse might be problematic. Click here for more details.
- tooluniverse/compose_scripts/tool_metadata_generator.py +6 -0
- tooluniverse/data/agentic_tools.json +1 -1
- tooluniverse/execute_function.py +185 -70
- tooluniverse/scripts/filter_tool_files.py +194 -0
- tooluniverse/test/test_list_built_in_tools.py +33 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/METADATA +6 -6
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/RECORD +11 -9
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/WHEEL +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/entry_points.txt +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/licenses/LICENSE +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -17,6 +17,7 @@ def compose(arguments, tooluniverse, call_tool):
|
|
|
17
17
|
"""
|
|
18
18
|
import json
|
|
19
19
|
import warnings
|
|
20
|
+
import uuid
|
|
20
21
|
from collections import Counter
|
|
21
22
|
|
|
22
23
|
def _parse_agent_output(output, tool_name="Unknown Tool"):
|
|
@@ -365,5 +366,10 @@ def compose(arguments, tooluniverse, call_tool):
|
|
|
365
366
|
|
|
366
367
|
except Exception as e:
|
|
367
368
|
print(f"An error occurred during single-occurrence tag removal: {e}")
|
|
369
|
+
|
|
370
|
+
# Step 6: Manually set the UUID 'id' field to ensure true randomness
|
|
371
|
+
for tool_metadata in all_tool_metadata:
|
|
372
|
+
if 'error' not in tool_metadata:
|
|
373
|
+
tool_metadata['id'] = str(uuid.uuid4())
|
|
368
374
|
|
|
369
375
|
return all_tool_metadata
|
|
@@ -1162,7 +1162,7 @@
|
|
|
1162
1162
|
"type": "AgenticTool",
|
|
1163
1163
|
"name": "ToolMetadataGenerator",
|
|
1164
1164
|
"description": "Generates a JSON structure with the metadata of a tool in ToolUniverse, given the JSON configuration of the tool.",
|
|
1165
|
-
"prompt": "You are an expert in processing ToolUniverse tool configurations. Your task is to extract and generate key metadata from a given tool's JSON configuration and return it as a new, structured JSON object.\n\n**Input Tool Configuration:**\n```json\n{tool_config}\n```\n\n**Tool Type Mappings (for simplifying toolType):**\n```json\n{tool_type_mappings}\n```\n\n**Instructions:**\nFrom the input configuration, generate a new JSON object with the specified structure. All fields enclosed in '<','>' are placeholders for instructions; you should generate a specific value for the tool based on its configuration. Fields not in brackets should use the default values provided.\n\n**Output JSON Structure:**\n```json\n{\n \"id\": \"<generate a new uuid>\",\n \"name\": \"<extract from tool_config.name>\",\n \"description\": \"<extract and tool_config.description and slightly summarize it if it is too long>\",\n \"detailed_description\": \"<extract from tool_config.description>\",\n \"toolType\": \"<if tool_config.type or tool_config.name appears in tool_type_mappings dict in one of the lists (among the dict's values), extract the corresponding key and set it as the simplified toolType. otherwise, set toolType to be 'API' (the default)>\",\n \"tags\": [],\n \"category\": \"<extract from tool_config.type>\",\n \"lab\": \"Zitnik Lab\",\n \"source\": \"<extract the name of the database, package, model, or write 'Agentic'>\",\n \"version\": \"v1.0.0\",\n \"reviewed\": true,\n \"isValidated\": true,\n \"usageStats\": \"100+ uses\",\n \"capabilities\": [\n \"<list capabilities strictly derivable from tool_config>\"\n ],\n \"limitations\": [\n \"
|
|
1165
|
+
"prompt": "You are an expert in processing ToolUniverse tool configurations. Your task is to extract and generate key metadata from a given tool's JSON configuration and return it as a new, structured JSON object.\n\n**Input Tool Configuration:**\n```json\n{tool_config}\n```\n\n**Tool Type Mappings (for simplifying toolType):**\n```json\n{tool_type_mappings}\n```\n\n**Instructions:**\nFrom the input configuration, generate a new JSON object with the specified structure. All fields enclosed in '<','>' are placeholders for instructions; you should generate a specific value for the tool based on its configuration. Fields not in brackets should use the default values provided.\n\n**Output JSON Structure:**\n```json\n{\n \"id\": \"<generate a new uuid>\",\n \"name\": \"<extract from tool_config.name>\",\n \"description\": \"<extract and tool_config.description and slightly summarize it if it is too long>\",\n \"detailed_description\": \"<extract from tool_config.description>\",\n \"toolType\": \"<if tool_config.type or tool_config.name appears in tool_type_mappings dict in one of the lists (among the dict's values), extract the corresponding key and set it as the simplified toolType. otherwise, set toolType to be 'API' (the default)>\",\n \"tags\": [],\n \"category\": \"<extract from tool_config.type>\",\n \"lab\": \"Zitnik Lab\",\n \"source\": \"<extract the name of the database, package, model, or write 'Agentic'>\",\n \"version\": \"v1.0.0\",\n \"reviewed\": true,\n \"isValidated\": true,\n \"usageStats\": \"100+ uses\",\n \"capabilities\": [\n \"<list capabilities strictly derivable from tool_config>\"\n ],\n \"limitations\": [\n \"None for now\"\n ],\n \"parameters\": {<for each parameter key include an object with type and description>},\n \"inputSchema\": <echo tool_config.parameter exactly>,\n \"exampleInput\": <JSON object with example values for each parameter>,\n \"apiEndpoints\": [\n {\n \"method\": \"MCP\",\n \"url\": \"https://tooluniversemcpserver.onrender.com/mcp/\"\n }\n ]\n}\n```\n\nReturn ONLY the final JSON object with no extra commentary.",
|
|
1166
1166
|
"input_arguments": [
|
|
1167
1167
|
"tool_config",
|
|
1168
1168
|
"tool_type_mappings"
|
tooluniverse/execute_function.py
CHANGED
|
@@ -1000,9 +1000,9 @@ class ToolUniverse:
|
|
|
1000
1000
|
"""
|
|
1001
1001
|
return copy.deepcopy(self.all_tools)
|
|
1002
1002
|
|
|
1003
|
-
def list_built_in_tools(self, mode="config"):
|
|
1003
|
+
def list_built_in_tools(self, mode="config", scan_all=False):
|
|
1004
1004
|
"""
|
|
1005
|
-
List all built-in tool categories and their statistics with
|
|
1005
|
+
List all built-in tool categories and their statistics with different modes.
|
|
1006
1006
|
|
|
1007
1007
|
This method provides a comprehensive overview of all available tools in the ToolUniverse,
|
|
1008
1008
|
organized by categories. It reads directly from the default tool files to gather statistics,
|
|
@@ -1012,39 +1012,62 @@ class ToolUniverse:
|
|
|
1012
1012
|
mode (str, optional): Organization mode for tools. Defaults to 'config'.
|
|
1013
1013
|
- 'config': Organize by config file categories (original behavior)
|
|
1014
1014
|
- 'type': Organize by tool types (implementation classes)
|
|
1015
|
+
- 'list_name': Return a list of all tool names
|
|
1016
|
+
- 'list_spec': Return a list of all tool specifications
|
|
1017
|
+
scan_all (bool, optional): Whether to scan all JSON files in data directory recursively.
|
|
1018
|
+
If True, scans all JSON files in data/ and its subdirectories.
|
|
1019
|
+
If False (default), uses predefined tool file mappings.
|
|
1015
1020
|
|
|
1016
1021
|
Returns:
|
|
1017
|
-
dict
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
'category_name': {
|
|
1022
|
-
'count': int, # Number of tools in this category
|
|
1023
|
-
'tools': list # List of tool names (only when mode='type')
|
|
1024
|
-
},
|
|
1025
|
-
...
|
|
1026
|
-
},
|
|
1027
|
-
'total_categories': int, # Total number of tool categories
|
|
1028
|
-
'total_tools': int, # Total number of unique tools
|
|
1029
|
-
'mode': str, # The mode used for organization
|
|
1030
|
-
'summary': str # Human-readable summary of statistics
|
|
1031
|
-
}
|
|
1022
|
+
dict or list:
|
|
1023
|
+
- For 'config' and 'type' modes: A dictionary containing tool statistics
|
|
1024
|
+
- For 'list_name' mode: A list of all tool names
|
|
1025
|
+
- For 'list_spec' mode: A list of all tool specifications
|
|
1032
1026
|
|
|
1033
1027
|
Example:
|
|
1034
1028
|
>>> tool_universe = ToolUniverse()
|
|
1035
|
-
>>> # Group by config file categories
|
|
1029
|
+
>>> # Group by config file categories (predefined files only)
|
|
1036
1030
|
>>> stats = tool_universe.list_built_in_tools(mode='config')
|
|
1037
|
-
>>> #
|
|
1038
|
-
>>> stats = tool_universe.list_built_in_tools(mode='
|
|
1031
|
+
>>> # Scan all JSON files in data directory recursively
|
|
1032
|
+
>>> stats = tool_universe.list_built_in_tools(mode='config', scan_all=True)
|
|
1033
|
+
>>> # Get all tool names from all JSON files
|
|
1034
|
+
>>> tool_names = tool_universe.list_built_in_tools(mode='list_name', scan_all=True)
|
|
1039
1035
|
|
|
1040
1036
|
Note:
|
|
1041
1037
|
- This method reads directly from tool files and works without calling load_tools()
|
|
1042
1038
|
- Tools are deduplicated across categories, so the same tool won't be counted multiple times
|
|
1043
|
-
- The summary is automatically printed to console when this method is called
|
|
1039
|
+
- The summary is automatically printed to console when this method is called (except for list_name and list_spec modes)
|
|
1040
|
+
- When scan_all=True, all JSON files in data/ and subdirectories are scanned
|
|
1044
1041
|
"""
|
|
1045
|
-
if mode not in ["config", "type"]:
|
|
1046
|
-
raise ValueError(
|
|
1042
|
+
if mode not in ["config", "type", "list_name", "list_spec"]:
|
|
1043
|
+
raise ValueError(
|
|
1044
|
+
"Mode must be one of: 'config', 'type', 'list_name', 'list_spec'"
|
|
1045
|
+
)
|
|
1047
1046
|
|
|
1047
|
+
# For list_name and list_spec modes, we can return early with just the data
|
|
1048
|
+
if mode in ["list_name", "list_spec"]:
|
|
1049
|
+
all_tools = []
|
|
1050
|
+
all_tool_names = set() # For deduplication across categories
|
|
1051
|
+
|
|
1052
|
+
if scan_all:
|
|
1053
|
+
# Scan all JSON files in data directory recursively
|
|
1054
|
+
all_tools, all_tool_names = self._scan_all_json_files()
|
|
1055
|
+
else:
|
|
1056
|
+
# Use predefined tool files (original behavior)
|
|
1057
|
+
all_tools, all_tool_names = self._scan_predefined_files()
|
|
1058
|
+
|
|
1059
|
+
# Deduplicate tools by name
|
|
1060
|
+
unique_tools = {}
|
|
1061
|
+
for tool in all_tools:
|
|
1062
|
+
if tool["name"] not in unique_tools:
|
|
1063
|
+
unique_tools[tool["name"]] = tool
|
|
1064
|
+
|
|
1065
|
+
if mode == "list_name":
|
|
1066
|
+
return sorted(list(unique_tools.keys()))
|
|
1067
|
+
elif mode == "list_spec":
|
|
1068
|
+
return list(unique_tools.values())
|
|
1069
|
+
|
|
1070
|
+
# Original logic for config and type modes
|
|
1048
1071
|
result = {
|
|
1049
1072
|
"categories": {},
|
|
1050
1073
|
"total_categories": 0,
|
|
@@ -1053,58 +1076,43 @@ class ToolUniverse:
|
|
|
1053
1076
|
"summary": "",
|
|
1054
1077
|
}
|
|
1055
1078
|
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
# Read tools from each category file
|
|
1060
|
-
for category, file_path in self.tool_files.items():
|
|
1061
|
-
try:
|
|
1062
|
-
# Read the JSON file for this category
|
|
1063
|
-
tools_in_category = read_json_list(file_path)
|
|
1064
|
-
all_tools.extend(tools_in_category)
|
|
1079
|
+
if scan_all:
|
|
1080
|
+
# Scan all JSON files in data directory recursively
|
|
1081
|
+
all_tools, all_tool_names = self._scan_all_json_files()
|
|
1065
1082
|
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1083
|
+
# For config mode with scan_all, organize by file names
|
|
1084
|
+
if mode == "config":
|
|
1085
|
+
file_tools_map = {}
|
|
1086
|
+
for tool in all_tools:
|
|
1087
|
+
# Get the source file for this tool (we need to track this)
|
|
1088
|
+
# For now, we'll organize by tool type as a fallback
|
|
1089
|
+
tool_type = tool.get("type", "Unknown")
|
|
1090
|
+
if tool_type not in file_tools_map:
|
|
1091
|
+
file_tools_map[tool_type] = []
|
|
1092
|
+
file_tools_map[tool_type].append(tool)
|
|
1093
|
+
|
|
1094
|
+
for category, tools in file_tools_map.items():
|
|
1095
|
+
result["categories"][category] = {"count": len(tools)}
|
|
1096
|
+
else:
|
|
1097
|
+
# Use predefined tool files (original behavior)
|
|
1098
|
+
all_tools, all_tool_names = self._scan_predefined_files()
|
|
1069
1099
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1100
|
+
# Read tools from each category file
|
|
1101
|
+
for category, file_path in self.tool_files.items():
|
|
1102
|
+
try:
|
|
1103
|
+
# Read the JSON file for this category
|
|
1104
|
+
tools_in_category = read_json_list(file_path)
|
|
1072
1105
|
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
)
|
|
1077
|
-
if mode == "config":
|
|
1078
|
-
result["categories"][category] = {"count": 0}
|
|
1106
|
+
if mode == "config":
|
|
1107
|
+
tool_names = [tool["name"] for tool in tools_in_category]
|
|
1108
|
+
result["categories"][category] = {"count": len(tool_names)}
|
|
1079
1109
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
remote_tools = []
|
|
1085
|
-
for fname in os.listdir(remote_dir):
|
|
1086
|
-
if not fname.lower().endswith(".json"):
|
|
1087
|
-
continue
|
|
1088
|
-
fpath = os.path.join(remote_dir, fname)
|
|
1089
|
-
try:
|
|
1090
|
-
tools_in_file = read_json_list(fpath)
|
|
1091
|
-
if isinstance(tools_in_file, dict):
|
|
1092
|
-
tools_in_file = list(tools_in_file.values())
|
|
1093
|
-
if isinstance(tools_in_file, list):
|
|
1094
|
-
remote_tools.extend(tools_in_file)
|
|
1095
|
-
except Exception as e:
|
|
1096
|
-
warning(
|
|
1097
|
-
f"Warning: Could not read remote tools from {fpath}: {e}"
|
|
1098
|
-
)
|
|
1099
|
-
if remote_tools:
|
|
1100
|
-
all_tools.extend(remote_tools)
|
|
1101
|
-
all_tool_names.update([tool["name"] for tool in remote_tools])
|
|
1110
|
+
except Exception as e:
|
|
1111
|
+
warning(
|
|
1112
|
+
f"Warning: Could not read tools from {category} ({file_path}): {e}"
|
|
1113
|
+
)
|
|
1102
1114
|
if mode == "config":
|
|
1103
|
-
result["categories"][
|
|
1104
|
-
"count": len(remote_tools)
|
|
1105
|
-
}
|
|
1106
|
-
except Exception as e:
|
|
1107
|
-
warning(f"Warning: Failed to scan remote tools directory: {e}")
|
|
1115
|
+
result["categories"][category] = {"count": 0}
|
|
1108
1116
|
|
|
1109
1117
|
# If mode is 'type', organize by tool types instead
|
|
1110
1118
|
if mode == "type":
|
|
@@ -1202,6 +1210,113 @@ class ToolUniverse:
|
|
|
1202
1210
|
|
|
1203
1211
|
return result
|
|
1204
1212
|
|
|
1213
|
+
def _scan_predefined_files(self):
|
|
1214
|
+
"""
|
|
1215
|
+
Scan predefined tool files (original behavior).
|
|
1216
|
+
|
|
1217
|
+
Returns:
|
|
1218
|
+
tuple: (all_tools, all_tool_names) where all_tools is a list of tool configs
|
|
1219
|
+
and all_tool_names is a set of tool names for deduplication
|
|
1220
|
+
"""
|
|
1221
|
+
all_tools = []
|
|
1222
|
+
all_tool_names = set()
|
|
1223
|
+
|
|
1224
|
+
# Read tools from each category file
|
|
1225
|
+
for category, file_path in self.tool_files.items():
|
|
1226
|
+
try:
|
|
1227
|
+
# Read the JSON file for this category
|
|
1228
|
+
tools_in_category = read_json_list(file_path)
|
|
1229
|
+
all_tools.extend(tools_in_category)
|
|
1230
|
+
all_tool_names.update([tool["name"] for tool in tools_in_category])
|
|
1231
|
+
except Exception as e:
|
|
1232
|
+
warning(
|
|
1233
|
+
f"Warning: Could not read tools from {category} ({file_path}): {e}"
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
# Also include remote tools
|
|
1237
|
+
try:
|
|
1238
|
+
remote_dir = os.path.join(current_dir, "data", "remote_tools")
|
|
1239
|
+
if os.path.isdir(remote_dir):
|
|
1240
|
+
remote_tools = []
|
|
1241
|
+
for fname in os.listdir(remote_dir):
|
|
1242
|
+
if not fname.lower().endswith(".json"):
|
|
1243
|
+
continue
|
|
1244
|
+
fpath = os.path.join(remote_dir, fname)
|
|
1245
|
+
try:
|
|
1246
|
+
tools_in_file = read_json_list(fpath)
|
|
1247
|
+
if isinstance(tools_in_file, dict):
|
|
1248
|
+
tools_in_file = list(tools_in_file.values())
|
|
1249
|
+
if isinstance(tools_in_file, list):
|
|
1250
|
+
remote_tools.extend(tools_in_file)
|
|
1251
|
+
except Exception as e:
|
|
1252
|
+
warning(
|
|
1253
|
+
f"Warning: Could not read remote tools from {fpath}: {e}"
|
|
1254
|
+
)
|
|
1255
|
+
if remote_tools:
|
|
1256
|
+
all_tools.extend(remote_tools)
|
|
1257
|
+
all_tool_names.update([tool["name"] for tool in remote_tools])
|
|
1258
|
+
except Exception as e:
|
|
1259
|
+
warning(f"Warning: Failed to scan remote tools directory: {e}")
|
|
1260
|
+
|
|
1261
|
+
return all_tools, all_tool_names
|
|
1262
|
+
|
|
1263
|
+
def _scan_all_json_files(self):
|
|
1264
|
+
"""
|
|
1265
|
+
Recursively scan all JSON files in the data directory and its subdirectories.
|
|
1266
|
+
|
|
1267
|
+
Returns:
|
|
1268
|
+
tuple: (all_tools, all_tool_names) where all_tools is a list of tool configs
|
|
1269
|
+
and all_tool_names is a set of tool names for deduplication
|
|
1270
|
+
"""
|
|
1271
|
+
all_tools = []
|
|
1272
|
+
all_tool_names = set()
|
|
1273
|
+
|
|
1274
|
+
# Get the data directory path
|
|
1275
|
+
data_dir = os.path.join(current_dir, "data")
|
|
1276
|
+
|
|
1277
|
+
if not os.path.exists(data_dir):
|
|
1278
|
+
warning(f"Warning: Data directory not found: {data_dir}")
|
|
1279
|
+
return all_tools, all_tool_names
|
|
1280
|
+
|
|
1281
|
+
# Recursively find all JSON files
|
|
1282
|
+
json_files = []
|
|
1283
|
+
for root, _dirs, files in os.walk(data_dir):
|
|
1284
|
+
for file in files:
|
|
1285
|
+
if file.lower().endswith(".json"):
|
|
1286
|
+
json_files.append(os.path.join(root, file))
|
|
1287
|
+
|
|
1288
|
+
self.logger.debug(f"Found {len(json_files)} JSON files to scan")
|
|
1289
|
+
|
|
1290
|
+
# Read tools from each JSON file
|
|
1291
|
+
for json_file in json_files:
|
|
1292
|
+
try:
|
|
1293
|
+
tools_in_file = read_json_list(json_file)
|
|
1294
|
+
|
|
1295
|
+
# Handle different data formats
|
|
1296
|
+
if isinstance(tools_in_file, dict):
|
|
1297
|
+
# Convert dict of tools to list of tools
|
|
1298
|
+
tools_in_file = list(tools_in_file.values())
|
|
1299
|
+
elif not isinstance(tools_in_file, list):
|
|
1300
|
+
# Skip files that don't contain tool configurations
|
|
1301
|
+
continue
|
|
1302
|
+
|
|
1303
|
+
# Add tools to our collection
|
|
1304
|
+
for tool in tools_in_file:
|
|
1305
|
+
if isinstance(tool, dict) and "name" in tool:
|
|
1306
|
+
all_tools.append(tool)
|
|
1307
|
+
all_tool_names.add(tool["name"])
|
|
1308
|
+
|
|
1309
|
+
self.logger.debug(f"Loaded {len(tools_in_file)} tools from {json_file}")
|
|
1310
|
+
|
|
1311
|
+
except Exception as e:
|
|
1312
|
+
warning(f"Warning: Could not read tools from {json_file}: {e}")
|
|
1313
|
+
continue
|
|
1314
|
+
|
|
1315
|
+
self.logger.info(
|
|
1316
|
+
f"Scanned {len(json_files)} JSON files, found {len(all_tools)} tools"
|
|
1317
|
+
)
|
|
1318
|
+
return all_tools, all_tool_names
|
|
1319
|
+
|
|
1205
1320
|
def refresh_tool_name_desc(
|
|
1206
1321
|
self,
|
|
1207
1322
|
enable_full_desc=False,
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Script to filter tool files by removing tools that don't exist in the current tool universe.
|
|
4
|
+
|
|
5
|
+
This script:
|
|
6
|
+
1. Gets all valid tool names from ToolUniverse using scan_all=True
|
|
7
|
+
2. Filters tool_relationship_graph_FINAL.json to keep only valid tools
|
|
8
|
+
3. Filters v4_all_tools_final.json to keep only valid tools
|
|
9
|
+
4. Preserves all other data structure and content
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
# Import after modifying sys.path
|
|
16
|
+
from tooluniverse import ToolUniverse
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def load_json_file(file_path):
|
|
20
|
+
"""Load JSON file and return the data."""
|
|
21
|
+
try:
|
|
22
|
+
with open(file_path, "r", encoding="utf-8") as f:
|
|
23
|
+
return json.load(f)
|
|
24
|
+
except Exception as e:
|
|
25
|
+
print(f"Error loading {file_path}: {e}")
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def save_json_file(file_path, data):
|
|
30
|
+
"""Save data to JSON file."""
|
|
31
|
+
try:
|
|
32
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
33
|
+
json.dump(data, f, ensure_ascii=False, indent=2)
|
|
34
|
+
print(f"Successfully saved filtered data to {file_path}")
|
|
35
|
+
return True
|
|
36
|
+
except Exception as e:
|
|
37
|
+
print(f"Error saving {file_path}: {e}")
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def filter_tool_relationship_graph(data, valid_tool_names):
|
|
42
|
+
"""
|
|
43
|
+
Filter tool_relationship_graph_FINAL.json to keep only valid tools.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
data: The loaded JSON data
|
|
47
|
+
valid_tool_names: Set of valid tool names
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Filtered data
|
|
51
|
+
"""
|
|
52
|
+
if not isinstance(data, dict):
|
|
53
|
+
print("Warning: tool_relationship_graph data is not a dict")
|
|
54
|
+
return data
|
|
55
|
+
|
|
56
|
+
filtered_data = {}
|
|
57
|
+
|
|
58
|
+
# Handle nodes array
|
|
59
|
+
if "nodes" in data and isinstance(data["nodes"], list):
|
|
60
|
+
filtered_nodes = []
|
|
61
|
+
for node in data["nodes"]:
|
|
62
|
+
if isinstance(node, dict) and "name" in node:
|
|
63
|
+
if node["name"] in valid_tool_names:
|
|
64
|
+
filtered_nodes.append(node)
|
|
65
|
+
else:
|
|
66
|
+
print(f"Removing node from relationship graph: {node['name']}")
|
|
67
|
+
else:
|
|
68
|
+
# Keep non-tool nodes (if any)
|
|
69
|
+
filtered_nodes.append(node)
|
|
70
|
+
filtered_data["nodes"] = filtered_nodes
|
|
71
|
+
print(
|
|
72
|
+
f"Nodes: {len(data['nodes'])} -> {len(filtered_nodes)} ({len(data['nodes']) - len(filtered_nodes)} removed)"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Handle edges array
|
|
76
|
+
if "edges" in data and isinstance(data["edges"], list):
|
|
77
|
+
filtered_edges = []
|
|
78
|
+
for edge in data["edges"]:
|
|
79
|
+
if isinstance(edge, dict) and "source" in edge and "target" in edge:
|
|
80
|
+
# Keep edge if both source and target are valid tools
|
|
81
|
+
if (
|
|
82
|
+
edge["source"] in valid_tool_names
|
|
83
|
+
and edge["target"] in valid_tool_names
|
|
84
|
+
):
|
|
85
|
+
filtered_edges.append(edge)
|
|
86
|
+
else:
|
|
87
|
+
print(
|
|
88
|
+
f"Removing edge from relationship graph: {edge.get('source', 'unknown')} -> {edge.get('target', 'unknown')}"
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
# Keep non-tool edges (if any)
|
|
92
|
+
filtered_edges.append(edge)
|
|
93
|
+
filtered_data["edges"] = filtered_edges
|
|
94
|
+
print(
|
|
95
|
+
f"Edges: {len(data['edges'])} -> {len(filtered_edges)} ({len(data['edges']) - len(filtered_edges)} removed)"
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
# Keep other fields as-is (like stats, metadata, etc.)
|
|
99
|
+
for key, value in data.items():
|
|
100
|
+
if key not in ["nodes", "edges"]:
|
|
101
|
+
filtered_data[key] = value
|
|
102
|
+
|
|
103
|
+
return filtered_data
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def filter_v4_all_tools(data, valid_tool_names):
|
|
107
|
+
"""
|
|
108
|
+
Filter v4_all_tools_final.json to keep only valid tools.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
data: The loaded JSON data
|
|
112
|
+
valid_tool_names: Set of valid tool names
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Filtered data
|
|
116
|
+
"""
|
|
117
|
+
if not isinstance(data, list):
|
|
118
|
+
print("Warning: v4_all_tools data is not a list")
|
|
119
|
+
return data
|
|
120
|
+
|
|
121
|
+
filtered_data = []
|
|
122
|
+
|
|
123
|
+
for tool in data:
|
|
124
|
+
if isinstance(tool, dict) and "name" in tool:
|
|
125
|
+
if tool["name"] in valid_tool_names:
|
|
126
|
+
filtered_data.append(tool)
|
|
127
|
+
else:
|
|
128
|
+
print(f"Removing tool from v4_all_tools: {tool['name']}")
|
|
129
|
+
else:
|
|
130
|
+
# Keep non-tool entries (if any)
|
|
131
|
+
filtered_data.append(tool)
|
|
132
|
+
|
|
133
|
+
return filtered_data
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def main():
|
|
137
|
+
"""Main function to filter the tool files."""
|
|
138
|
+
print("Starting tool file filtering process...")
|
|
139
|
+
|
|
140
|
+
# Initialize ToolUniverse and get all valid tool names
|
|
141
|
+
print("Getting all valid tool names from ToolUniverse...")
|
|
142
|
+
tu = ToolUniverse()
|
|
143
|
+
all_tool_names = tu.list_built_in_tools(mode="list_name", scan_all=True)
|
|
144
|
+
valid_tool_names = set(all_tool_names)
|
|
145
|
+
print(f"Found {len(valid_tool_names)} valid tools")
|
|
146
|
+
|
|
147
|
+
# Define file paths
|
|
148
|
+
project_root = Path(__file__).parent.parent.parent.parent
|
|
149
|
+
web_dir = project_root / "web"
|
|
150
|
+
|
|
151
|
+
relationship_graph_file = web_dir / "tool_relationship_graph_FINAL.json"
|
|
152
|
+
v4_tools_file = web_dir / "v4_all_tools_final.json"
|
|
153
|
+
|
|
154
|
+
# Check if files exist
|
|
155
|
+
if not relationship_graph_file.exists():
|
|
156
|
+
print(f"Error: {relationship_graph_file} not found")
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
if not v4_tools_file.exists():
|
|
160
|
+
print(f"Error: {v4_tools_file} not found")
|
|
161
|
+
return
|
|
162
|
+
|
|
163
|
+
# Process tool_relationship_graph_FINAL.json
|
|
164
|
+
print(f"\nProcessing {relationship_graph_file.name}...")
|
|
165
|
+
relationship_data = load_json_file(relationship_graph_file)
|
|
166
|
+
if relationship_data is not None:
|
|
167
|
+
len(relationship_data.get("nodes", []))
|
|
168
|
+
len(relationship_data.get("edges", []))
|
|
169
|
+
filtered_relationship_data = filter_tool_relationship_graph(
|
|
170
|
+
relationship_data, valid_tool_names
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
# Save filtered data
|
|
174
|
+
save_json_file(relationship_graph_file, filtered_relationship_data)
|
|
175
|
+
|
|
176
|
+
# Process v4_all_tools_final.json
|
|
177
|
+
print(f"\nProcessing {v4_tools_file.name}...")
|
|
178
|
+
v4_data = load_json_file(v4_tools_file)
|
|
179
|
+
if v4_data is not None:
|
|
180
|
+
original_count = len(v4_data)
|
|
181
|
+
filtered_v4_data = filter_v4_all_tools(v4_data, valid_tool_names)
|
|
182
|
+
filtered_count = len(filtered_v4_data)
|
|
183
|
+
print(
|
|
184
|
+
f"V4 tools: {original_count} -> {filtered_count} tools ({original_count - filtered_count} removed)"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Save filtered data
|
|
188
|
+
save_json_file(v4_tools_file, filtered_v4_data)
|
|
189
|
+
|
|
190
|
+
print("\nTool file filtering completed!")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
if __name__ == "__main__":
|
|
194
|
+
main()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Basic test for list_built_in_tools including scan_all option.
|
|
4
|
+
Run directly: python tests/test_list_built_in_tools.py
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from tooluniverse import ToolUniverse # noqa: E402
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
tu = ToolUniverse()
|
|
12
|
+
|
|
13
|
+
# Use predefined files (original behavior)
|
|
14
|
+
tool_names = tu.list_built_in_tools(mode="list_name", scan_all=False)
|
|
15
|
+
print(f"predefined tool names: {len(tool_names)}")
|
|
16
|
+
|
|
17
|
+
# Scan all JSON files
|
|
18
|
+
all_tool_names = tu.list_built_in_tools(mode="list_name", scan_all=True)
|
|
19
|
+
print(f"all tool names (scan_all): {len(all_tool_names)}")
|
|
20
|
+
|
|
21
|
+
# Get all tool specifications
|
|
22
|
+
all_tool_specs = tu.list_built_in_tools(mode="list_spec", scan_all=True)
|
|
23
|
+
print(f"all tool specs (scan_all): {len(all_tool_specs)}")
|
|
24
|
+
|
|
25
|
+
# Organize all tools by type
|
|
26
|
+
type_stats = tu.list_built_in_tools(mode="type", scan_all=True)
|
|
27
|
+
print(
|
|
28
|
+
f"type stats -> total_categories: {type_stats['total_categories']}, total_tools: {type_stats['total_tools']}"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
main()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tooluniverse
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: A comprehensive collection of scientific tools for Agentic AI, offering integration with the ToolUniverse SDK and MCP Server to support advanced scientific workflows.
|
|
5
5
|
Author-email: Shanghua Gao <shanghuagao@gmail.com>
|
|
6
6
|
Project-URL: Homepage, https://github.com/mims-harvard/TxAgent
|
|
@@ -63,7 +63,7 @@ Provides-Extra: all
|
|
|
63
63
|
Requires-Dist: tooluniverse[dev,docs,graph]; extra == "all"
|
|
64
64
|
Dynamic: license-file
|
|
65
65
|
|
|
66
|
-
# ToolUniverse: Democratizing AI scientists
|
|
66
|
+
# <img src="docs/_static/logo.png" alt="ToolUniverse Logo" height="28" style="vertical-align: middle; margin-right: 8px;" /> ToolUniverse: Democratizing AI scientists
|
|
67
67
|
|
|
68
68
|
[](https://pypi.org/project/tooluniverse/)
|
|
69
69
|
[](https://github.com/mims-harvard/ToolUniverse)
|
|
@@ -96,7 +96,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
|
|
|
96
96
|
|
|
97
97
|
|
|
98
98
|
## 🤖 Building AI Scientists with ToolUniverse in 5 minutes
|
|
99
|
-
- **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI
|
|
99
|
+
- **[Overview](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/index.html)**: Create AI scientists from any LLM
|
|
100
100
|
- **[Claude Desktop Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_desktop.html)**: Native MCP integration with Claude Desktop App
|
|
101
101
|
- **[Claude Code Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/claude_code.html)**: AI scientist development in Claude Code environment
|
|
102
102
|
- **[Gemini CLI Integration](https://zitniklab.hms.harvard.edu/bioagent/guide/building_ai_scientists/gemini_cli.html)**: Command-line scientific research with Google Gemini
|
|
@@ -109,7 +109,7 @@ ToolUniverse is an ecosystem for creating AI scientist systems from any open or
|
|
|
109
109
|
|
|
110
110
|
AI scientists are emerging computational systems that serve as collaborative partners in discovery. However, these systems remain difficult to build because they are bespoke, tied to rigid workflows, and lack shared environments that unify tools, data, and analysts into a common ecosystem.
|
|
111
111
|
|
|
112
|
-
ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI
|
|
112
|
+
ToolUniverse addresses this challenge by providing a standardized ecosystem that transforms any AI model into a powerful research scientist. By abstracting capabilities behind a unified interface, ToolUniverse wraps around any AI model (LLM, AI agent, or large reasoning model) and enables users to create and refine entirely custom AI scientists without additional training or finetuning.
|
|
113
113
|
|
|
114
114
|
**Key Features:**
|
|
115
115
|
|
|
@@ -212,7 +212,7 @@ tooluniverse-smcp
|
|
|
212
212
|
|
|
213
213
|
|
|
214
214
|
---
|
|
215
|
-
**Hypercholesterolemia Drug Discovery** [[Tutorial]](
|
|
215
|
+
**Hypercholesterolemia Drug Discovery** [[Tutorial]](https://zitniklab.hms.harvard.edu/bioagent/tutorials/tooluniverse_case_study.html) [[Code]](https://colab.research.google.com/drive/1UwJ6RwyUoqI5risKQ365EeFdDQWOeOCv?usp=sharing)
|
|
216
216
|
|
|
217
217
|
---
|
|
218
218
|
|
|
@@ -316,4 +316,4 @@ ToolUniverse is developed by the [Zitnik Lab](https://zitniklab.hms.harvard.edu/
|
|
|
316
316
|
|
|
317
317
|
---
|
|
318
318
|
|
|
319
|
-
*Democratizing AI agents for science
|
|
319
|
+
*Democratizing AI agents for science with ToolUniverse.*
|
|
@@ -16,7 +16,7 @@ tooluniverse/embedding_database.py,sha256=ua-yIyv3dPq2GHdyZmiwZyaUlkKYyV3nslB4H9
|
|
|
16
16
|
tooluniverse/embedding_sync.py,sha256=Cq3g64MjNXXpiImrzPMNsCTvGZeEI9009OlHOD3EloI,14669
|
|
17
17
|
tooluniverse/enrichr_tool.py,sha256=YXuHwoG_Hi-kUWK7cY_X21b5pbqoREdG7fJxKq050Jg,9472
|
|
18
18
|
tooluniverse/europe_pmc_tool.py,sha256=aYTkDE_KMnpkYYMG5ojYZQ1QYsYB_Msf3pvNwY1Zz6w,1645
|
|
19
|
-
tooluniverse/execute_function.py,sha256=
|
|
19
|
+
tooluniverse/execute_function.py,sha256=T5y0OQdsPyz0hvrl9pqIfWjPi2OBJ1fhpuDPzjlKPac,82979
|
|
20
20
|
tooluniverse/extended_hooks.py,sha256=1Cg0rETh6_D21YFVqD-FhL7y4inZAGyQ6vMtHyugueg,15376
|
|
21
21
|
tooluniverse/gene_ontology_tool.py,sha256=l89XYdBEBm3eN3dG0lR4oV01H16_9IUB6a2c7W_c7Lg,7180
|
|
22
22
|
tooluniverse/graphql_tool.py,sha256=y7ztNyb9hn4OdSGKhLsHev_snkNgbAwJZL8ZGHm0LEk,8980
|
|
@@ -62,11 +62,11 @@ tooluniverse/compose_scripts/tool_description_optimizer.py,sha256=L6Kd8e6Af8pD6d
|
|
|
62
62
|
tooluniverse/compose_scripts/tool_discover.py,sha256=RKzCmhDWzDCsAl2KCcEQRNH96mOf_2bTQOH2EVGvpC8,24976
|
|
63
63
|
tooluniverse/compose_scripts/tool_graph_composer.py,sha256=lmGeN94Mc9SZCeYo8ez55tKYLS1-dI3JDm-onKB5DZA,15566
|
|
64
64
|
tooluniverse/compose_scripts/tool_graph_generation.py,sha256=zPVN58GufflVCo8Ak3V9_DpNjS9oLrhQ5A_JCN9TCzY,10052
|
|
65
|
-
tooluniverse/compose_scripts/tool_metadata_generator.py,sha256=
|
|
65
|
+
tooluniverse/compose_scripts/tool_metadata_generator.py,sha256=JAcLhXnmsZMzRjNRFM1fsxBtkjgY92LTzMGpgEq4s2k,19235
|
|
66
66
|
tooluniverse/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
tooluniverse/data/admetai_tools.json,sha256=08LCNSCZJxL6ZuPF1mVfbdlh9f2X5tnEwIYU7RcLLSM,6497
|
|
68
68
|
tooluniverse/data/adverse_event_tools.json,sha256=EDU_X8r4YtMnVt7SJpyimdo3FlcSNFSdvckWLShDODA,12752
|
|
69
|
-
tooluniverse/data/agentic_tools.json,sha256=
|
|
69
|
+
tooluniverse/data/agentic_tools.json,sha256=ABlMWxS06u-xqGHEDDEizUpprQ4MfDg1NXSKSWTo8x4,103299
|
|
70
70
|
tooluniverse/data/alphafold_tools.json,sha256=--JO9z9fQM95t5dphrBoKKr28HKWg7iQOUm1hQYhEOk,10628
|
|
71
71
|
tooluniverse/data/boltz_tools.json,sha256=_pMSRBkO3uj4vjYnQ7H6qbZFa4rzc9Looj6zHRyIkMg,303
|
|
72
72
|
tooluniverse/data/chembl_tools.json,sha256=aQfFyFlUNz_y9ZTO3dNje4QGhqt7rLv6j5YdMJR2-x8,747
|
|
@@ -135,6 +135,7 @@ tooluniverse/remote/pinnacle/pinnacle_tool.py,sha256=Oprd2RyMCZF-NsMjkZMpPOB2apC
|
|
|
135
135
|
tooluniverse/remote/transcriptformer/transcriptformer_tool.py,sha256=ZU_TT340LBwjTuowTaQV6QcANcsjYimA4CVSJHdU0c0,25229
|
|
136
136
|
tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py,sha256=gsfs-a3RZ7kwauO3uWNJjEOEi2ctHva3iRUtEYqjS6U,2352
|
|
137
137
|
tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py,sha256=hkDmSVh2-KEc56YhmgtwrgIwkYMFXR2fN1jrIL9vVlk,4460
|
|
138
|
+
tooluniverse/scripts/filter_tool_files.py,sha256=IZZIeSmBfHKntno790er1hQHSDcrxfdIHZwZ-Lvq7I4,6415
|
|
138
139
|
tooluniverse/scripts/generate_tool_graph.py,sha256=X_TpGcJ29CFH49Djzsz4gs8VvjDe0CcF6KFWU_hJHDI,12727
|
|
139
140
|
tooluniverse/scripts/visualize_tool_graph.py,sha256=4USGnUVdffJfOxourBKELldGhhk5HV7ZeHHREhyKqOc,25812
|
|
140
141
|
tooluniverse/test/mcp_server_test.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -158,6 +159,7 @@ tooluniverse/test/test_gwas_tool.py,sha256=_VXaf8K_1uO-oKlacJP6VTt4u5cjtX3w92G_e
|
|
|
158
159
|
tooluniverse/test/test_hpa.py,sha256=53lx0rXx0-aT1LRm9IEeBbV5cXaYlp3Ol5bmeRz10Hc,30006
|
|
159
160
|
tooluniverse/test/test_humanbase_tool.py,sha256=4KQnaehJwGtePEwb5my3yldZnMWQHfvCGEpzm-AWLH0,564
|
|
160
161
|
tooluniverse/test/test_idmap_tools.py,sha256=bP_AKhZG1lX9vQKrrrzuZ1iXIMWIWjAriB66pbFCE5M,1743
|
|
162
|
+
tooluniverse/test/test_list_built_in_tools.py,sha256=fLnCHCwoF1kWd28joMPwrYXi0b1e8EqfuIVq6GzZCvA,1035
|
|
161
163
|
tooluniverse/test/test_mcp_server.py,sha256=n3C3UuU9D-E6m6PbooWUgWwshLzncXDEmn0U9ugapaY,6631
|
|
162
164
|
tooluniverse/test/test_mcp_tool.py,sha256=q6a9MCjFggK6KRy8F9h14tV2BezNVj63v6tDFaST-ys,8290
|
|
163
165
|
tooluniverse/test/test_medlineplus.py,sha256=hQ2QyeHmF8BkCXMn9vW0SNImAcqd5jkZz_FhaZbZS7A,7332
|
|
@@ -176,9 +178,9 @@ tooluniverse/test/test_tools_find.py,sha256=vP9EV04iXXmejoY2rDhEM0Yc9uHEO2qEmd8s
|
|
|
176
178
|
tooluniverse/test/test_uniprot_tools.py,sha256=V0x7aQjjlE-wS5alFWviHNwC3saHiEE5pMMqAcbokaw,2582
|
|
177
179
|
tooluniverse/test/test_uspto_tool.py,sha256=y2VQBMbiHCTAmvXhBosHrc1innstUl9UMWNoPaBv-RY,2311
|
|
178
180
|
tooluniverse/test/test_xml_tool.py,sha256=tx-w_psZ801C2kM6aDbBzR8QV3Vgu1p2zP6zExhjpVs,3602
|
|
179
|
-
tooluniverse-1.0.
|
|
180
|
-
tooluniverse-1.0.
|
|
181
|
-
tooluniverse-1.0.
|
|
182
|
-
tooluniverse-1.0.
|
|
183
|
-
tooluniverse-1.0.
|
|
184
|
-
tooluniverse-1.0.
|
|
181
|
+
tooluniverse-1.0.3.dist-info/licenses/LICENSE,sha256=Unq9Y3buGL9J0vFop03FYZiVweytt1xdgLIh2n9ck2c,1115
|
|
182
|
+
tooluniverse-1.0.3.dist-info/METADATA,sha256=QijTmbm2I8zn-L7IVLeRuuDUcmpk8l-ZkWUQOhNK200,18362
|
|
183
|
+
tooluniverse-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
184
|
+
tooluniverse-1.0.3.dist-info/entry_points.txt,sha256=Z_znqzUc_XR9ZIXViavqNAwBkwM4XYfC9h7LoBbAAog,600
|
|
185
|
+
tooluniverse-1.0.3.dist-info/top_level.txt,sha256=zZ8YeCJ5FAkEwdd_mxsFtSCQMBDgBdxrrmHo3RNBiWs,13
|
|
186
|
+
tooluniverse-1.0.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|