Skip to content

Commit 9c031e5

Browse files
authored
Merge pull request #3 from ShinyRou/feature/windows-tool-load
fix: dynamically load tools from directory
2 parents 2104964 + 3c01885 commit 9c031e5

1 file changed

Lines changed: 40 additions & 42 deletions

File tree

agentmesh/tools/tool_manager.py

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import importlib
2+
import importlib.util
23
from pathlib import Path
34
from typing import Dict
45
from agentmesh.tools.base_tool import BaseTool
@@ -28,7 +29,7 @@ def __init__(self):
2829
def load_tools(self, tools_dir: str = "agentmesh/tools"):
2930
"""
3031
Load tools from both directory and configuration.
31-
32+
3233
:param tools_dir: Directory to scan for tool modules
3334
"""
3435
# First, load tools from directory (for backward compatibility)
@@ -38,52 +39,49 @@ def load_tools(self, tools_dir: str = "agentmesh/tools"):
3839
self._configure_tools_from_config()
3940

4041
print(f"Loaded {len(self.tools)} tools: {', '.join(self.tools.keys())}")
41-
42+
4243
def _load_tools_from_directory(self, tools_dir: str):
43-
"""Dynamically load tools from directory"""
44+
"""Dynamically load tools from directory using file loading"""
4445
tools_path = Path(tools_dir)
45-
for py_file in tools_path.rglob("*.py"): # Use rglob to recursively find .py files
46+
47+
# Traverse all .py files
48+
for py_file in tools_path.rglob("*.py"):
49+
# Skip initialization files and base tool files
4650
if py_file.name in ["__init__.py", "base_tool.py", "tool_manager.py"]:
4751
continue
4852

49-
# Construct the module name based on the relative path
50-
plugin_name = py_file.stem
51-
module_name = str(py_file.relative_to(Path(tools_dir).parent)).replace("/", ".").replace(".py", "")
52-
# print(f"plugin_name: {plugin_name}, module_name: {module_name}")
53+
# Get module name
54+
module_name = py_file.stem
5355

54-
# Import using the corrected module name
5556
try:
56-
module = importlib.import_module(f"agentmesh.{module_name}") # Ensure the correct base package
57-
except ModuleNotFoundError as e:
58-
# If browser_use dependency is missing, silently ignore
59-
if "browser_use" in str(e):
60-
# Optional: Print a more friendly message
61-
# print(f"Skipping optional tool {module_name}: {e}")
62-
continue
63-
# Other import errors are printed
64-
print(f"Error importing module {module_name}: {e}")
65-
continue
66-
67-
for attr_name in dir(module):
68-
cls = getattr(module, attr_name)
69-
if (
70-
isinstance(cls, type)
71-
and issubclass(cls, BaseTool)
72-
and cls != BaseTool
73-
):
74-
try:
75-
tool_instance = cls()
76-
self.tools[tool_instance.name] = tool_instance
77-
except TypeError as e:
78-
print(f"Error initializing tool {cls.__name__}: {e}")
79-
except ImportError as e:
80-
# Catch tool initialization import errors
81-
if "browser_use" in str(e):
82-
# Optional: Print a more friendly message
83-
# print(f"Skipping optional tool {cls.__name__}: {e}")
84-
pass
85-
else:
86-
print(f"Error initializing tool {cls.__name__}: {e}")
57+
# Load module directly from file
58+
spec = importlib.util.spec_from_file_location(module_name, py_file)
59+
if spec and spec.loader:
60+
module = importlib.util.module_from_spec(spec)
61+
spec.loader.exec_module(module)
62+
63+
# Find tool classes in the module
64+
for attr_name in dir(module):
65+
cls = getattr(module, attr_name)
66+
if (
67+
isinstance(cls, type)
68+
and issubclass(cls, BaseTool)
69+
and cls != BaseTool
70+
):
71+
try:
72+
# Instantiate tool and add to tool dictionary
73+
tool_instance = cls()
74+
self.tools[tool_instance.name] = tool_instance
75+
except ImportError as e:
76+
# Ignore browser_use dependency missing errors
77+
if "browser_use" in str(e):
78+
pass
79+
else:
80+
print(f"Error initializing tool {cls.__name__}: {e}")
81+
except Exception as e:
82+
print(f"Error initializing tool {cls.__name__}: {e}")
83+
except Exception as e:
84+
print(f"Error importing module {py_file}: {e}")
8785

8886
def _configure_tools_from_config(self):
8987
"""Configure tools based on configuration file"""
@@ -120,7 +118,7 @@ def _configure_tools_from_config(self):
120118
def get_tool(self, name: str) -> BaseTool:
121119
"""
122120
Get a tool by name.
123-
121+
124122
:param name: The name of the tool to get.
125123
:return: The tool instance or None if not found.
126124
"""
@@ -129,7 +127,7 @@ def get_tool(self, name: str) -> BaseTool:
129127
def list_tools(self) -> dict:
130128
"""
131129
Get information about all loaded tools.
132-
130+
133131
:return: A dictionary with tool information.
134132
"""
135133
return {

0 commit comments

Comments
 (0)