Rebase onto upstream (a4d95fd) #12

Closed
wylab wants to merge 144 commits from rebase-onto-upstream into main
5 changed files with 123 additions and 0 deletions
Showing only changes of commit 10f48acd4a - Show all commits
+15
View File
@@ -0,0 +1,15 @@
"""Anthropic native tools implementation."""
from nanobot.agent.tools.anthropic.base import (
BaseAnthropicTool,
ToolResult,
CLIResult,
ToolError,
)
__all__ = [
"BaseAnthropicTool",
"ToolResult",
"CLIResult",
"ToolError",
]
+66
View File
@@ -0,0 +1,66 @@
"""Base classes for Anthropic native tools.
Ported from anthropic-quickstarts/computer-use-demo.
"""
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
from typing import Any
@dataclass(kw_only=True, frozen=True)
class ToolResult:
"""Result from tool execution.
Structured result that can contain text output, errors, images, and system messages.
"""
output: str | None = None
error: str | None = None
base64_image: str | None = None
system: str | None = None
@dataclass(kw_only=True, frozen=True)
class CLIResult:
"""Result from CLI-style tools (like text editor).
Similar to ToolResult but simpler for text-only tools.
"""
output: str
class ToolError(Exception):
"""Exception raised by tool execution."""
pass
class BaseAnthropicTool(metaclass=ABCMeta):
"""Base class for Anthropic native tools.
Native tools are version-coupled to model training and don't require schemas.
"""
api_type: str # e.g., "bash_20250124"
name: str # e.g., "bash"
beta_flag: str | None = None # e.g., "computer-use-2025-11-24"
@abstractmethod
async def __call__(self, **kwargs: Any) -> ToolResult | CLIResult:
"""Execute the tool.
Args:
**kwargs: Tool-specific parameters
Returns:
ToolResult or CLIResult with execution output
"""
...
@abstractmethod
def to_params(self) -> dict[str, Any]:
"""Return tool definition for API.
Returns:
Dict with type and name (no schema for native tools)
"""
...
+42
View File
@@ -0,0 +1,42 @@
"""Tests for Anthropic native tool base classes."""
import pytest
from nanobot.agent.tools.anthropic.base import BaseAnthropicTool, ToolResult
class DummyTool(BaseAnthropicTool):
"""Test tool implementation."""
api_type = "test_20250227"
name = "test_tool"
beta_flag = "test-beta"
async def __call__(self, **kwargs):
return ToolResult(output="test output")
def to_params(self):
return {"type": self.api_type, "name": self.name}
def test_tool_result_dataclass():
"""Test ToolResult can be created with fields."""
result = ToolResult(output="hello", error=None)
assert result.output == "hello"
assert result.error is None
assert result.base64_image is None
def test_base_anthropic_tool_to_params():
"""Test tool returns correct params format."""
tool = DummyTool()
params = tool.to_params()
assert params["type"] == "test_20250227"
assert params["name"] == "test_tool"
@pytest.mark.asyncio
async def test_base_anthropic_tool_call():
"""Test tool can be called and returns ToolResult."""
tool = DummyTool()
result = await tool()
assert isinstance(result, ToolResult)
assert result.output == "test output"