#!/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