Rebase onto upstream (a4d95fd)
#12
@@ -46,24 +46,39 @@ class ToolRegistry:
|
||||
raise ValueError(f"Tool {tool.name} has no schema method (to_params or to_schema)")
|
||||
return definitions
|
||||
|
||||
async def execute(self, name: str, params: dict[str, Any]) -> str:
|
||||
"""Execute a tool by name with given parameters."""
|
||||
_HINT = "\n\n[Analyze the error above and try a different approach.]"
|
||||
async def execute(self, name: str, params: dict[str, Any]) -> Any:
|
||||
"""
|
||||
Execute a tool by name with given parameters.
|
||||
|
||||
Supports both native Anthropic tools (via __call__) and function tools (via execute).
|
||||
|
||||
Args:
|
||||
name: Tool name.
|
||||
params: Tool parameters.
|
||||
|
||||
Returns:
|
||||
Tool execution result (ToolResult, CLIResult, or string).
|
||||
|
||||
Raises:
|
||||
KeyError: If tool not found.
|
||||
"""
|
||||
tool = self._tools.get(name)
|
||||
if not tool:
|
||||
return f"Error: Tool '{name}' not found. Available: {', '.join(self.tool_names)}"
|
||||
return f"Error: Tool '{name}' not found"
|
||||
|
||||
try:
|
||||
errors = tool.validate_params(params)
|
||||
if errors:
|
||||
return f"Error: Invalid parameters for tool '{name}': " + "; ".join(errors) + _HINT
|
||||
result = await tool.execute(**params)
|
||||
if isinstance(result, str) and result.startswith("Error"):
|
||||
return result + _HINT
|
||||
return result
|
||||
# Duck typing - support both native and function tools
|
||||
if hasattr(tool, 'to_params'):
|
||||
# Native Anthropic tool - call directly via __call__, no validation needed
|
||||
return await tool(**params)
|
||||
else:
|
||||
# Legacy function tool - validate then execute
|
||||
errors = tool.validate_params(params)
|
||||
if errors:
|
||||
return f"Error: Invalid parameters for tool '{name}': " + "; ".join(errors)
|
||||
return await tool.execute(**params)
|
||||
except Exception as e:
|
||||
return f"Error executing {name}: {str(e)}" + _HINT
|
||||
return f"Error executing {name}: {str(e)}"
|
||||
|
||||
@property
|
||||
def tool_names(self) -> list[str]:
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
"""Tests for registry execution of native tools."""
|
||||
|
||||
import pytest
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from nanobot.agent.tools.registry import ToolRegistry
|
||||
from nanobot.agent.tools.anthropic import BashTool20250124, EditTool20250728
|
||||
from nanobot.agent.tools.anthropic.base import ToolResult, CLIResult
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_registry_executes_bash_tool():
|
||||
"""Test registry can execute BashTool20250124 and returns ToolResult."""
|
||||
registry = ToolRegistry()
|
||||
registry.register(BashTool20250124())
|
||||
|
||||
result = await registry.execute("bash", {"command": "echo 'test'"})
|
||||
|
||||
assert isinstance(result, ToolResult)
|
||||
assert result.output is not None
|
||||
assert "test" in result.output
|
||||
assert result.error is None
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_registry_executes_edit_tool():
|
||||
"""Test registry can execute EditTool20250728 and returns CLIResult."""
|
||||
registry = ToolRegistry()
|
||||
registry.register(EditTool20250728())
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
test_file = str(Path(tmpdir) / "test.txt")
|
||||
|
||||
result = await registry.execute("str_replace_editor", {
|
||||
"command": "create",
|
||||
"path": test_file,
|
||||
"file_text": "Hello, world!"
|
||||
})
|
||||
|
||||
assert isinstance(result, CLIResult)
|
||||
assert "created" in result.output.lower() or "success" in result.output.lower()
|
||||
assert Path(test_file).exists()
|
||||
assert Path(test_file).read_text() == "Hello, world!"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_registry_mixed_tools():
|
||||
"""Test registry can execute both native and function tools in same registry."""
|
||||
registry = ToolRegistry()
|
||||
|
||||
# Register native tool
|
||||
registry.register(BashTool20250124())
|
||||
|
||||
# Execute native tool
|
||||
result = await registry.execute("bash", {"command": "echo 'native'"})
|
||||
assert isinstance(result, ToolResult)
|
||||
assert "native" in result.output
|
||||
Reference in New Issue
Block a user