tooluniverse 0.1.4__py3-none-any.whl → 1.0.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.
Potentially problematic release.
This version of tooluniverse might be problematic. Click here for more details.
- tooluniverse/__init__.py +340 -4
- tooluniverse/admetai_tool.py +84 -0
- tooluniverse/agentic_tool.py +563 -0
- tooluniverse/alphafold_tool.py +96 -0
- tooluniverse/base_tool.py +129 -6
- tooluniverse/boltz_tool.py +207 -0
- tooluniverse/chem_tool.py +192 -0
- tooluniverse/compose_scripts/__init__.py +1 -0
- tooluniverse/compose_scripts/biomarker_discovery.py +293 -0
- tooluniverse/compose_scripts/comprehensive_drug_discovery.py +186 -0
- tooluniverse/compose_scripts/drug_safety_analyzer.py +89 -0
- tooluniverse/compose_scripts/literature_tool.py +34 -0
- tooluniverse/compose_scripts/output_summarizer.py +279 -0
- tooluniverse/compose_scripts/tool_description_optimizer.py +681 -0
- tooluniverse/compose_scripts/tool_discover.py +705 -0
- tooluniverse/compose_scripts/tool_graph_composer.py +448 -0
- tooluniverse/compose_tool.py +371 -0
- tooluniverse/ctg_tool.py +1002 -0
- tooluniverse/custom_tool.py +81 -0
- tooluniverse/dailymed_tool.py +108 -0
- tooluniverse/data/admetai_tools.json +155 -0
- tooluniverse/data/agentic_tools.json +1156 -0
- tooluniverse/data/alphafold_tools.json +87 -0
- tooluniverse/data/boltz_tools.json +9 -0
- tooluniverse/data/chembl_tools.json +16 -0
- tooluniverse/data/clait_tools.json +108 -0
- tooluniverse/data/clinicaltrials_gov_tools.json +326 -0
- tooluniverse/data/compose_tools.json +202 -0
- tooluniverse/data/dailymed_tools.json +70 -0
- tooluniverse/data/dataset_tools.json +646 -0
- tooluniverse/data/disease_target_score_tools.json +712 -0
- tooluniverse/data/efo_tools.json +17 -0
- tooluniverse/data/embedding_tools.json +319 -0
- tooluniverse/data/enrichr_tools.json +31 -0
- tooluniverse/data/europe_pmc_tools.json +22 -0
- tooluniverse/data/expert_feedback_tools.json +10 -0
- tooluniverse/data/fda_drug_adverse_event_tools.json +491 -0
- tooluniverse/data/fda_drug_labeling_tools.json +544 -168
- tooluniverse/data/fda_drugs_with_brand_generic_names_for_tool.py +76929 -148860
- tooluniverse/data/finder_tools.json +209 -0
- tooluniverse/data/gene_ontology_tools.json +113 -0
- tooluniverse/data/gwas_tools.json +1082 -0
- tooluniverse/data/hpa_tools.json +333 -0
- tooluniverse/data/humanbase_tools.json +47 -0
- tooluniverse/data/idmap_tools.json +74 -0
- tooluniverse/data/mcp_client_tools_example.json +113 -0
- tooluniverse/data/mcpautoloadertool_defaults.json +28 -0
- tooluniverse/data/medlineplus_tools.json +141 -0
- tooluniverse/data/monarch_tools.json +1 -1
- tooluniverse/data/openalex_tools.json +36 -0
- tooluniverse/data/opentarget_tools.json +82 -58
- tooluniverse/data/output_summarization_tools.json +101 -0
- tooluniverse/data/packages/bioinformatics_core_tools.json +1756 -0
- tooluniverse/data/packages/categorized_tools.txt +206 -0
- tooluniverse/data/packages/cheminformatics_tools.json +347 -0
- tooluniverse/data/packages/earth_sciences_tools.json +74 -0
- tooluniverse/data/packages/genomics_tools.json +776 -0
- tooluniverse/data/packages/image_processing_tools.json +38 -0
- tooluniverse/data/packages/machine_learning_tools.json +789 -0
- tooluniverse/data/packages/neuroscience_tools.json +62 -0
- tooluniverse/data/packages/original_tools.txt +0 -0
- tooluniverse/data/packages/physics_astronomy_tools.json +62 -0
- tooluniverse/data/packages/scientific_computing_tools.json +560 -0
- tooluniverse/data/packages/single_cell_tools.json +453 -0
- tooluniverse/data/packages/software_tools.json +4954 -0
- tooluniverse/data/packages/structural_biology_tools.json +396 -0
- tooluniverse/data/packages/visualization_tools.json +399 -0
- tooluniverse/data/pubchem_tools.json +215 -0
- tooluniverse/data/pubtator_tools.json +68 -0
- tooluniverse/data/rcsb_pdb_tools.json +1332 -0
- tooluniverse/data/reactome_tools.json +19 -0
- tooluniverse/data/semantic_scholar_tools.json +26 -0
- tooluniverse/data/special_tools.json +2 -25
- tooluniverse/data/tool_composition_tools.json +88 -0
- tooluniverse/data/toolfinderkeyword_defaults.json +34 -0
- tooluniverse/data/txagent_client_tools.json +9 -0
- tooluniverse/data/uniprot_tools.json +211 -0
- tooluniverse/data/url_fetch_tools.json +94 -0
- tooluniverse/data/uspto_downloader_tools.json +9 -0
- tooluniverse/data/uspto_tools.json +811 -0
- tooluniverse/data/xml_tools.json +3275 -0
- tooluniverse/dataset_tool.py +296 -0
- tooluniverse/default_config.py +165 -0
- tooluniverse/efo_tool.py +42 -0
- tooluniverse/embedding_database.py +630 -0
- tooluniverse/embedding_sync.py +396 -0
- tooluniverse/enrichr_tool.py +266 -0
- tooluniverse/europe_pmc_tool.py +52 -0
- tooluniverse/execute_function.py +1775 -95
- tooluniverse/extended_hooks.py +444 -0
- tooluniverse/gene_ontology_tool.py +194 -0
- tooluniverse/graphql_tool.py +158 -36
- tooluniverse/gwas_tool.py +358 -0
- tooluniverse/hpa_tool.py +1645 -0
- tooluniverse/humanbase_tool.py +389 -0
- tooluniverse/logging_config.py +254 -0
- tooluniverse/mcp_client_tool.py +764 -0
- tooluniverse/mcp_integration.py +413 -0
- tooluniverse/mcp_tool_registry.py +925 -0
- tooluniverse/medlineplus_tool.py +337 -0
- tooluniverse/openalex_tool.py +228 -0
- tooluniverse/openfda_adv_tool.py +283 -0
- tooluniverse/openfda_tool.py +393 -160
- tooluniverse/output_hook.py +1122 -0
- tooluniverse/package_tool.py +195 -0
- tooluniverse/pubchem_tool.py +158 -0
- tooluniverse/pubtator_tool.py +168 -0
- tooluniverse/rcsb_pdb_tool.py +38 -0
- tooluniverse/reactome_tool.py +108 -0
- tooluniverse/remote/boltz/boltz_mcp_server.py +50 -0
- tooluniverse/remote/depmap_24q2/depmap_24q2_mcp_tool.py +442 -0
- tooluniverse/remote/expert_feedback/human_expert_mcp_tools.py +2013 -0
- tooluniverse/remote/expert_feedback/simple_test.py +23 -0
- tooluniverse/remote/expert_feedback/start_web_interface.py +188 -0
- tooluniverse/remote/expert_feedback/web_only_interface.py +0 -0
- tooluniverse/remote/expert_feedback_mcp/human_expert_mcp_server.py +1611 -0
- tooluniverse/remote/expert_feedback_mcp/simple_test.py +34 -0
- tooluniverse/remote/expert_feedback_mcp/start_web_interface.py +91 -0
- tooluniverse/remote/immune_compass/compass_tool.py +327 -0
- tooluniverse/remote/pinnacle/pinnacle_tool.py +328 -0
- tooluniverse/remote/transcriptformer/transcriptformer_tool.py +586 -0
- tooluniverse/remote/uspto_downloader/uspto_downloader_mcp_server.py +61 -0
- tooluniverse/remote/uspto_downloader/uspto_downloader_tool.py +120 -0
- tooluniverse/remote_tool.py +99 -0
- tooluniverse/restful_tool.py +53 -30
- tooluniverse/scripts/generate_tool_graph.py +408 -0
- tooluniverse/scripts/visualize_tool_graph.py +829 -0
- tooluniverse/semantic_scholar_tool.py +62 -0
- tooluniverse/smcp.py +2452 -0
- tooluniverse/smcp_server.py +975 -0
- tooluniverse/test/mcp_server_test.py +0 -0
- tooluniverse/test/test_admetai_tool.py +370 -0
- tooluniverse/test/test_agentic_tool.py +129 -0
- tooluniverse/test/test_alphafold_tool.py +71 -0
- tooluniverse/test/test_chem_tool.py +37 -0
- tooluniverse/test/test_compose_lieraturereview.py +63 -0
- tooluniverse/test/test_compose_tool.py +448 -0
- tooluniverse/test/test_dailymed.py +69 -0
- tooluniverse/test/test_dataset_tool.py +200 -0
- tooluniverse/test/test_disease_target_score.py +56 -0
- tooluniverse/test/test_drugbank_filter_examples.py +179 -0
- tooluniverse/test/test_efo.py +31 -0
- tooluniverse/test/test_enrichr_tool.py +21 -0
- tooluniverse/test/test_europe_pmc_tool.py +20 -0
- tooluniverse/test/test_fda_adv.py +95 -0
- tooluniverse/test/test_fda_drug_labeling.py +91 -0
- tooluniverse/test/test_gene_ontology_tools.py +66 -0
- tooluniverse/test/test_gwas_tool.py +139 -0
- tooluniverse/test/test_hpa.py +625 -0
- tooluniverse/test/test_humanbase_tool.py +20 -0
- tooluniverse/test/test_idmap_tools.py +61 -0
- tooluniverse/test/test_mcp_server.py +211 -0
- tooluniverse/test/test_mcp_tool.py +247 -0
- tooluniverse/test/test_medlineplus.py +220 -0
- tooluniverse/test/test_openalex_tool.py +32 -0
- tooluniverse/test/test_opentargets.py +28 -0
- tooluniverse/test/test_pubchem_tool.py +116 -0
- tooluniverse/test/test_pubtator_tool.py +37 -0
- tooluniverse/test/test_rcsb_pdb_tool.py +86 -0
- tooluniverse/test/test_reactome.py +54 -0
- tooluniverse/test/test_semantic_scholar_tool.py +24 -0
- tooluniverse/test/test_software_tools.py +147 -0
- tooluniverse/test/test_tool_description_optimizer.py +49 -0
- tooluniverse/test/test_tool_finder.py +26 -0
- tooluniverse/test/test_tool_finder_llm.py +252 -0
- tooluniverse/test/test_tools_find.py +195 -0
- tooluniverse/test/test_uniprot_tools.py +74 -0
- tooluniverse/test/test_uspto_tool.py +72 -0
- tooluniverse/test/test_xml_tool.py +113 -0
- tooluniverse/tool_finder_embedding.py +267 -0
- tooluniverse/tool_finder_keyword.py +693 -0
- tooluniverse/tool_finder_llm.py +699 -0
- tooluniverse/tool_graph_web_ui.py +955 -0
- tooluniverse/tool_registry.py +416 -0
- tooluniverse/uniprot_tool.py +155 -0
- tooluniverse/url_tool.py +253 -0
- tooluniverse/uspto_tool.py +240 -0
- tooluniverse/utils.py +369 -41
- tooluniverse/xml_tool.py +369 -0
- tooluniverse-1.0.0.dist-info/METADATA +377 -0
- tooluniverse-1.0.0.dist-info/RECORD +186 -0
- {tooluniverse-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/WHEEL +1 -1
- tooluniverse-1.0.0.dist-info/entry_points.txt +9 -0
- tooluniverse-0.1.4.dist-info/METADATA +0 -141
- tooluniverse-0.1.4.dist-info/RECORD +0 -18
- {tooluniverse-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {tooluniverse-0.1.4.dist-info → tooluniverse-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ComposeTool - A tool that composes other tools using custom code logic.
|
|
3
|
+
Supports intelligent dependency management with automatic tool loading.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import copy
|
|
8
|
+
import traceback
|
|
9
|
+
import os
|
|
10
|
+
import importlib.util
|
|
11
|
+
import re
|
|
12
|
+
from typing import Set
|
|
13
|
+
from .base_tool import BaseTool
|
|
14
|
+
from .tool_registry import register_tool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@register_tool("ComposeTool")
|
|
18
|
+
class ComposeTool(BaseTool):
|
|
19
|
+
"""
|
|
20
|
+
A flexible tool that can compose other tools using custom code logic.
|
|
21
|
+
Supports both inline composition_code and external Python files.
|
|
22
|
+
Features intelligent dependency management with automatic tool loading.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, tool_config, tooluniverse=None):
|
|
26
|
+
super().__init__(tool_config)
|
|
27
|
+
"""
|
|
28
|
+
Initialize the ComposeTool.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
tool_config (dict): Tool configuration containing composition code or file reference
|
|
32
|
+
tooluniverse (ToolUniverse): Reference to the ToolUniverse instance
|
|
33
|
+
"""
|
|
34
|
+
self.tool_config = tool_config
|
|
35
|
+
self.name = tool_config.get("name", "unnamed_compose_tool")
|
|
36
|
+
self.tooluniverse = tooluniverse
|
|
37
|
+
|
|
38
|
+
# Configuration for dependency handling
|
|
39
|
+
self.auto_load_dependencies = tool_config.get("auto_load_dependencies", True)
|
|
40
|
+
self.required_tools = tool_config.get(
|
|
41
|
+
"required_tools", []
|
|
42
|
+
) # Explicitly specified dependencies
|
|
43
|
+
self.fail_on_missing_tools = tool_config.get("fail_on_missing_tools", False)
|
|
44
|
+
|
|
45
|
+
# Check if using external file or inline code
|
|
46
|
+
self.composition_file = tool_config.get("composition_file")
|
|
47
|
+
self.composition_function = tool_config.get("composition_function", "compose")
|
|
48
|
+
|
|
49
|
+
if self.composition_file:
|
|
50
|
+
# Load code from external file
|
|
51
|
+
self.composition_code = self._load_code_from_file()
|
|
52
|
+
else:
|
|
53
|
+
# Use inline code (existing behavior)
|
|
54
|
+
composition_code_raw = tool_config.get("composition_code", "")
|
|
55
|
+
if isinstance(composition_code_raw, list):
|
|
56
|
+
self.composition_code = "\n".join(composition_code_raw)
|
|
57
|
+
else:
|
|
58
|
+
self.composition_code = composition_code_raw
|
|
59
|
+
|
|
60
|
+
# Extract tool dependencies from code
|
|
61
|
+
self.discovered_dependencies = self._discover_tool_dependencies()
|
|
62
|
+
|
|
63
|
+
def _discover_tool_dependencies(self):
|
|
64
|
+
"""
|
|
65
|
+
Automatically discover tool dependencies from composition code.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
set: Set of tool names that this composition calls
|
|
69
|
+
"""
|
|
70
|
+
dependencies = set()
|
|
71
|
+
|
|
72
|
+
if not self.composition_code:
|
|
73
|
+
return dependencies
|
|
74
|
+
|
|
75
|
+
# Look for call_tool patterns: call_tool('ToolName', ...)
|
|
76
|
+
call_tool_pattern = r"call_tool\s*\(\s*['\"]([^'\"]+)['\"]"
|
|
77
|
+
matches = re.findall(call_tool_pattern, self.composition_code)
|
|
78
|
+
dependencies.update(matches)
|
|
79
|
+
|
|
80
|
+
# Look for tooluniverse.run_one_function patterns
|
|
81
|
+
run_function_pattern = r"tooluniverse\.run_one_function\s*\(\s*\{\s*['\"]name['\"]:\s*['\"]([^'\"]+)['\"]"
|
|
82
|
+
matches = re.findall(run_function_pattern, self.composition_code)
|
|
83
|
+
dependencies.update(matches)
|
|
84
|
+
|
|
85
|
+
return dependencies
|
|
86
|
+
|
|
87
|
+
def _get_tool_category_mapping(self):
|
|
88
|
+
"""
|
|
89
|
+
Create a mapping from tool names to their categories.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
dict: Mapping of tool names to category names
|
|
93
|
+
"""
|
|
94
|
+
tool_to_category = {}
|
|
95
|
+
|
|
96
|
+
if not self.tooluniverse:
|
|
97
|
+
return tool_to_category
|
|
98
|
+
|
|
99
|
+
# Check all tool files to build mapping
|
|
100
|
+
for category, file_path in self.tooluniverse.tool_files.items():
|
|
101
|
+
try:
|
|
102
|
+
from .execute_function import read_json_list
|
|
103
|
+
|
|
104
|
+
tools_in_category = read_json_list(file_path)
|
|
105
|
+
for tool in tools_in_category:
|
|
106
|
+
tool_name = tool.get("name")
|
|
107
|
+
if tool_name:
|
|
108
|
+
tool_to_category[tool_name] = category
|
|
109
|
+
except Exception as e:
|
|
110
|
+
print(f"Warning: Could not read tool file {file_path}: {e}")
|
|
111
|
+
|
|
112
|
+
return tool_to_category
|
|
113
|
+
|
|
114
|
+
def _load_missing_dependencies(self, missing_tools: Set[str]):
|
|
115
|
+
"""
|
|
116
|
+
Automatically load missing tool dependencies.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
missing_tools (set): Set of missing tool names
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
tuple: (successfully_loaded, failed_to_load)
|
|
123
|
+
"""
|
|
124
|
+
if not self.tooluniverse or not self.auto_load_dependencies:
|
|
125
|
+
return set(), missing_tools
|
|
126
|
+
|
|
127
|
+
tool_to_category = self._get_tool_category_mapping()
|
|
128
|
+
categories_to_load = set()
|
|
129
|
+
successfully_loaded = set()
|
|
130
|
+
failed_to_load = set()
|
|
131
|
+
|
|
132
|
+
# Determine which categories need to be loaded
|
|
133
|
+
for tool_name in missing_tools:
|
|
134
|
+
category = tool_to_category.get(tool_name)
|
|
135
|
+
if category:
|
|
136
|
+
categories_to_load.add(category)
|
|
137
|
+
else:
|
|
138
|
+
failed_to_load.add(tool_name)
|
|
139
|
+
|
|
140
|
+
# Load the required categories
|
|
141
|
+
for category in categories_to_load:
|
|
142
|
+
try:
|
|
143
|
+
print(
|
|
144
|
+
f"🔄 Auto-loading category '{category}' for ComposeTool '{self.name}'"
|
|
145
|
+
)
|
|
146
|
+
self.tooluniverse.load_tools(tool_type=[category])
|
|
147
|
+
|
|
148
|
+
# Check which tools from this category are now available
|
|
149
|
+
for tool_name in missing_tools:
|
|
150
|
+
# Check both callable_functions and all_tool_dict
|
|
151
|
+
if (
|
|
152
|
+
tool_name in self.tooluniverse.callable_functions
|
|
153
|
+
or tool_name in self.tooluniverse.all_tool_dict
|
|
154
|
+
):
|
|
155
|
+
successfully_loaded.add(tool_name)
|
|
156
|
+
|
|
157
|
+
except Exception as e:
|
|
158
|
+
print(f"❌ Failed to auto-load category '{category}': {e}")
|
|
159
|
+
|
|
160
|
+
failed_to_load = missing_tools - successfully_loaded
|
|
161
|
+
|
|
162
|
+
if successfully_loaded:
|
|
163
|
+
print(
|
|
164
|
+
f"✅ Successfully auto-loaded tools: {', '.join(successfully_loaded)}"
|
|
165
|
+
)
|
|
166
|
+
if failed_to_load:
|
|
167
|
+
print(f"❌ Failed to load tools: {', '.join(failed_to_load)}")
|
|
168
|
+
|
|
169
|
+
return successfully_loaded, failed_to_load
|
|
170
|
+
|
|
171
|
+
def _load_code_from_file(self):
|
|
172
|
+
"""
|
|
173
|
+
Load composition code from external Python file.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
str: The composition code as a string
|
|
177
|
+
"""
|
|
178
|
+
if not self.composition_file:
|
|
179
|
+
return ""
|
|
180
|
+
|
|
181
|
+
# Resolve file path relative to the tool configuration file
|
|
182
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
183
|
+
file_path = os.path.join(current_dir, "compose_scripts", self.composition_file)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Load the Python file as a module
|
|
187
|
+
spec = importlib.util.spec_from_file_location("compose_module", file_path)
|
|
188
|
+
compose_module = importlib.util.module_from_spec(spec)
|
|
189
|
+
spec.loader.exec_module(compose_module)
|
|
190
|
+
|
|
191
|
+
# Get the composition function
|
|
192
|
+
if hasattr(compose_module, self.composition_function):
|
|
193
|
+
compose_func = getattr(compose_module, self.composition_function)
|
|
194
|
+
# Extract the function code
|
|
195
|
+
import inspect
|
|
196
|
+
|
|
197
|
+
return inspect.getsource(compose_func)
|
|
198
|
+
else:
|
|
199
|
+
raise AttributeError(
|
|
200
|
+
f"Function '{self.composition_function}' not found in {self.composition_file}"
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
print(f"Error loading composition file {self.composition_file}: {e}")
|
|
205
|
+
return f"# Error loading file: {e}\nresult = {{'error': 'Failed to load composition code'}}"
|
|
206
|
+
|
|
207
|
+
def run(self, arguments):
|
|
208
|
+
"""
|
|
209
|
+
Execute the composed tool with custom code logic.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
arguments (dict): Input arguments for the composition
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Any: Result from the composition execution
|
|
216
|
+
"""
|
|
217
|
+
if not self.tooluniverse:
|
|
218
|
+
return {"error": "ToolUniverse reference is required for ComposeTool"}
|
|
219
|
+
|
|
220
|
+
if not self.composition_code:
|
|
221
|
+
return {"error": "No composition code provided"}
|
|
222
|
+
|
|
223
|
+
# Check for missing dependencies
|
|
224
|
+
all_dependencies = self.discovered_dependencies.union(set(self.required_tools))
|
|
225
|
+
missing_tools = set()
|
|
226
|
+
|
|
227
|
+
for tool_name in all_dependencies:
|
|
228
|
+
# Check both callable_functions and all_tool_dict
|
|
229
|
+
if (
|
|
230
|
+
tool_name not in self.tooluniverse.callable_functions
|
|
231
|
+
and tool_name not in self.tooluniverse.all_tool_dict
|
|
232
|
+
):
|
|
233
|
+
missing_tools.add(tool_name)
|
|
234
|
+
|
|
235
|
+
# Handle missing dependencies
|
|
236
|
+
if missing_tools:
|
|
237
|
+
if self.auto_load_dependencies:
|
|
238
|
+
print(
|
|
239
|
+
f"🔍 ComposeTool '{self.name}' detected missing dependencies: {', '.join(missing_tools)}"
|
|
240
|
+
)
|
|
241
|
+
successfully_loaded, still_missing = self._load_missing_dependencies(
|
|
242
|
+
missing_tools
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
if still_missing:
|
|
246
|
+
if self.fail_on_missing_tools:
|
|
247
|
+
return {
|
|
248
|
+
"error": f"Required tools not available: {', '.join(still_missing)}",
|
|
249
|
+
"missing_tools": list(still_missing),
|
|
250
|
+
"auto_loaded": list(successfully_loaded),
|
|
251
|
+
}
|
|
252
|
+
else:
|
|
253
|
+
print(
|
|
254
|
+
f"⚠️ Continuing execution despite missing tools: {', '.join(still_missing)}"
|
|
255
|
+
)
|
|
256
|
+
else:
|
|
257
|
+
if self.fail_on_missing_tools:
|
|
258
|
+
return {
|
|
259
|
+
"error": f"Required tools not available: {', '.join(missing_tools)}",
|
|
260
|
+
"missing_tools": list(missing_tools),
|
|
261
|
+
"auto_load_disabled": True,
|
|
262
|
+
}
|
|
263
|
+
else:
|
|
264
|
+
print(
|
|
265
|
+
f"⚠️ ComposeTool '{self.name}' has missing dependencies but continuing: {', '.join(missing_tools)}"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
if self.composition_file:
|
|
270
|
+
# Execute function from external file
|
|
271
|
+
return self._execute_from_file(arguments)
|
|
272
|
+
else:
|
|
273
|
+
# Execute inline code (existing behavior)
|
|
274
|
+
return self._execute_inline_code(arguments)
|
|
275
|
+
|
|
276
|
+
except Exception as e:
|
|
277
|
+
error_msg = f"Error in ComposeTool '{self.name}': {str(e)}"
|
|
278
|
+
traceback.print_exc() # 打印完整堆栈
|
|
279
|
+
print(f"\033[91m{error_msg}\033[0m")
|
|
280
|
+
|
|
281
|
+
return {"error": error_msg, "traceback": traceback.format_exc()}
|
|
282
|
+
|
|
283
|
+
def _execute_from_file(self, arguments):
|
|
284
|
+
"""
|
|
285
|
+
Execute composition code from external file.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
arguments (dict): Input arguments
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Any: Result from the composition execution
|
|
292
|
+
"""
|
|
293
|
+
# Resolve file path
|
|
294
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
295
|
+
file_path = os.path.join(current_dir, "compose_scripts", self.composition_file)
|
|
296
|
+
|
|
297
|
+
# Load the Python file as a module
|
|
298
|
+
spec = importlib.util.spec_from_file_location("compose_module", file_path)
|
|
299
|
+
compose_module = importlib.util.module_from_spec(spec)
|
|
300
|
+
spec.loader.exec_module(compose_module)
|
|
301
|
+
|
|
302
|
+
# Get the composition function
|
|
303
|
+
compose_func = getattr(compose_module, self.composition_function)
|
|
304
|
+
|
|
305
|
+
# Execute the function with context
|
|
306
|
+
return compose_func(arguments, self.tooluniverse, self._call_tool)
|
|
307
|
+
|
|
308
|
+
def _execute_inline_code(self, arguments):
|
|
309
|
+
"""
|
|
310
|
+
Execute inline composition code (existing behavior).
|
|
311
|
+
|
|
312
|
+
Args:
|
|
313
|
+
arguments (dict): Input arguments
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Any: Result from the composition execution
|
|
317
|
+
"""
|
|
318
|
+
# Initialize execution context
|
|
319
|
+
context = {
|
|
320
|
+
"arguments": arguments,
|
|
321
|
+
"tooluniverse": self.tooluniverse,
|
|
322
|
+
"call_tool": self._call_tool,
|
|
323
|
+
"json": json,
|
|
324
|
+
"copy": copy,
|
|
325
|
+
"result": None, # The code should set this variable
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
# Execute the composition code
|
|
329
|
+
exec_globals = {"__builtins__": __builtins__, **context}
|
|
330
|
+
|
|
331
|
+
exec(self.composition_code, exec_globals)
|
|
332
|
+
|
|
333
|
+
# Return the result variable set by the code
|
|
334
|
+
return exec_globals.get(
|
|
335
|
+
"result", {"error": "No result variable set in composition code"}
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def _call_tool(self, tool_name, arguments):
|
|
339
|
+
"""
|
|
340
|
+
Helper function to call other tools from within composition code.
|
|
341
|
+
|
|
342
|
+
Args:
|
|
343
|
+
tool_name (str): Name of the tool to call
|
|
344
|
+
arguments (dict): Arguments to pass to the tool
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Any: Result from the tool execution
|
|
348
|
+
"""
|
|
349
|
+
# Check if tool is available (check both callable_functions and all_tool_dict)
|
|
350
|
+
if (
|
|
351
|
+
tool_name not in self.tooluniverse.callable_functions
|
|
352
|
+
and tool_name not in self.tooluniverse.all_tool_dict
|
|
353
|
+
):
|
|
354
|
+
if self.auto_load_dependencies:
|
|
355
|
+
# Try to load the tool
|
|
356
|
+
missing_tools = {tool_name}
|
|
357
|
+
successfully_loaded, still_missing = self._load_missing_dependencies(
|
|
358
|
+
missing_tools
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if (
|
|
362
|
+
tool_name in still_missing
|
|
363
|
+
and tool_name not in self.tooluniverse.all_tool_dict
|
|
364
|
+
):
|
|
365
|
+
return f"Invalid function call: Function name {tool_name} not found in loaded tools."
|
|
366
|
+
else:
|
|
367
|
+
return f"Invalid function call: Function name {tool_name} not found in loaded tools."
|
|
368
|
+
|
|
369
|
+
function_call = {"name": tool_name, "arguments": arguments}
|
|
370
|
+
|
|
371
|
+
return self.tooluniverse.run_one_function(function_call)
|