From 85e702ce25effcfb96b20c3292ab8fc172ad7f1e Mon Sep 17 00:00:00 2001 From: mirivlad Date: Tue, 17 Mar 2026 03:21:44 +0800 Subject: [PATCH] =?UTF-8?q?=D0=AD=D1=82=D0=B0=D0=BF=202:=20=D0=98=D0=BD?= =?UTF-8?q?=D1=82=D0=B5=D0=B3=D1=80=D0=B0=D1=86=D0=B8=D1=8F=20=D1=81=20qwe?= =?UTF-8?q?n-code=20=D0=B8=20opencode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot/main.py | 36 ++++++++++++++++++++--- src/tools/__init__.py | 0 src/tools/tool_runner.py | 62 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 4 deletions(-) create mode 100644 src/tools/__init__.py create mode 100644 src/tools/tool_runner.py diff --git a/src/bot/main.py b/src/bot/main.py index 2a12e74..4686b18 100644 --- a/src/bot/main.py +++ b/src/bot/main.py @@ -3,6 +3,7 @@ import logging from telegram import Update from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes from config.config import get_settings +from src.tools.tool_runner import ToolRunner logging.basicConfig( format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", @@ -11,6 +12,7 @@ logging.basicConfig( logger = logging.getLogger(__name__) settings = get_settings() +tool_runner = ToolRunner() async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -20,20 +22,44 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): ) -async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE): - await update.message.reply_text(update.message.text) - - async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): help_text = ( f"Я {settings.bot_name}, ваш ИИ-ассистент.\n\n" "Доступные команды:\n" "/start - Начать работу\n" "/help - Показать эту справку\n" + "/qwen <текст> - Задать вопрос qwen-code\n" + "/open <текст> - Задать вопрос opencode\n" ) await update.message.reply_text(help_text) +async def qwen_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + prompt = " ".join(context.args) + if not prompt: + await update.message.reply_text("Использование: /qwen <текст>") + return + + await update.message.reply_text("Думаю...") + result, success = await tool_runner.run_qwen(prompt) + await update.message.reply_text(result[:4096] if len(result) > 4096 else result) + + +async def open_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + prompt = " ".join(context.args) + if not prompt: + await update.message.reply_text("Использование: /open <текст>") + return + + await update.message.reply_text("Думаю...") + result, success = await tool_runner.run_opencode(prompt) + await update.message.reply_text(result[:4096] if len(result) > 4096 else result) + + +async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE): + await update.message.reply_text(update.message.text) + + def main(): builder = Application.builder() builder.token(settings.telegram_bot_token) @@ -46,6 +72,8 @@ def main(): application.add_handler(CommandHandler("start", start)) application.add_handler(CommandHandler("help", help_command)) + application.add_handler(CommandHandler("qwen", qwen_command)) + application.add_handler(CommandHandler("open", open_command)) application.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo)) logger.info("Бот запущен") diff --git a/src/tools/__init__.py b/src/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/tools/tool_runner.py b/src/tools/tool_runner.py new file mode 100644 index 0000000..294e8ec --- /dev/null +++ b/src/tools/tool_runner.py @@ -0,0 +1,62 @@ +import asyncio +import logging +from typing import Optional, Tuple +from config.config import get_settings + +logger = logging.getLogger(__name__) +settings = get_settings() + + +class ToolRunner: + def __init__(self): + self.qwen_command = settings.qwen_command + self.opencode_command = settings.opencode_command + self.timeout = settings.tool_timeout + + async def run_qwen(self, prompt: str) -> Tuple[str, bool]: + return await self._run_tool(self.qwen_command, prompt) + + async def run_opencode(self, prompt: str) -> Tuple[str, bool]: + return await self._run_tool(self.opencode_command, prompt) + + async def _run_tool(self, command: str, prompt: str) -> Tuple[str, bool]: + try: + process = await asyncio.create_subprocess_exec( + command, + prompt, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE + ) + + try: + stdout, stderr = await asyncio.wait_for( + process.communicate(), + timeout=self.timeout + ) + except asyncio.TimeoutError: + process.kill() + await process.wait() + return "Превышен таймаут выполнения", False + + stdout_str = stdout.decode() if stdout else "" + stderr_str = stderr.decode() if stderr else "" + + if process.returncode != 0: + error_msg = stderr_str or stdout_str + return f"Ошибка выполнения: {error_msg}", False + + return stdout_str, True + + except FileNotFoundError: + return f"Инструмент {command} не найден. Убедитесь, что он установлен и доступен в PATH.", False + except Exception as e: + logger.exception("Ошибка при выполнении инструмента") + return f"Ошибка: {str(e)}", False + + async def run_tool(self, tool_name: str, prompt: str) -> Tuple[str, bool]: + if tool_name == "qwen": + return await self.run_qwen(prompt) + elif tool_name == "opencode": + return await self.run_opencode(prompt) + else: + return f"Неизвестный инструмент: {tool_name}", False