|
|
import json |
|
|
from abc import ABC, abstractmethod |
|
|
from typing import Any, Dict, Optional, Union |
|
|
|
|
|
from pydantic import BaseModel, Field |
|
|
|
|
|
from app.utils.logger import logger |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ToolResult(BaseModel): |
|
|
"""Represents the result of a tool execution.""" |
|
|
|
|
|
output: Any = Field(default=None) |
|
|
error: Optional[str] = Field(default=None) |
|
|
base64_image: Optional[str] = Field(default=None) |
|
|
system: Optional[str] = Field(default=None) |
|
|
|
|
|
class Config: |
|
|
arbitrary_types_allowed = True |
|
|
|
|
|
def __bool__(self): |
|
|
return any(getattr(self, field) for field in self.__fields__) |
|
|
|
|
|
def __add__(self, other: "ToolResult"): |
|
|
def combine_fields( |
|
|
field: Optional[str], other_field: Optional[str], concatenate: bool = True |
|
|
): |
|
|
if field and other_field: |
|
|
if concatenate: |
|
|
return field + other_field |
|
|
raise ValueError("Cannot combine tool results") |
|
|
return field or other_field |
|
|
|
|
|
return ToolResult( |
|
|
output=combine_fields(self.output, other.output), |
|
|
error=combine_fields(self.error, other.error), |
|
|
base64_image=combine_fields(self.base64_image, other.base64_image, False), |
|
|
system=combine_fields(self.system, other.system), |
|
|
) |
|
|
|
|
|
def __str__(self): |
|
|
return f"Error: {self.error}" if self.error else self.output |
|
|
|
|
|
def replace(self, **kwargs): |
|
|
"""Returns a new ToolResult with the given fields replaced.""" |
|
|
|
|
|
return type(self)(**{**self.dict(), **kwargs}) |
|
|
|
|
|
|
|
|
class BaseTool(ABC, BaseModel): |
|
|
"""Consolidated base class for all tools combining BaseModel and Tool functionality. |
|
|
|
|
|
Provides: |
|
|
- Pydantic model validation |
|
|
- Schema registration |
|
|
- Standardized result handling |
|
|
- Abstract execution interface |
|
|
|
|
|
Attributes: |
|
|
name (str): Tool name |
|
|
description (str): Tool description |
|
|
parameters (dict): Tool parameters schema |
|
|
_schemas (Dict[str, List[ToolSchema]]): Registered method schemas |
|
|
""" |
|
|
|
|
|
name: str |
|
|
description: str |
|
|
parameters: Optional[dict] = None |
|
|
|
|
|
|
|
|
class Config: |
|
|
arbitrary_types_allowed = True |
|
|
underscore_attrs_are_private = False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def __call__(self, **kwargs) -> Any: |
|
|
"""Execute the tool with given parameters.""" |
|
|
return await self.execute(**kwargs) |
|
|
|
|
|
@abstractmethod |
|
|
async def execute(self, **kwargs) -> Any: |
|
|
"""Execute the tool with given parameters.""" |
|
|
|
|
|
def to_param(self) -> Dict: |
|
|
"""Convert tool to function call format. |
|
|
|
|
|
Returns: |
|
|
Dictionary with tool metadata in OpenAI function calling format |
|
|
""" |
|
|
return { |
|
|
"type": "function", |
|
|
"function": { |
|
|
"name": self.name, |
|
|
"description": self.description, |
|
|
"parameters": self.parameters, |
|
|
}, |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def success_response(self, data: Union[Dict[str, Any], str]) -> ToolResult: |
|
|
"""Create a successful tool result. |
|
|
|
|
|
Args: |
|
|
data: Result data (dictionary or string) |
|
|
|
|
|
Returns: |
|
|
ToolResult with success=True and formatted output |
|
|
""" |
|
|
if isinstance(data, str): |
|
|
text = data |
|
|
else: |
|
|
text = json.dumps(data, indent=2) |
|
|
logger.debug(f"Created success response for {self.__class__.__name__}") |
|
|
return ToolResult(output=text) |
|
|
|
|
|
def fail_response(self, msg: str) -> ToolResult: |
|
|
"""Create a failed tool result. |
|
|
|
|
|
Args: |
|
|
msg: Error message describing the failure |
|
|
|
|
|
Returns: |
|
|
ToolResult with success=False and error message |
|
|
""" |
|
|
logger.debug(f"Tool {self.__class__.__name__} returned failed result: {msg}") |
|
|
return ToolResult(error=msg) |
|
|
|
|
|
|
|
|
class CLIResult(ToolResult): |
|
|
"""A ToolResult that can be rendered as a CLI output.""" |
|
|
|
|
|
|
|
|
class ToolFailure(ToolResult): |
|
|
"""A ToolResult that represents a failure.""" |
|
|
|