tooluniverse 1.0.2__py3-none-any.whl → 1.0.4__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/agentic_tool.py +262 -330
- tooluniverse/compose_scripts/output_summarizer.py +21 -15
- tooluniverse/compose_scripts/tool_metadata_generator.py +6 -0
- tooluniverse/data/agentic_tools.json +1 -1
- tooluniverse/data/output_summarization_tools.json +2 -2
- tooluniverse/execute_function.py +185 -70
- tooluniverse/llm_clients.py +369 -0
- tooluniverse/output_hook.py +92 -3
- tooluniverse/scripts/filter_tool_files.py +194 -0
- tooluniverse/smcp_server.py +19 -13
- tooluniverse/test/list_azure_openai_models.py +210 -0
- tooluniverse/test/test_agentic_tool_azure_models.py +91 -0
- tooluniverse/test/test_api_key_validation_min.py +64 -0
- tooluniverse/test/test_global_fallback.py +288 -0
- tooluniverse/test/test_hooks_direct.py +219 -0
- tooluniverse/test/test_list_built_in_tools.py +33 -0
- tooluniverse/test/test_stdio_hooks.py +285 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/METADATA +7 -6
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/RECORD +23 -14
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/WHEEL +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/entry_points.txt +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/licenses/LICENSE +0 -0
- {tooluniverse-1.0.2.dist-info → tooluniverse-1.0.4.dist-info}/top_level.txt +0 -0
|
@@ -81,31 +81,37 @@ def compose(arguments: Dict[str, Any], tooluniverse, call_tool) -> Dict[str, Any
|
|
|
81
81
|
else:
|
|
82
82
|
print(f"❌ Chunk {i+1} summarization failed")
|
|
83
83
|
|
|
84
|
-
# Step 3: Merge summaries
|
|
84
|
+
# Step 3: Merge summaries (or gracefully fall back)
|
|
85
85
|
if chunk_summaries:
|
|
86
86
|
final_summary = _merge_summaries(
|
|
87
87
|
chunk_summaries, query_context, tool_name, max_summary_length, call_tool
|
|
88
88
|
)
|
|
89
|
+
print(
|
|
90
|
+
f"✅ Summarization completed. Final length: {len(final_summary)} characters"
|
|
91
|
+
)
|
|
92
|
+
return {
|
|
93
|
+
"success": True,
|
|
94
|
+
"original_length": len(tool_output),
|
|
95
|
+
"summary_length": len(final_summary),
|
|
96
|
+
"chunks_processed": len(chunks),
|
|
97
|
+
"summary": final_summary,
|
|
98
|
+
"tool_name": tool_name,
|
|
99
|
+
}
|
|
89
100
|
else:
|
|
90
|
-
|
|
101
|
+
# Treat as a non-fatal failure so upstream falls back to original output
|
|
91
102
|
print("❌ No chunk summaries were generated. This usually indicates:")
|
|
92
103
|
print(" 1. ToolOutputSummarizer tool is not available")
|
|
93
104
|
print(" 2. The output_summarization tools are not loaded")
|
|
94
105
|
print(" 3. There was an error in the summarization process")
|
|
95
106
|
print(" Please check that the SMCP server is started with hooks enabled.")
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
"summary_length": len(final_summary),
|
|
105
|
-
"chunks_processed": len(chunks),
|
|
106
|
-
"summary": final_summary,
|
|
107
|
-
"tool_name": tool_name,
|
|
108
|
-
}
|
|
107
|
+
return {
|
|
108
|
+
"success": False,
|
|
109
|
+
"error": "No chunk summaries generated",
|
|
110
|
+
"original_length": len(tool_output),
|
|
111
|
+
"chunks_processed": len(chunks),
|
|
112
|
+
"original_output": tool_output,
|
|
113
|
+
"tool_name": tool_name,
|
|
114
|
+
}
|
|
109
115
|
|
|
110
116
|
except Exception as e:
|
|
111
117
|
error_msg = f"Error in output summarization: {str(e)}"
|
|
@@ -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"
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"type": "integer",
|
|
74
74
|
"description": "Size of each chunk for processing",
|
|
75
75
|
"required": false,
|
|
76
|
-
"default":
|
|
76
|
+
"default": 30000
|
|
77
77
|
},
|
|
78
78
|
"focus_areas": {
|
|
79
79
|
"type": "string",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"type": "integer",
|
|
86
86
|
"description": "Maximum length of final summary",
|
|
87
87
|
"required": false,
|
|
88
|
-
"default":
|
|
88
|
+
"default": 10000
|
|
89
89
|
}
|
|
90
90
|
},
|
|
91
91
|
"required": ["tool_output", "query_context", "tool_name"]
|
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,
|