feat: мульти-провайдер AI, выбор типов заданий, настройка количества

- Добавлена поддержка нескольких AI провайдеров (OpenAI, Google Gemini)
- Добавлена админ-панель (/admin) для переключения AI моделей
- Добавлен AIModelService для управления моделями в БД
- Добавлен выбор типа заданий (микс, перевод слов, подстановка, перевод предложений)
- Добавлена настройка количества заданий (5-15)
- ai_service динамически выбирает провайдера на основе активной модели
- Обработка ограничений моделей (temperature, response_format)
- Очистка markdown обёртки из ответов Gemini

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-08 15:16:24 +03:00
parent 3e5c1be464
commit eb666ec9bc
17 changed files with 1095 additions and 129 deletions

View File

@@ -28,6 +28,7 @@ def get_settings_keyboard(user) -> InlineKeyboardMarkup:
ui_lang_code = getattr(user, 'language_interface', 'ru') or 'ru'
translation_lang_code = get_translation_language(user)
current_level = get_user_level_for_language(user)
tasks_count = getattr(user, 'tasks_count', 5) or 5
keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(
text=t(lang, 'settings.level_prefix') + f"{current_level}",
@@ -45,6 +46,10 @@ def get_settings_keyboard(user) -> InlineKeyboardMarkup:
text=t(lang, 'settings.translation_prefix') + t(lang, f'settings.lang_name.{translation_lang_code}'),
callback_data="settings_translation"
)],
[InlineKeyboardButton(
text=t(lang, 'settings.tasks_count_prefix') + str(tasks_count),
callback_data="settings_tasks_count"
)],
[InlineKeyboardButton(
text=t(lang, 'settings.close'),
callback_data="settings_close"
@@ -113,6 +118,26 @@ def get_learning_language_keyboard(user=None) -> InlineKeyboardMarkup:
return InlineKeyboardMarkup(inline_keyboard=keyboard)
def get_tasks_count_keyboard(user=None) -> InlineKeyboardMarkup:
"""Клавиатура выбора количества заданий"""
lang = get_user_lang(user)
current_count = getattr(user, 'tasks_count', 5) or 5
# Создаём кнопки для каждого значения (5, 7, 10, 12, 15)
counts = [5, 7, 10, 12, 15]
keyboard = []
for count in counts:
marker = "" if count == current_count else ""
keyboard.append([InlineKeyboardButton(
text=f"{marker}{count}",
callback_data=f"set_tasks_count_{count}"
)])
keyboard.append([InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")])
return InlineKeyboardMarkup(inline_keyboard=keyboard)
@router.message(Command("settings"))
async def cmd_settings(message: Message):
"""Обработчик команды /settings"""
@@ -270,6 +295,39 @@ async def set_translation_language(callback: CallbackQuery):
await callback.answer()
@router.callback_query(F.data == "settings_tasks_count")
async def settings_tasks_count(callback: CallbackQuery):
"""Показать выбор количества заданий"""
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
lang = get_user_lang(user)
await callback.message.edit_text(
t(lang, 'settings.tasks_count_title') + t(lang, 'settings.tasks_count_desc'),
reply_markup=get_tasks_count_keyboard(user)
)
await callback.answer()
@router.callback_query(F.data.startswith("set_tasks_count_"))
async def set_tasks_count(callback: CallbackQuery):
"""Установить количество заданий"""
new_count = int(callback.data.split("_")[-1])
async with async_session_maker() as session:
user = await UserService.get_user_by_telegram_id(session, callback.from_user.id)
if user:
await UserService.update_user_tasks_count(session, user.id, new_count)
lang = get_user_lang(user)
text = t(lang, 'settings.tasks_count_changed', count=new_count)
await callback.message.edit_text(
text,
reply_markup=InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton(text=t(lang, 'settings.back'), callback_data="settings_back")]])
)
await callback.answer()
@router.callback_query(F.data == "settings_back")
async def settings_back(callback: CallbackQuery):
"""Вернуться к настройкам"""