import importlib import inspect import logging from pathlib import Path from types import SimpleNamespace class ToolWrapper: """ Wraps a legacy tool function to provide a standard run() interface. """ def __init__(self, definition, func): self.definition = definition self.function = func def run(self, **kwargs): return self.function(**kwargs) def load_tools(tools_path: Path): registry = {} for file in tools_path.glob("*.py"): if file.name.startswith("_"): continue module_name = f"tools.{file.stem}" try: module = importlib.import_module(module_name) # -------------------------------------------------- # TOOL_DEFINITION is mandatory # -------------------------------------------------- if not hasattr(module, "TOOL_DEFINITION"): logging.warning(f"Tool {file.name} missing TOOL_DEFINITION, skipping") continue tool_def = module.TOOL_DEFINITION tool_name = tool_def.get("name", file.stem) # -------------------------------------------------- # Preferred: explicit run() # -------------------------------------------------- if hasattr(module, "run") and callable(module.run): registry[tool_name] = { "definition": tool_def, "function": module.run, } logging.info(f"Loaded tool (run): {tool_name}") continue # -------------------------------------------------- # Legacy auto-wrap (single public function) # -------------------------------------------------- public_funcs = [ obj for name, obj in inspect.getmembers(module, inspect.isfunction) if not name.startswith("_") ] if len(public_funcs) == 1: wrapped = ToolWrapper(tool_def, public_funcs[0]) registry[tool_name] = { "definition": tool_def, "function": wrapped.run, } logging.info(f"Wrapped legacy tool: {tool_name}") continue # -------------------------------------------------- # Failure case # -------------------------------------------------- logging.error( f"Tool {file.name} has no run() and multiple public functions; skipping" ) except Exception: logging.error(f"Failed to load tool {file.name}", exc_info=True) return registry