Multi-agent AI automation system with shared message bus, specialized roles (coder/researcher/reviewer), and deny-by-default security. - Config system with Pydantic validation and YAML loading - Async message bus with inter-agent delegation - LLM providers: Anthropic (Claude) and LiteLLM (DeepSeek/Kimi/MiniMax) - Tool system: registry, builtins (file/bash/web), approval engine, MCP client - Agent engine with tool-calling loop and orchestrator for multi-agent management - CLI channel (REPL) and Discord channel - Docker + Dockge deployment config - Typer CLI: chat, serve, status, agents commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
81 lines
2.2 KiB
Python
81 lines
2.2 KiB
Python
"""Tool registry — ABC and dynamic registration."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from abc import ABC, abstractmethod
|
|
from typing import Any
|
|
|
|
from loguru import logger
|
|
|
|
|
|
class Tool(ABC):
|
|
"""Abstract base for all tools."""
|
|
|
|
@property
|
|
@abstractmethod
|
|
def name(self) -> str: ...
|
|
|
|
@property
|
|
@abstractmethod
|
|
def description(self) -> str: ...
|
|
|
|
@property
|
|
@abstractmethod
|
|
def parameters(self) -> dict[str, Any]:
|
|
"""JSON Schema for tool parameters."""
|
|
|
|
@abstractmethod
|
|
async def execute(self, **kwargs: Any) -> str:
|
|
"""Execute the tool and return a string result."""
|
|
|
|
def to_openai_schema(self) -> dict[str, Any]:
|
|
"""Convert to OpenAI function-calling format."""
|
|
return {
|
|
"type": "function",
|
|
"function": {
|
|
"name": self.name,
|
|
"description": self.description,
|
|
"parameters": self.parameters,
|
|
},
|
|
}
|
|
|
|
|
|
class ToolRegistry:
|
|
"""Manages registered tools and dispatches execution."""
|
|
|
|
def __init__(self) -> None:
|
|
self._tools: dict[str, Tool] = {}
|
|
|
|
def register(self, tool: Tool) -> None:
|
|
self._tools[tool.name] = tool
|
|
|
|
def get(self, name: str) -> Tool | None:
|
|
return self._tools.get(name)
|
|
|
|
def names(self) -> list[str]:
|
|
return list(self._tools.keys())
|
|
|
|
def get_definitions(self) -> list[dict[str, Any]]:
|
|
"""Get all tool schemas for the LLM."""
|
|
return [t.to_openai_schema() for t in self._tools.values()]
|
|
|
|
def filtered(self, allowed: list[str]) -> ToolRegistry:
|
|
"""Return a new registry containing only the specified tools."""
|
|
filtered_reg = ToolRegistry()
|
|
for name in allowed:
|
|
tool = self._tools.get(name)
|
|
if tool:
|
|
filtered_reg.register(tool)
|
|
return filtered_reg
|
|
|
|
async def execute(self, name: str, arguments: dict[str, Any]) -> str:
|
|
"""Execute a tool by name."""
|
|
tool = self._tools.get(name)
|
|
if not tool:
|
|
return f"Error: Unknown tool '{name}'"
|
|
try:
|
|
return await tool.execute(**arguments)
|
|
except Exception as e:
|
|
logger.error(f"Tool '{name}' failed: {e}")
|
|
return f"Error executing '{name}': {e}"
|