Rebase onto upstream (a4d95fd) #12

Closed
wylab wants to merge 144 commits from rebase-onto-upstream into main
2 changed files with 107 additions and 2 deletions
Showing only changes of commit 926a3a7526 - Show all commits
+13 -2
View File
@@ -32,8 +32,19 @@ class ToolRegistry:
return name in self._tools
def get_definitions(self) -> list[dict[str, Any]]:
"""Get all tool definitions in OpenAI format."""
return [tool.to_schema() for tool in self._tools.values()]
"""Get tool definitions for all registered tools.
Supports both function tools (with to_schema) and native tools (with to_params).
"""
definitions = []
for tool in self._tools.values():
if hasattr(tool, 'to_params'): # Native Anthropic tool
definitions.append(tool.to_params())
elif hasattr(tool, 'to_schema'): # Function tool
definitions.append(tool.to_schema())
else:
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."""
+94
View File
@@ -0,0 +1,94 @@
"""Tests for registry duck typing support."""
import pytest
from nanobot.agent.tools.registry import ToolRegistry
from nanobot.agent.tools.anthropic.base import BaseAnthropicTool, ToolResult
class MockNativeTool(BaseAnthropicTool):
"""Mock native tool for testing."""
api_type = "test_20250227"
name = "native_test"
beta_flag = "test-beta"
async def __call__(self, **kwargs):
return ToolResult(output="native result")
def to_params(self):
return {"type": self.api_type, "name": self.name}
class MockFunctionTool:
"""Mock function tool for testing."""
def __init__(self):
self.name = "function_test"
def to_schema(self):
return {
"type": "function",
"function": {
"name": self.name,
"description": "Test function tool",
"parameters": {"type": "object", "properties": {}}
}
}
async def execute(self, **kwargs):
return "function result"
def test_registry_supports_native_tools():
"""Test registry can register and get definitions from native tools."""
registry = ToolRegistry()
native_tool = MockNativeTool()
registry.register(native_tool)
definitions = registry.get_definitions()
assert len(definitions) == 1
assert definitions[0]["type"] == "test_20250227"
assert definitions[0]["name"] == "native_test"
def test_registry_supports_function_tools():
"""Test registry still supports function tools."""
registry = ToolRegistry()
function_tool = MockFunctionTool()
registry.register(function_tool)
definitions = registry.get_definitions()
assert len(definitions) == 1
assert definitions[0]["type"] == "function"
assert definitions[0]["function"]["name"] == "function_test"
def test_registry_supports_mixed_tools():
"""Test registry can handle both native and function tools."""
registry = ToolRegistry()
native_tool = MockNativeTool()
function_tool = MockFunctionTool()
registry.register(native_tool)
registry.register(function_tool)
definitions = registry.get_definitions()
assert len(definitions) == 2
# Find each tool type in definitions
native_def = next(d for d in definitions if d.get("type") == "test_20250227")
function_def = next(d for d in definitions if d.get("type") == "function")
assert native_def["name"] == "native_test"
assert function_def["function"]["name"] == "function_test"
def test_registry_rejects_tools_without_schema_method():
"""Test registry raises error for tools with no schema method."""
registry = ToolRegistry()
class BadTool:
name = "bad"
registry.register(BadTool())
with pytest.raises(ValueError, match="has no schema method"):
registry.get_definitions()