suna/agentpress/tool.py

156 lines
5.2 KiB
Python
Raw Normal View History

2024-10-23 09:28:12 +08:00
"""
This module provides a flexible foundation for creating and managing tools in the AgentPress system.
2024-10-23 09:28:12 +08:00
The tool system allows for easy creation of function-like tools that can be used by AI models.
It provides a way to define any schema format for these tools, making it compatible with
various AI model interfaces and custom implementations.
2024-10-23 09:28:12 +08:00
Key components:
- ToolResult: A dataclass representing the result of a tool execution
- Tool: An abstract base class that all tools should inherit from
- tool_schema: A decorator for defining tool schemas in any format
2024-10-23 09:28:12 +08:00
Usage:
1. Create a new tool by subclassing Tool
2. Define methods and decorate them with @tool_schema
3. The Tool class will automatically register these schemas
4. Use the tool in your ThreadManager
2024-10-23 09:28:12 +08:00
Example OpenAPI Schema:
2024-11-02 07:25:30 +08:00
class CalculatorTool(Tool):
2024-10-23 09:28:12 +08:00
@tool_schema({
"type": "function",
"function": {
"name": "divide",
"description": "Divide two numbers",
"parameters": {
"type": "object",
"properties": {
"a": {"type": "number", "description": "Numerator"},
"b": {"type": "number", "description": "Denominator"}
},
"required": ["a", "b"]
}
2024-10-23 09:28:12 +08:00
}
})
2024-11-02 07:25:30 +08:00
async def divide(self, a: float, b: float) -> ToolResult:
if b == 0:
return self.fail_response("Cannot divide by zero")
return self.success_response(f"Result: {a/b}")
2024-10-23 09:28:12 +08:00
Example Custom Schema:
class DeliveryTool(Tool):
@tool_schema({
"name": "get_delivery_date",
"description": "Get delivery date for order",
"input_format": "order_id: string",
"output_format": "delivery_date: ISO-8601 date string",
"examples": [
{"input": "ORD123", "output": "2024-03-25"}
],
"error_handling": {
"invalid_order": "Returns error if order not found",
"system_error": "Returns error on system failures"
}
})
async def get_delivery_date(self, order_id: str) -> ToolResult:
date = await self.fetch_delivery_date(order_id)
return self.success_response(date)
2024-10-23 09:28:12 +08:00
"""
2024-11-02 08:01:26 +08:00
from typing import Dict, Any, Union
2024-10-06 01:04:15 +08:00
from dataclasses import dataclass
2024-10-23 09:28:12 +08:00
from abc import ABC
2024-10-06 01:04:15 +08:00
import json
2024-10-23 09:28:12 +08:00
import inspect
2024-10-06 01:04:15 +08:00
@dataclass
class ToolResult:
"""Container for tool execution results."""
2024-10-06 01:04:15 +08:00
success: bool
output: str
class Tool(ABC):
"""Abstract base class for all tools.
This class provides the foundation for creating tools with flexible schema definitions.
Subclasses can implement specific tool methods and define schemas in any format.
2024-10-23 09:28:12 +08:00
"""
2024-10-06 01:04:15 +08:00
def __init__(self):
2024-10-23 09:28:12 +08:00
self._schemas = {}
self._register_schemas()
def _register_schemas(self):
"""Register schemas from all decorated methods."""
2024-10-23 09:28:12 +08:00
for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
if hasattr(method, 'schema'):
self._schemas[name] = method.schema
2024-10-06 01:04:15 +08:00
def get_schemas(self) -> Dict[str, Dict[str, Any]]:
"""Get all registered tool schemas."""
2024-10-23 09:28:12 +08:00
return self._schemas
2024-10-06 01:04:15 +08:00
2024-11-02 08:01:26 +08:00
def success_response(self, data: Union[Dict[str, Any], str]) -> ToolResult:
"""Create a successful tool result."""
2024-10-06 01:04:15 +08:00
if isinstance(data, str):
text = data
else:
text = json.dumps(data, indent=2)
return ToolResult(success=True, output=text)
def fail_response(self, msg: str) -> ToolResult:
"""Create a failed tool result."""
2024-10-06 01:04:15 +08:00
return ToolResult(success=False, output=msg)
2024-10-23 09:28:12 +08:00
def tool_schema(schema: Dict[str, Any]):
"""Decorator for defining tool schemas.
Allows attaching any schema format to tool methods. The schema can follow
any specification (OpenAPI, custom format, etc.) as long as it's serializable
to JSON.
Examples:
# OpenAPI-style schema
2024-10-23 09:28:12 +08:00
@tool_schema({
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather forecast",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
}
}
2024-10-23 09:28:12 +08:00
}
})
# Custom schema format
@tool_schema({
"tool_name": "analyze_sentiment",
"input": {
"text": "string - Text to analyze",
"language": "optional string - Language code"
},
"output": {
"sentiment": "string - Positive/Negative/Neutral",
"confidence": "float - Confidence score"
},
"error_cases": [
"invalid_language",
"text_too_long"
]
})
# Minimal schema
@tool_schema({
"name": "ping",
"description": "Check if service is alive"
})
2024-10-23 09:28:12 +08:00
"""
def decorator(func):
func.schema = schema
2024-10-23 09:28:12 +08:00
return func
return decorator