2024-10-23 09:28:12 +08:00
|
|
|
"""
|
2024-11-18 04:20:16 +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
|
|
|
"""
|
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
from typing import Dict, Any, Union, Optional, List
|
|
|
|
from dataclasses import dataclass, field
|
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-11-18 08:38:31 +08:00
|
|
|
from enum import Enum
|
|
|
|
|
|
|
|
class SchemaType(Enum):
|
|
|
|
OPENAPI = "openapi"
|
|
|
|
XML = "xml"
|
|
|
|
CUSTOM = "custom"
|
|
|
|
|
2024-11-18 09:15:34 +08:00
|
|
|
@dataclass
|
|
|
|
class XMLNodeMapping:
|
|
|
|
"""Maps an XML node (element or attribute) to a function parameter"""
|
|
|
|
param_name: str # Name of the function parameter
|
|
|
|
node_type: str = "element" # "element", "attribute", or "content"
|
|
|
|
path: str = "." # XPath-like path to the node, "." means root element
|
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
@dataclass
|
|
|
|
class XMLTagSchema:
|
2024-11-18 09:15:34 +08:00
|
|
|
"""Schema for XML tool tags with improved node mapping"""
|
|
|
|
tag_name: str # Root tag name (e.g. "str-replace")
|
|
|
|
mappings: List[XMLNodeMapping] = field(default_factory=list)
|
|
|
|
description: Optional[str] = None
|
|
|
|
|
|
|
|
def add_mapping(self, param_name: str, node_type: str = "element", path: str = ".") -> None:
|
|
|
|
"""Add a new node mapping"""
|
|
|
|
self.mappings.append(XMLNodeMapping(
|
|
|
|
param_name=param_name,
|
|
|
|
node_type=node_type,
|
|
|
|
path=path
|
|
|
|
))
|
2024-11-18 08:38:31 +08:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class ToolSchema:
|
|
|
|
"""Container for tool schemas with type information"""
|
|
|
|
schema_type: SchemaType
|
|
|
|
schema: Dict[str, Any]
|
|
|
|
xml_schema: Optional[XMLTagSchema] = None
|
2024-10-06 01:04:15 +08:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class ToolResult:
|
2024-11-18 04:20:16 +08:00
|
|
|
"""Container for tool execution results."""
|
2024-10-06 01:04:15 +08:00
|
|
|
success: bool
|
|
|
|
output: str
|
|
|
|
|
|
|
|
class Tool(ABC):
|
2024-11-18 08:38:31 +08:00
|
|
|
"""Abstract base class for all tools."""
|
2024-11-18 04:20:16 +08:00
|
|
|
|
2024-10-06 01:04:15 +08:00
|
|
|
def __init__(self):
|
2024-11-18 08:38:31 +08:00
|
|
|
self._schemas: Dict[str, List[ToolSchema]] = {}
|
2024-10-23 09:28:12 +08:00
|
|
|
self._register_schemas()
|
|
|
|
|
|
|
|
def _register_schemas(self):
|
2024-11-18 04:20:16 +08:00
|
|
|
"""Register schemas from all decorated methods."""
|
2024-10-23 09:28:12 +08:00
|
|
|
for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
|
2024-11-18 08:38:31 +08:00
|
|
|
if hasattr(method, 'tool_schemas'):
|
|
|
|
self._schemas[name] = method.tool_schemas
|
2024-10-06 01:04:15 +08:00
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
def get_schemas(self) -> Dict[str, List[ToolSchema]]:
|
2024-11-18 04:20:16 +08:00
|
|
|
"""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:
|
2024-11-18 04:20:16 +08:00
|
|
|
"""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:
|
2024-11-18 04:20:16 +08:00
|
|
|
"""Create a failed tool result."""
|
2024-10-06 01:04:15 +08:00
|
|
|
return ToolResult(success=False, output=msg)
|
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
def _add_schema(func, schema: ToolSchema):
|
|
|
|
"""Helper to add schema to a function."""
|
|
|
|
if not hasattr(func, 'tool_schemas'):
|
|
|
|
func.tool_schemas = []
|
|
|
|
func.tool_schemas.append(schema)
|
|
|
|
return func
|
|
|
|
|
|
|
|
def openapi_schema(schema: Dict[str, Any]):
|
|
|
|
"""Decorator for OpenAPI schema tools."""
|
|
|
|
def decorator(func):
|
|
|
|
return _add_schema(func, ToolSchema(
|
|
|
|
schema_type=SchemaType.OPENAPI,
|
|
|
|
schema=schema
|
|
|
|
))
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
def xml_schema(
|
|
|
|
tag_name: str,
|
2024-11-18 09:15:34 +08:00
|
|
|
mappings: List[Dict[str, str]] = None,
|
|
|
|
description: str = None
|
2024-11-18 08:38:31 +08:00
|
|
|
):
|
|
|
|
"""
|
2024-11-18 09:15:34 +08:00
|
|
|
Decorator for XML schema tools with improved node mapping.
|
2024-11-18 04:20:16 +08:00
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
Args:
|
|
|
|
tag_name: Name of the root XML tag
|
2024-11-18 09:15:34 +08:00
|
|
|
mappings: List of mapping definitions, each containing:
|
|
|
|
- param_name: Name of the function parameter
|
|
|
|
- node_type: "element", "attribute", or "content"
|
|
|
|
- path: Path to the node (default "." for root)
|
|
|
|
description: Optional description of the tool
|
2024-11-18 04:20:16 +08:00
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
Example:
|
|
|
|
@xml_schema(
|
|
|
|
tag_name="str-replace",
|
2024-11-18 09:15:34 +08:00
|
|
|
mappings=[
|
|
|
|
{"param_name": "file_path", "node_type": "attribute", "path": "."},
|
|
|
|
{"param_name": "old_str", "node_type": "element", "path": "old_str"},
|
|
|
|
{"param_name": "new_str", "node_type": "element", "path": "new_str"}
|
|
|
|
],
|
|
|
|
description="Replace text in a file"
|
2024-11-18 08:38:31 +08:00
|
|
|
)
|
2024-10-23 09:28:12 +08:00
|
|
|
"""
|
|
|
|
def decorator(func):
|
2024-11-18 09:15:34 +08:00
|
|
|
xml_schema = XMLTagSchema(tag_name=tag_name, description=description)
|
|
|
|
|
|
|
|
# Add mappings
|
|
|
|
if mappings:
|
|
|
|
for mapping in mappings:
|
|
|
|
xml_schema.add_mapping(
|
|
|
|
param_name=mapping["param_name"],
|
|
|
|
node_type=mapping.get("node_type", "element"),
|
|
|
|
path=mapping.get("path", ".")
|
|
|
|
)
|
|
|
|
|
2024-11-18 08:38:31 +08:00
|
|
|
return _add_schema(func, ToolSchema(
|
|
|
|
schema_type=SchemaType.XML,
|
2024-11-18 09:15:34 +08:00
|
|
|
schema={}, # OpenAPI schema could be added here if needed
|
|
|
|
xml_schema=xml_schema
|
2024-11-18 08:38:31 +08:00
|
|
|
))
|
|
|
|
return decorator
|
|
|
|
|
|
|
|
def custom_schema(schema: Dict[str, Any]):
|
|
|
|
"""Decorator for custom schema tools."""
|
|
|
|
def decorator(func):
|
|
|
|
return _add_schema(func, ToolSchema(
|
|
|
|
schema_type=SchemaType.CUSTOM,
|
|
|
|
schema=schema
|
|
|
|
))
|
2024-10-23 09:28:12 +08:00
|
|
|
return decorator
|