onetool-mcp 1.0.0b1__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.
- bench/__init__.py +5 -0
- bench/cli.py +69 -0
- bench/harness/__init__.py +66 -0
- bench/harness/client.py +692 -0
- bench/harness/config.py +397 -0
- bench/harness/csv_writer.py +109 -0
- bench/harness/evaluate.py +512 -0
- bench/harness/metrics.py +283 -0
- bench/harness/runner.py +899 -0
- bench/py.typed +0 -0
- bench/reporter.py +629 -0
- bench/run.py +487 -0
- bench/secrets.py +101 -0
- bench/utils.py +16 -0
- onetool/__init__.py +4 -0
- onetool/cli.py +391 -0
- onetool/py.typed +0 -0
- onetool_mcp-1.0.0b1.dist-info/METADATA +163 -0
- onetool_mcp-1.0.0b1.dist-info/RECORD +132 -0
- onetool_mcp-1.0.0b1.dist-info/WHEEL +4 -0
- onetool_mcp-1.0.0b1.dist-info/entry_points.txt +3 -0
- onetool_mcp-1.0.0b1.dist-info/licenses/LICENSE.txt +687 -0
- onetool_mcp-1.0.0b1.dist-info/licenses/NOTICE.txt +64 -0
- ot/__init__.py +37 -0
- ot/__main__.py +6 -0
- ot/_cli.py +107 -0
- ot/_tui.py +53 -0
- ot/config/__init__.py +46 -0
- ot/config/defaults/bench.yaml +4 -0
- ot/config/defaults/diagram-templates/api-flow.mmd +33 -0
- ot/config/defaults/diagram-templates/c4-context.puml +30 -0
- ot/config/defaults/diagram-templates/class-diagram.mmd +87 -0
- ot/config/defaults/diagram-templates/feature-mindmap.mmd +70 -0
- ot/config/defaults/diagram-templates/microservices.d2 +81 -0
- ot/config/defaults/diagram-templates/project-gantt.mmd +37 -0
- ot/config/defaults/diagram-templates/state-machine.mmd +42 -0
- ot/config/defaults/onetool.yaml +25 -0
- ot/config/defaults/prompts.yaml +97 -0
- ot/config/defaults/servers.yaml +7 -0
- ot/config/defaults/snippets.yaml +4 -0
- ot/config/defaults/tool_templates/__init__.py +7 -0
- ot/config/defaults/tool_templates/extension.py +52 -0
- ot/config/defaults/tool_templates/isolated.py +61 -0
- ot/config/dynamic.py +121 -0
- ot/config/global_templates/__init__.py +2 -0
- ot/config/global_templates/bench-secrets-template.yaml +6 -0
- ot/config/global_templates/bench.yaml +9 -0
- ot/config/global_templates/onetool.yaml +27 -0
- ot/config/global_templates/secrets-template.yaml +44 -0
- ot/config/global_templates/servers.yaml +18 -0
- ot/config/global_templates/snippets.yaml +235 -0
- ot/config/loader.py +1087 -0
- ot/config/mcp.py +145 -0
- ot/config/secrets.py +190 -0
- ot/config/tool_config.py +125 -0
- ot/decorators.py +116 -0
- ot/executor/__init__.py +35 -0
- ot/executor/base.py +16 -0
- ot/executor/fence_processor.py +83 -0
- ot/executor/linter.py +142 -0
- ot/executor/pack_proxy.py +260 -0
- ot/executor/param_resolver.py +140 -0
- ot/executor/pep723.py +288 -0
- ot/executor/result_store.py +369 -0
- ot/executor/runner.py +496 -0
- ot/executor/simple.py +163 -0
- ot/executor/tool_loader.py +396 -0
- ot/executor/validator.py +398 -0
- ot/executor/worker_pool.py +388 -0
- ot/executor/worker_proxy.py +189 -0
- ot/http_client.py +145 -0
- ot/logging/__init__.py +37 -0
- ot/logging/config.py +315 -0
- ot/logging/entry.py +213 -0
- ot/logging/format.py +188 -0
- ot/logging/span.py +349 -0
- ot/meta.py +1555 -0
- ot/paths.py +453 -0
- ot/prompts.py +218 -0
- ot/proxy/__init__.py +21 -0
- ot/proxy/manager.py +396 -0
- ot/py.typed +0 -0
- ot/registry/__init__.py +189 -0
- ot/registry/models.py +57 -0
- ot/registry/parser.py +269 -0
- ot/registry/registry.py +413 -0
- ot/server.py +315 -0
- ot/shortcuts/__init__.py +15 -0
- ot/shortcuts/aliases.py +87 -0
- ot/shortcuts/snippets.py +258 -0
- ot/stats/__init__.py +35 -0
- ot/stats/html.py +250 -0
- ot/stats/jsonl_writer.py +283 -0
- ot/stats/reader.py +354 -0
- ot/stats/timing.py +57 -0
- ot/support.py +63 -0
- ot/tools.py +114 -0
- ot/utils/__init__.py +81 -0
- ot/utils/batch.py +161 -0
- ot/utils/cache.py +120 -0
- ot/utils/deps.py +403 -0
- ot/utils/exceptions.py +23 -0
- ot/utils/factory.py +179 -0
- ot/utils/format.py +65 -0
- ot/utils/http.py +202 -0
- ot/utils/platform.py +45 -0
- ot/utils/sanitize.py +130 -0
- ot/utils/truncate.py +69 -0
- ot_tools/__init__.py +4 -0
- ot_tools/_convert/__init__.py +12 -0
- ot_tools/_convert/excel.py +279 -0
- ot_tools/_convert/pdf.py +254 -0
- ot_tools/_convert/powerpoint.py +268 -0
- ot_tools/_convert/utils.py +358 -0
- ot_tools/_convert/word.py +283 -0
- ot_tools/brave_search.py +604 -0
- ot_tools/code_search.py +736 -0
- ot_tools/context7.py +495 -0
- ot_tools/convert.py +614 -0
- ot_tools/db.py +415 -0
- ot_tools/diagram.py +1604 -0
- ot_tools/diagram.yaml +167 -0
- ot_tools/excel.py +1372 -0
- ot_tools/file.py +1348 -0
- ot_tools/firecrawl.py +732 -0
- ot_tools/grounding_search.py +646 -0
- ot_tools/package.py +604 -0
- ot_tools/py.typed +0 -0
- ot_tools/ripgrep.py +544 -0
- ot_tools/scaffold.py +471 -0
- ot_tools/transform.py +213 -0
- ot_tools/web_fetch.py +384 -0
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
"""Tool loading and discovery for command execution.
|
|
2
|
+
|
|
3
|
+
Handles:
|
|
4
|
+
- Loading tool functions from config-defined tool files
|
|
5
|
+
- Caching based on file modification times
|
|
6
|
+
- Pack extraction from tool modules
|
|
7
|
+
- PEP 723 detection for routing to worker processes
|
|
8
|
+
|
|
9
|
+
Used by the runner to make tools available during code execution.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import importlib.util
|
|
15
|
+
import sys
|
|
16
|
+
from collections import OrderedDict
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import TYPE_CHECKING, Any
|
|
20
|
+
|
|
21
|
+
from loguru import logger
|
|
22
|
+
|
|
23
|
+
from ot.executor.pep723 import ToolFileInfo, categorize_tools
|
|
24
|
+
from ot.executor.worker_proxy import create_worker_proxy
|
|
25
|
+
from ot.paths import get_effective_cwd
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _get_bundled_tools_dir() -> Path | None:
|
|
29
|
+
"""Get the bundled tools directory from the ot_tools package.
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Path to ot_tools package directory, or None if not found.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
import ot_tools
|
|
36
|
+
|
|
37
|
+
return Path(ot_tools.__file__).parent
|
|
38
|
+
except (ImportError, AttributeError):
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
if TYPE_CHECKING:
|
|
43
|
+
from ot.config.loader import OneToolConfig
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class LoadedTools:
|
|
48
|
+
"""Registry of loaded tool functions with pack support.
|
|
49
|
+
|
|
50
|
+
The functions dict uses full pack-qualified names as keys (e.g., "brave.search")
|
|
51
|
+
to avoid collisions when multiple packs have functions with the same name.
|
|
52
|
+
The packs dict provides grouped access by pack.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
functions: dict[str, Any] # Full name -> callable (e.g., "brave.search" -> func)
|
|
56
|
+
packs: dict[str, dict[str, Any]] # Nested: pack -> {name -> callable}
|
|
57
|
+
worker_tools: list[ToolFileInfo] = field(
|
|
58
|
+
default_factory=list
|
|
59
|
+
) # Tools using workers
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# Module cache: stores (LoadedTools, mtime_dict) for each tools_dir
|
|
63
|
+
# Uses OrderedDict for LRU eviction with bounded size
|
|
64
|
+
_MODULE_CACHE_MAXSIZE = 16
|
|
65
|
+
_module_cache: OrderedDict[Path, tuple[LoadedTools, dict[str, float]]] = OrderedDict()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _cache_get(key: Path) -> tuple[LoadedTools, dict[str, float]] | None:
|
|
69
|
+
"""Get from cache with LRU update."""
|
|
70
|
+
if key in _module_cache:
|
|
71
|
+
_module_cache.move_to_end(key)
|
|
72
|
+
return _module_cache[key]
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _cache_set(key: Path, value: tuple[LoadedTools, dict[str, float]]) -> None:
|
|
77
|
+
"""Set in cache with LRU eviction."""
|
|
78
|
+
if key in _module_cache:
|
|
79
|
+
_module_cache.move_to_end(key)
|
|
80
|
+
_module_cache[key] = value
|
|
81
|
+
while len(_module_cache) > _MODULE_CACHE_MAXSIZE:
|
|
82
|
+
_module_cache.popitem(last=False)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _get_tool_files(
|
|
86
|
+
tools_dir: Path | None, config: OneToolConfig | None
|
|
87
|
+
) -> tuple[set[Path], set[Path], Path]:
|
|
88
|
+
"""Resolve tool files from config, bundled package, or directory.
|
|
89
|
+
|
|
90
|
+
Always includes bundled tools from ot_tools package, plus any
|
|
91
|
+
additional tools from config or explicit tools_dir.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
tools_dir: Explicit tools directory path.
|
|
95
|
+
config: Loaded configuration (may be None).
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Tuple of (all tool file paths, internal tool paths, cache key).
|
|
99
|
+
"""
|
|
100
|
+
tool_files: list[Path] = []
|
|
101
|
+
internal_files: set[Path] = set()
|
|
102
|
+
|
|
103
|
+
# Always include bundled tools from ot_tools package
|
|
104
|
+
bundled_dir = _get_bundled_tools_dir()
|
|
105
|
+
if bundled_dir and bundled_dir.exists():
|
|
106
|
+
bundled_files = [f for f in bundled_dir.glob("*.py") if f.name != "__init__.py"]
|
|
107
|
+
tool_files.extend(bundled_files)
|
|
108
|
+
# Mark bundled tools as internal (shipped with OneTool)
|
|
109
|
+
internal_files = {f.resolve() for f in bundled_files if f.exists()}
|
|
110
|
+
logger.debug(f"Found {len(bundled_files)} bundled tools from {bundled_dir}")
|
|
111
|
+
|
|
112
|
+
# Add config-specified tools (these are extension tools, not internal)
|
|
113
|
+
config_tool_files = config.get_tool_files() if config else []
|
|
114
|
+
if config_tool_files:
|
|
115
|
+
tool_files.extend(config_tool_files)
|
|
116
|
+
cache_key = Path("__config__")
|
|
117
|
+
elif tools_dir is not None:
|
|
118
|
+
# Explicit tools_dir provided - use it
|
|
119
|
+
if tools_dir.exists():
|
|
120
|
+
tools_dir = tools_dir.resolve()
|
|
121
|
+
tool_files.extend(tools_dir.glob("*.py"))
|
|
122
|
+
cache_key = tools_dir
|
|
123
|
+
else:
|
|
124
|
+
cache_key = Path("__bundled__")
|
|
125
|
+
|
|
126
|
+
if not tool_files:
|
|
127
|
+
return set(), set(), Path("__no_tools__")
|
|
128
|
+
|
|
129
|
+
current_files = {f.resolve() for f in tool_files if f.exists()}
|
|
130
|
+
return current_files, internal_files, cache_key
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _check_cache(cache_key: Path, current_files: set[Path]) -> LoadedTools | None:
|
|
134
|
+
"""Return cached registry if valid, None if stale or missing.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
cache_key: Key for cache lookup.
|
|
138
|
+
current_files: Set of current tool file paths.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Cached LoadedTools if valid, None otherwise.
|
|
142
|
+
"""
|
|
143
|
+
cached = _cache_get(cache_key)
|
|
144
|
+
if cached is None:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
cached_registry, cached_mtimes = cached
|
|
148
|
+
cached_files = {Path(f) for f in cached_mtimes}
|
|
149
|
+
|
|
150
|
+
if current_files != cached_files:
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
for py_file in current_files:
|
|
154
|
+
try:
|
|
155
|
+
if py_file.stat().st_mtime != cached_mtimes.get(str(py_file), 0):
|
|
156
|
+
return None
|
|
157
|
+
except OSError:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
return cached_registry
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _load_worker_tools(
|
|
164
|
+
worker_tools: list[ToolFileInfo],
|
|
165
|
+
config_dict: dict[str, Any],
|
|
166
|
+
secrets: dict[str, Any],
|
|
167
|
+
packs: dict[str, dict[str, Any]],
|
|
168
|
+
mtimes: dict[str, float],
|
|
169
|
+
) -> tuple[dict[str, Any], list[ToolFileInfo]]:
|
|
170
|
+
"""Load PEP 723 tools via worker proxies.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
worker_tools: List of tool file info for extension tools.
|
|
174
|
+
config_dict: Configuration as dict.
|
|
175
|
+
secrets: Secrets dict.
|
|
176
|
+
packs: Packs dict to populate.
|
|
177
|
+
mtimes: Modification times dict to populate.
|
|
178
|
+
|
|
179
|
+
Returns:
|
|
180
|
+
Tuple of (functions dict, loaded extension tools list).
|
|
181
|
+
"""
|
|
182
|
+
functions: dict[str, Any] = {}
|
|
183
|
+
loaded_workers: list[ToolFileInfo] = []
|
|
184
|
+
|
|
185
|
+
for tool_info in worker_tools:
|
|
186
|
+
py_file = tool_info.path
|
|
187
|
+
try:
|
|
188
|
+
mtimes[str(py_file)] = py_file.stat().st_mtime
|
|
189
|
+
|
|
190
|
+
pack = tool_info.pack
|
|
191
|
+
if pack and pack in packs:
|
|
192
|
+
logger.warning(
|
|
193
|
+
f"Pack collision: '{pack}' already defined, "
|
|
194
|
+
f"merging functions from {py_file.stem}"
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
proxy = create_worker_proxy(
|
|
198
|
+
tool_path=py_file,
|
|
199
|
+
functions=tool_info.functions,
|
|
200
|
+
config=config_dict,
|
|
201
|
+
secrets=secrets,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if pack:
|
|
205
|
+
if pack not in packs:
|
|
206
|
+
packs[pack] = {}
|
|
207
|
+
packs[pack] = proxy # type: ignore[assignment]
|
|
208
|
+
for func_name in tool_info.functions:
|
|
209
|
+
full_name = f"{pack}.{func_name}"
|
|
210
|
+
functions[full_name] = getattr(proxy, func_name)
|
|
211
|
+
else:
|
|
212
|
+
for func_name in tool_info.functions:
|
|
213
|
+
functions[func_name] = getattr(proxy, func_name)
|
|
214
|
+
|
|
215
|
+
loaded_workers.append(tool_info)
|
|
216
|
+
logger.debug(
|
|
217
|
+
f"Loaded extension tool {py_file.stem} with {len(tool_info.functions)} functions"
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.warning(f"Failed to load extension tool {py_file.stem}: {e}")
|
|
222
|
+
|
|
223
|
+
return functions, loaded_workers
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _load_inprocess_tools(
|
|
227
|
+
inprocess_tools: list[ToolFileInfo],
|
|
228
|
+
packs: dict[str, dict[str, Any]],
|
|
229
|
+
mtimes: dict[str, float],
|
|
230
|
+
) -> dict[str, Any]:
|
|
231
|
+
"""Load regular Python tools via importlib.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
inprocess_tools: List of tool file info for in-process tools.
|
|
235
|
+
packs: Packs dict to populate.
|
|
236
|
+
mtimes: Modification times dict to populate.
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
Functions dict with loaded tools.
|
|
240
|
+
"""
|
|
241
|
+
functions: dict[str, Any] = {}
|
|
242
|
+
|
|
243
|
+
for tool_info in inprocess_tools:
|
|
244
|
+
py_file = tool_info.path
|
|
245
|
+
module_name = f"tools.{py_file.stem}"
|
|
246
|
+
|
|
247
|
+
try:
|
|
248
|
+
mtimes[str(py_file)] = py_file.stat().st_mtime
|
|
249
|
+
|
|
250
|
+
spec = importlib.util.spec_from_file_location(module_name, py_file)
|
|
251
|
+
if spec is None or spec.loader is None:
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
module = importlib.util.module_from_spec(spec)
|
|
255
|
+
sys.modules[module_name] = module
|
|
256
|
+
spec.loader.exec_module(module)
|
|
257
|
+
|
|
258
|
+
pack = getattr(module, "pack", None)
|
|
259
|
+
if pack and pack in packs:
|
|
260
|
+
logger.warning(
|
|
261
|
+
f"Pack collision: '{pack}' already defined, "
|
|
262
|
+
f"merging functions from {py_file.stem}"
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
export_names = getattr(module, "__all__", None)
|
|
266
|
+
if export_names is None:
|
|
267
|
+
export_names = [n for n in dir(module) if not n.startswith("_")]
|
|
268
|
+
|
|
269
|
+
for name in export_names:
|
|
270
|
+
obj = getattr(module, name, None)
|
|
271
|
+
if obj is not None and callable(obj) and not isinstance(obj, type):
|
|
272
|
+
if pack:
|
|
273
|
+
if pack not in packs:
|
|
274
|
+
packs[pack] = {}
|
|
275
|
+
packs[pack][name] = obj
|
|
276
|
+
full_name = f"{pack}.{name}"
|
|
277
|
+
functions[full_name] = obj
|
|
278
|
+
else:
|
|
279
|
+
functions[name] = obj
|
|
280
|
+
|
|
281
|
+
except Exception as e:
|
|
282
|
+
logger.warning(f"Failed to load tool module {py_file.stem}: {e}")
|
|
283
|
+
|
|
284
|
+
return functions
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def load_tool_registry(tools_dir: Path | None = None) -> LoadedTools:
|
|
288
|
+
"""Load all tool functions from config tool files with pack support.
|
|
289
|
+
|
|
290
|
+
Uses caching based on file modification times to avoid redundant loading.
|
|
291
|
+
Reads `pack` module variable from each tool file to group functions.
|
|
292
|
+
|
|
293
|
+
Tool loading strategy:
|
|
294
|
+
- Internal tools (bundled with OneTool from ot_tools package): Run in-process
|
|
295
|
+
via importlib. These tools have no PEP 723 headers and use ot.* imports.
|
|
296
|
+
- Extension tools (user-created without PEP 723 headers): Run in-process
|
|
297
|
+
with full ot.* access (logging, config, inter-tool calling).
|
|
298
|
+
- Isolated tools (user-created with PEP 723 headers): Run in worker
|
|
299
|
+
subprocesses with isolated dependencies. Fully standalone, no ot imports.
|
|
300
|
+
|
|
301
|
+
The core 'ot' pack (from meta.py) is always registered regardless of config.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
tools_dir: Explicit path to tools directory. If not provided,
|
|
305
|
+
tool files are loaded from config. If neither is available,
|
|
306
|
+
only the core 'ot' pack will be available.
|
|
307
|
+
|
|
308
|
+
Returns:
|
|
309
|
+
LoadedTools with functions dict (pack-qualified keys) and packs dict.
|
|
310
|
+
"""
|
|
311
|
+
from ot.config.loader import get_config
|
|
312
|
+
from ot.config.secrets import get_secrets
|
|
313
|
+
|
|
314
|
+
config = get_config()
|
|
315
|
+
current_files, internal_files, cache_key = _get_tool_files(tools_dir, config)
|
|
316
|
+
|
|
317
|
+
if not current_files:
|
|
318
|
+
return LoadedTools(functions={}, packs={})
|
|
319
|
+
|
|
320
|
+
cached = _check_cache(cache_key, current_files)
|
|
321
|
+
if cached is not None:
|
|
322
|
+
return cached
|
|
323
|
+
|
|
324
|
+
logger.debug(f"Loading tools from {cache_key} ({len(current_files)} files)")
|
|
325
|
+
|
|
326
|
+
packs: dict[str, dict[str, Any]] = {}
|
|
327
|
+
mtimes: dict[str, float] = {}
|
|
328
|
+
|
|
329
|
+
# Categorize tools: internal (bundled) vs extension (user-created)
|
|
330
|
+
# Internal tools run in-process, extension tools with PEP 723 use workers
|
|
331
|
+
worker_tools, inprocess_tools = categorize_tools(
|
|
332
|
+
list(current_files), internal_files
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
secrets_path = config.get_secrets_file_path() if config else None
|
|
336
|
+
secrets = get_secrets(secrets_path)
|
|
337
|
+
config_dict = config.model_dump() if config else {}
|
|
338
|
+
|
|
339
|
+
# Inject path context for isolated worker tools
|
|
340
|
+
config_dict["_project_path"] = str(get_effective_cwd())
|
|
341
|
+
if config and config._config_dir:
|
|
342
|
+
config_dict["_config_dir"] = str(config._config_dir)
|
|
343
|
+
|
|
344
|
+
worker_funcs, worker_tools_list = _load_worker_tools(
|
|
345
|
+
worker_tools, config_dict, secrets, packs, mtimes
|
|
346
|
+
)
|
|
347
|
+
inprocess_funcs = _load_inprocess_tools(inprocess_tools, packs, mtimes)
|
|
348
|
+
|
|
349
|
+
functions = {**worker_funcs, **inprocess_funcs}
|
|
350
|
+
|
|
351
|
+
# Register the core 'ot' pack from meta.py (not loaded from tools_dir)
|
|
352
|
+
ot_funcs = _register_ot_pack(packs)
|
|
353
|
+
functions.update(ot_funcs)
|
|
354
|
+
|
|
355
|
+
registry = LoadedTools(
|
|
356
|
+
functions=functions, packs=packs, worker_tools=worker_tools_list
|
|
357
|
+
)
|
|
358
|
+
_cache_set(cache_key, (registry, mtimes))
|
|
359
|
+
|
|
360
|
+
return registry
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _register_ot_pack(packs: dict[str, dict[str, Any]]) -> dict[str, Any]:
|
|
364
|
+
"""Register the core 'ot' pack from ot.meta module.
|
|
365
|
+
|
|
366
|
+
The 'ot' pack provides introspection functions (tools, packs, config, etc.)
|
|
367
|
+
and is always available regardless of tools_dir configuration.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
packs: Packs dict to add 'ot' pack to.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Functions dict with ot.* entries.
|
|
374
|
+
"""
|
|
375
|
+
from ot.meta import PACK_NAME, get_ot_pack_functions
|
|
376
|
+
|
|
377
|
+
ot_functions = get_ot_pack_functions()
|
|
378
|
+
packs[PACK_NAME] = ot_functions
|
|
379
|
+
|
|
380
|
+
# Build full function names
|
|
381
|
+
return {f"{PACK_NAME}.{name}": func for name, func in ot_functions.items()}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def load_tool_functions(tools_dir: Path | None = None) -> dict[str, Any]:
|
|
385
|
+
"""Load all tool functions from the tools directory.
|
|
386
|
+
|
|
387
|
+
Uses caching based on file modification times to avoid redundant loading.
|
|
388
|
+
|
|
389
|
+
Args:
|
|
390
|
+
tools_dir: Explicit path to tools directory. If not provided,
|
|
391
|
+
tool files are loaded from config.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
Dictionary mapping function names to callable functions.
|
|
395
|
+
"""
|
|
396
|
+
return load_tool_registry(tools_dir).functions
|