299 lines
12 KiB
Python
299 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Обработчики команд для переключения AI-пресетов.
|
||
|
||
Доступные пресеты:
|
||
- off: ИИ отключен, режим CLI команд
|
||
- qwen: Qwen Code (бесплатно, локально)
|
||
- giga_auto: GigaChat авто-переключение (Lite/Pro)
|
||
- giga_lite: GigaChat Lite (дешевле)
|
||
- giga_pro: GigaChat Pro (максимальное качество)
|
||
"""
|
||
|
||
import logging
|
||
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
|
||
from telegram.ext import CommandHandler, CallbackQueryHandler
|
||
|
||
from bot.models.user_state import (
|
||
AI_PRESET_OFF,
|
||
AI_PRESET_QWEN,
|
||
AI_PRESET_GIGA_AUTO,
|
||
AI_PRESET_GIGA_LITE,
|
||
AI_PRESET_GIGA_PRO,
|
||
AI_PRESET_GIGA_MAX,
|
||
AI_PRESET_OPENCODE,
|
||
)
|
||
from bot.config import state_manager
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# Описание пресетов
|
||
PRESET_DESCRIPTIONS = {
|
||
AI_PRESET_OFF: {
|
||
"name": "❌ ИИ Отключен",
|
||
"description": "Режим CLI команд. Бот выполняет команды напрямую.",
|
||
"icon": "⌨️"
|
||
},
|
||
AI_PRESET_QWEN: {
|
||
"name": "🤖 Qwen Code",
|
||
"description": "Бесплатно, локально. Лучший для кода и работы с файлами.",
|
||
"icon": "💻"
|
||
},
|
||
AI_PRESET_GIGA_AUTO: {
|
||
"name": "🔄 GigaChat Авто",
|
||
"description": "Умное переключение Lite/Pro. Простые → Lite, сложные → Pro.",
|
||
"icon": "🧠"
|
||
},
|
||
AI_PRESET_GIGA_LITE: {
|
||
"name": "⚡ GigaChat Lite",
|
||
"description": "Быстро и дёшево. Для простых вопросов и чата.",
|
||
"icon": "🚀"
|
||
},
|
||
AI_PRESET_GIGA_PRO: {
|
||
"name": "🔥 GigaChat Pro",
|
||
"description": "Максимальное качество. Для сложных творческих задач.",
|
||
"icon": "👑"
|
||
},
|
||
AI_PRESET_GIGA_MAX: {
|
||
"name": "💎 GigaChat Max",
|
||
"description": "Топовая модель для самых сложных задач.",
|
||
"icon": "💎"
|
||
},
|
||
AI_PRESET_OPENCODE: {
|
||
"name": "⚡ Opencode",
|
||
"description": "Бесплатные модели (minimax, big-pickle, gpt-5-nano).",
|
||
"icon": "🚀"
|
||
},
|
||
}
|
||
|
||
|
||
def get_preset_display_name(preset: str) -> str:
|
||
"""Получить отображаемое имя пресета."""
|
||
desc = PRESET_DESCRIPTIONS.get(preset, {})
|
||
return f"{desc.get('icon', '❓')} {desc.get('name', preset)}"
|
||
|
||
|
||
async def ai_presets_command(update: Update, context):
|
||
"""Показать меню выбора AI-пресета."""
|
||
user_id = update.effective_user.id
|
||
state = state_manager.get(user_id)
|
||
current_preset = state.ai_preset
|
||
|
||
# Формируем меню - Opencode и GigaChat теперь открывают подменю выбора моделей
|
||
keyboard = [
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if current_preset == AI_PRESET_OFF else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_OFF]['icon']} ИИ Отключен",
|
||
callback_data=f"ai_preset_{AI_PRESET_OFF}"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if current_preset == AI_PRESET_QWEN else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_QWEN]['icon']} Qwen Code",
|
||
callback_data=f"ai_preset_{AI_PRESET_QWEN}"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if current_preset == AI_PRESET_GIGA_AUTO else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_AUTO]['icon']} GigaChat Авто",
|
||
callback_data=f"ai_preset_{AI_PRESET_GIGA_AUTO}"
|
||
)
|
||
],
|
||
# Кнопка GigaChat с подменю - убираем Lite и Pro из основного меню
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if current_preset in [AI_PRESET_GIGA_LITE, AI_PRESET_GIGA_PRO, AI_PRESET_GIGA_MAX] else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_LITE]['icon']} GigaChat ▶",
|
||
callback_data="gigachat_submenu"
|
||
)
|
||
],
|
||
# Кнопка Opencode с подменю
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if current_preset == AI_PRESET_OPENCODE else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_OPENCODE]['icon']} Opencode ▶",
|
||
callback_data="opencode_submenu"
|
||
)
|
||
],
|
||
]
|
||
|
||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||
|
||
current_name = get_preset_display_name(current_preset)
|
||
|
||
output = f"🎛️ **Панель управления AI**\n\n"
|
||
output += f"**Текущий пресет:** {current_name}\n\n"
|
||
output += "Выберите AI-провайдер:\n\n"
|
||
output += "ℹ️ **Описание пресетов:**\n"
|
||
output += f"• {PRESET_DESCRIPTIONS[AI_PRESET_OFF]['icon']} **ИИ Отключен** — {PRESET_DESCRIPTIONS[AI_PRESET_OFF]['description']}\n"
|
||
output += f"• {PRESET_DESCRIPTIONS[AI_PRESET_QWEN]['icon']} **Qwen Code** — {PRESET_DESCRIPTIONS[AI_PRESET_QWEN]['description']}\n"
|
||
output += f"• {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_AUTO]['icon']} **GigaChat Авто** — {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_AUTO]['description']}\n"
|
||
output += f"• {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_LITE]['icon']} **GigaChat** — {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_LITE]['description']}\n"
|
||
output += f"• {PRESET_DESCRIPTIONS[AI_PRESET_OPENCODE]['icon']} **Opencode** — {PRESET_DESCRIPTIONS[AI_PRESET_OPENCODE]['description']}"
|
||
|
||
await update.message.reply_text(output, parse_mode="Markdown", reply_markup=reply_markup)
|
||
|
||
|
||
async def ai_preset_callback(update: Update, context):
|
||
"""Обработка выбора пресета из инлайн-меню."""
|
||
user_id = update.effective_user.id
|
||
query = update.callback_query
|
||
await query.answer()
|
||
|
||
# Извлекаем название пресета из callback_data
|
||
preset = query.data.replace("ai_preset_", "")
|
||
|
||
if preset not in PRESET_DESCRIPTIONS:
|
||
await query.edit_message_text("❌ Неверный пресет")
|
||
return
|
||
|
||
state = state_manager.get(user_id)
|
||
old_preset = state.ai_preset
|
||
state.ai_preset = preset
|
||
|
||
# Обновляем ai_chat_mode и current_ai_provider для совместимости
|
||
if preset == AI_PRESET_OFF:
|
||
state.ai_chat_mode = False
|
||
state.current_ai_provider = "none"
|
||
else:
|
||
state.ai_chat_mode = True
|
||
# Для совместимости с существующим кодом
|
||
if preset == AI_PRESET_QWEN:
|
||
state.current_ai_provider = "qwen"
|
||
elif preset == AI_PRESET_OPENCODE:
|
||
state.current_ai_provider = "opencode"
|
||
else: # Любой GigaChat
|
||
state.current_ai_provider = "gigachat"
|
||
|
||
preset_name = get_preset_display_name(preset)
|
||
|
||
output = f"✅ **Переключено на:** {preset_name}\n\n"
|
||
output += f"{PRESET_DESCRIPTIONS[preset]['description']}"
|
||
|
||
# Обновляем инлайн-меню - с подменю для Opencode и GigaChat
|
||
keyboard = [
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if preset == AI_PRESET_OFF else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_OFF]['icon']} ИИ Отключен",
|
||
callback_data=f"ai_preset_{AI_PRESET_OFF}"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if preset == AI_PRESET_QWEN else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_QWEN]['icon']} Qwen Code",
|
||
callback_data=f"ai_preset_{AI_PRESET_QWEN}"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if preset == AI_PRESET_GIGA_AUTO else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_AUTO]['icon']} GigaChat Авто",
|
||
callback_data=f"ai_preset_{AI_PRESET_GIGA_AUTO}"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if preset in [AI_PRESET_GIGA_LITE, AI_PRESET_GIGA_PRO, AI_PRESET_GIGA_MAX] else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_GIGA_LITE]['icon']} GigaChat ▶",
|
||
callback_data="gigachat_submenu"
|
||
)
|
||
],
|
||
[
|
||
InlineKeyboardButton(
|
||
f"{'✅' if preset == AI_PRESET_OPENCODE else '⬜'} {PRESET_DESCRIPTIONS[AI_PRESET_OPENCODE]['icon']} Opencode ▶",
|
||
callback_data="opencode_submenu"
|
||
)
|
||
],
|
||
]
|
||
|
||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||
await query.edit_message_text(output, parse_mode="Markdown", reply_markup=reply_markup)
|
||
|
||
logger.info(f"Пользователь {user_id} переключил AI-пресет: {old_preset} → {preset}")
|
||
|
||
|
||
# Быстрые команды для переключения одним сообщением
|
||
async def ai_off_command(update: Update, context):
|
||
"""Быстрое переключение на ИИ отключен."""
|
||
await switch_preset(update, AI_PRESET_OFF)
|
||
|
||
|
||
async def ai_qwen_command(update: Update, context):
|
||
"""Быстрое переключение на Qwen Code."""
|
||
await switch_preset(update, AI_PRESET_QWEN)
|
||
|
||
|
||
async def ai_giga_auto_command(update: Update, context):
|
||
"""Быстрое переключение на GigaChat Авто."""
|
||
await switch_preset(update, AI_PRESET_GIGA_AUTO)
|
||
|
||
|
||
async def ai_giga_lite_command(update: Update, context):
|
||
"""Быстрое переключение на GigaChat Lite."""
|
||
await switch_preset(update, AI_PRESET_GIGA_LITE)
|
||
|
||
|
||
async def ai_giga_pro_command(update: Update, context):
|
||
"""Быстрое переключение на GigaChat Pro."""
|
||
await switch_preset(update, AI_PRESET_GIGA_PRO)
|
||
|
||
|
||
async def ai_giga_max_command(update: Update, context):
|
||
"""Быстрое переключение на GigaChat Max."""
|
||
await switch_preset(update, AI_PRESET_GIGA_MAX)
|
||
|
||
|
||
async def ai_opencode_command(update: Update, context):
|
||
"""Быстрое переключение на Opencode."""
|
||
await switch_preset(update, AI_PRESET_OPENCODE)
|
||
|
||
|
||
async def switch_preset(update: Update, preset: str):
|
||
"""Переключить пресет и показать уведомление."""
|
||
user_id = update.effective_user.id
|
||
state = state_manager.get(user_id)
|
||
old_preset = state.ai_preset
|
||
state.ai_preset = preset
|
||
|
||
# Обновляем совместимость
|
||
if preset == AI_PRESET_OFF:
|
||
state.ai_chat_mode = False
|
||
state.current_ai_provider = "none"
|
||
else:
|
||
state.ai_chat_mode = True
|
||
if preset == AI_PRESET_QWEN:
|
||
state.current_ai_provider = "qwen"
|
||
elif preset == AI_PRESET_OPENCODE:
|
||
state.current_ai_provider = "opencode"
|
||
else:
|
||
state.current_ai_provider = "gigachat"
|
||
|
||
preset_name = get_preset_display_name(preset)
|
||
|
||
output = f"✅ **AI-пресет переключен**\n\n"
|
||
output += f"**Текущий:** {preset_name}\n"
|
||
output += f"_{PRESET_DESCRIPTIONS[preset]['description']}_\n\n"
|
||
|
||
if old_preset != preset:
|
||
output += f"~{get_preset_display_name(old_preset)}~ → ✅ {preset_name}"
|
||
|
||
await update.message.reply_text(output, parse_mode="Markdown")
|
||
|
||
logger.info(f"Пользователь {user_id} переключил AI-пресет: {old_preset} → {preset}")
|
||
|
||
|
||
def register_ai_preset_handlers(dispatcher):
|
||
"""Зарегистрировать обработчики AI-пресетов."""
|
||
# Основное меню
|
||
dispatcher.add_handler(CommandHandler("ai_presets", ai_presets_command))
|
||
|
||
# Callback для инлайн-меню
|
||
dispatcher.add_handler(CallbackQueryHandler(ai_preset_callback, pattern="^ai_preset_"))
|
||
|
||
# Быстрые команды
|
||
dispatcher.add_handler(CommandHandler("ai_off", ai_off_command))
|
||
dispatcher.add_handler(CommandHandler("ai_qwen", ai_qwen_command))
|
||
dispatcher.add_handler(CommandHandler("ai_giga_auto", ai_giga_auto_command))
|
||
dispatcher.add_handler(CommandHandler("ai_giga_lite", ai_giga_lite_command))
|
||
dispatcher.add_handler(CommandHandler("ai_giga_pro", ai_giga_pro_command))
|
||
dispatcher.add_handler(CommandHandler("ai_giga_max", ai_giga_max_command))
|
||
dispatcher.add_handler(CommandHandler("ai_opencode", ai_opencode_command))
|
||
|
||
logger.info("Обработчики AI-пресетов зарегистрированы")
|