feat: персональные AI модели, оптимизация задач, фильтрация словаря
- Добавлена поддержка персональных AI моделей для каждого пользователя - Оптимизация создания заданий: батч-запрос к AI вместо N запросов - Фильтрация слов по языку изучения (source_lang) в словаре - Удалены неиспользуемые колонки examples и category из vocabulary - Миграции для ai_model_id и удаления колонок 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from database.models import AIModel, AIProvider
|
||||
from typing import Optional, List
|
||||
from database.models import AIModel, AIProvider, User
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
|
||||
# Дефолтная модель если в БД ничего нет
|
||||
@@ -188,3 +188,81 @@ class AIModelService:
|
||||
session.add(model)
|
||||
|
||||
await session.commit()
|
||||
|
||||
@staticmethod
|
||||
async def get_user_model(session: AsyncSession, user_id: int) -> Optional[AIModel]:
|
||||
"""
|
||||
Получить AI модель пользователя.
|
||||
Если у пользователя не выбрана модель, возвращает глобальную активную.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя в БД
|
||||
|
||||
Returns:
|
||||
AIModel или None
|
||||
"""
|
||||
# Получаем пользователя
|
||||
result = await session.execute(
|
||||
select(User).where(User.id == user_id)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if user and user.ai_model_id:
|
||||
# У пользователя выбрана своя модель
|
||||
model_result = await session.execute(
|
||||
select(AIModel).where(AIModel.id == user.ai_model_id)
|
||||
)
|
||||
model = model_result.scalar_one_or_none()
|
||||
if model:
|
||||
return model
|
||||
|
||||
# Fallback на глобальную активную модель
|
||||
return await AIModelService.get_active_model(session)
|
||||
|
||||
@staticmethod
|
||||
async def get_user_model_info(session: AsyncSession, user_id: int) -> Tuple[str, AIProvider]:
|
||||
"""
|
||||
Получить название модели и провайдера для пользователя.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя в БД
|
||||
|
||||
Returns:
|
||||
Tuple[model_name, provider]
|
||||
"""
|
||||
model = await AIModelService.get_user_model(session, user_id)
|
||||
if model:
|
||||
return model.model_name, model.provider
|
||||
return DEFAULT_MODEL, DEFAULT_PROVIDER
|
||||
|
||||
@staticmethod
|
||||
async def set_user_model(session: AsyncSession, user_id: int, model_id: Optional[int]) -> bool:
|
||||
"""
|
||||
Установить AI модель для пользователя.
|
||||
|
||||
Args:
|
||||
user_id: ID пользователя в БД
|
||||
model_id: ID модели или None для сброса на глобальную
|
||||
|
||||
Returns:
|
||||
True если успешно
|
||||
"""
|
||||
result = await session.execute(
|
||||
select(User).where(User.id == user_id)
|
||||
)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
return False
|
||||
|
||||
# Проверяем существование модели если указан ID
|
||||
if model_id is not None:
|
||||
model_result = await session.execute(
|
||||
select(AIModel).where(AIModel.id == model_id)
|
||||
)
|
||||
if not model_result.scalar_one_or_none():
|
||||
return False
|
||||
|
||||
user.ai_model_id = model_id
|
||||
await session.commit()
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user