telegram-cli-bot/bot/tools/__init__.py

128 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Реестр инструментов для Telegram CLI Bot.
Инструменты - это capabilities, которые бот может использовать автономно
для выполнения задач пользователя (Agentic AI подход).
"""
import logging
from abc import ABC, abstractmethod
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, field
from datetime import datetime
logger = logging.getLogger(__name__)
@dataclass
class ToolResult:
"""Результат выполнения инструмента."""
success: bool
data: Any = None
error: Optional[str] = None
metadata: Dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> Dict:
return {
'success': self.success,
'data': self.data,
'error': self.error,
'metadata': self.metadata
}
class BaseTool(ABC):
"""Базовый класс для всех инструментов."""
name: str = "base_tool"
description: str = "Базовый инструмент"
category: str = "general"
@abstractmethod
async def execute(self, **kwargs) -> ToolResult:
"""Выполнить инструмент."""
pass
def get_capabilities(self) -> Dict:
"""Вернуть описание возможностей инструмента."""
return {
'name': self.name,
'description': self.description,
'category': self.category
}
class ToolsRegistry:
"""Реестр всех доступных инструментов."""
_instance: Optional['ToolsRegistry'] = None
_tools: Dict[str, BaseTool] = {}
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def register(self, tool: BaseTool):
"""Зарегистрировать инструмент."""
self._tools[tool.name] = tool
logger.info(f"Зарегистрирован инструмент: {tool.name}")
def unregister(self, tool_name: str):
"""От-register инструмент."""
if tool_name in self._tools:
del self._tools[tool_name]
logger.info(f"Удален инструмент: {tool_name}")
def get(self, tool_name: str) -> Optional[BaseTool]:
"""Получить инструмент по имени."""
return self._tools.get(tool_name)
def get_all(self) -> Dict[str, BaseTool]:
"""Получить все инструменты."""
return self._tools.copy()
def get_capabilities_list(self) -> List[Dict]:
"""Получить список всех возможностей для ИИ."""
return [tool.get_capabilities() for tool in self._tools.values()]
async def execute_tool(self, tool_name: str, **kwargs) -> ToolResult:
"""Выполнить инструмент по имени."""
tool = self.get(tool_name)
if not tool:
return ToolResult(
success=False,
error=f"Инструмент '{tool_name}' не найден"
)
logger.info(f"Выполнение инструмента: {tool_name} с аргументами: {kwargs}")
try:
result = await tool.execute(**kwargs)
result.metadata['tool_name'] = tool_name
result.metadata['timestamp'] = datetime.now().isoformat()
return result
except Exception as e:
logger.exception(f"Ошибка выполнения инструмента {tool_name}: {e}")
return ToolResult(
success=False,
error=str(e),
metadata={'tool_name': tool_name}
)
# Глобальный экземпляр реестра
tools_registry = ToolsRegistry()
def register_tool(tool_class: type) -> type:
"""Декоратор для автоматической регистрации инструмента."""
tool_instance = tool_class()
tools_registry.register(tool_instance)
return tool_class
# Авто-импорт инструментов для регистрации
# Импортируем после определения register_tool чтобы декоратор сработал
from bot.tools import ddgs_tool, rss_tool, ssh_tool, cron_tool, gigachat_tool