mirror of
https://github.com/AstrBotDevs/AstrBot
synced 2026-07-16 01:40:15 +08:00
This commit adds the _internal package structure for AstrBot's standardized MCP & Skills support: astrbot/_internal/mcp/: - MCPClient for MCP server connections - MCPTool wrapper for MCP tools - MCP configuration management astrbot/_internal/skills/: - SkillManager for skill lifecycle - Skill parser and loader - SkillToToolConverter for tool-based skills - Prompt builder for skills astrbot/_internal/tools/: - ToolSchema, FunctionTool, ToolSet base definitions - FunctionToolManager for tool registry - Builtin tools (cron, send_message, kb_query) - Tool providers (internal, plugin, computer) astrbot/api/: - Public API for tools (ToolRegistry, tool decorator) - Public API for MCP (get_mcp_servers, register_mcp_server) - Public API for skills (get_skill_manager, skill_to_tool)
111 lines
3.2 KiB
Python
111 lines
3.2 KiB
Python
"""
|
|
Tools Public API for AstrBot.
|
|
|
|
Example:
|
|
from astrbot.api.tools import tool, get_registry
|
|
|
|
@tool(name="weather", description="Get weather", parameters={...})
|
|
async def get_weather(city: str) -> str:
|
|
return f"Weather in {city} is sunny"
|
|
|
|
registry = get_registry()
|
|
tools = registry.list_tools()
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Awaitable, Callable
|
|
from functools import wraps
|
|
from typing import Any
|
|
|
|
from astrbot.core.agent.tool import FunctionTool
|
|
|
|
__all__ = ["FunctionTool", "ToolRegistry", "get_registry", "tool"]
|
|
|
|
|
|
class ToolRegistry:
|
|
"""Simple wrapper around FunctionToolManager for tool registration."""
|
|
|
|
_instance: ToolRegistry | None = None
|
|
|
|
def __init__(self) -> None:
|
|
from astrbot.core.provider.register import llm_tools as func_tool_manager
|
|
|
|
self._manager = func_tool_manager
|
|
|
|
@classmethod
|
|
def get_instance(cls) -> ToolRegistry:
|
|
"""Get the singleton ToolRegistry instance."""
|
|
if cls._instance is None:
|
|
cls._instance = cls()
|
|
return cls._instance
|
|
|
|
def register(self, tool: FunctionTool) -> None:
|
|
"""Register a FunctionTool."""
|
|
self._manager.func_list.append(tool)
|
|
|
|
def unregister(self, name: str) -> bool:
|
|
"""Unregister a tool by name. Returns True if found and removed."""
|
|
for i, f in enumerate(self._manager.func_list):
|
|
if f.name == name:
|
|
self._manager.func_list.pop(i)
|
|
return True
|
|
return False
|
|
|
|
def list_tools(self) -> list[FunctionTool]:
|
|
"""List all registered tools."""
|
|
return self._manager.func_list.copy()
|
|
|
|
def get_tool(self, name: str) -> FunctionTool | None:
|
|
"""Get a tool by name."""
|
|
return self._manager.get_func(name)
|
|
|
|
|
|
def get_registry() -> ToolRegistry:
|
|
"""Get the global ToolRegistry instance."""
|
|
return ToolRegistry.get_instance()
|
|
|
|
|
|
def tool(
|
|
name: str,
|
|
description: str,
|
|
parameters: dict[str, Any] | None = None,
|
|
) -> Callable[
|
|
[Callable[..., Awaitable[str | None]]], Callable[..., Awaitable[str | None]]
|
|
]:
|
|
"""Decorator to register an async function as a tool.
|
|
|
|
Args:
|
|
name: Tool name (used by LLM to invoke it)
|
|
description: What the tool does
|
|
parameters: JSON Schema for parameters (optional)
|
|
|
|
Example:
|
|
@tool(name="weather", description="Get weather for a city", parameters={...})
|
|
async def get_weather(city: str) -> str:
|
|
return f"The weather in {city} is sunny"
|
|
"""
|
|
if parameters is None:
|
|
parameters = {"type": "object", "properties": {}}
|
|
|
|
def decorator(
|
|
func: Callable[..., Awaitable[str | None]],
|
|
) -> Callable[..., Awaitable[str | None]]:
|
|
func_tool = FunctionTool(
|
|
name=name,
|
|
description=description,
|
|
parameters=parameters,
|
|
handler=func,
|
|
handler_module_path=getattr(func, "__module__", ""),
|
|
source="api",
|
|
)
|
|
get_registry().register(func_tool)
|
|
|
|
@wraps(func)
|
|
async def wrapper(*args: Any, **kwargs: Any) -> str | None:
|
|
return await func(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
return decorator
|