2025-12-08 15:16:24 +03:00
|
|
|
|
from sqlalchemy import select, update
|
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
2025-12-08 16:43:08 +03:00
|
|
|
|
from database.models import AIModel, AIProvider, User
|
|
|
|
|
|
from typing import Optional, List, Tuple
|
2025-12-08 15:16:24 +03:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Дефолтная модель если в БД ничего нет
|
|
|
|
|
|
DEFAULT_MODEL = "gpt-4o-mini"
|
|
|
|
|
|
DEFAULT_PROVIDER = AIProvider.openai
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AIModelService:
|
|
|
|
|
|
"""Сервис для работы с AI моделями"""
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_active_model(session: AsyncSession) -> Optional[AIModel]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Получить активную AI модель
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
AIModel или None если нет активной модели
|
|
|
|
|
|
"""
|
|
|
|
|
|
result = await session.execute(
|
|
|
|
|
|
select(AIModel).where(AIModel.is_active == True)
|
|
|
|
|
|
)
|
|
|
|
|
|
return result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_active_model_name(session: AsyncSession) -> str:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Получить название активной модели
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Название модели (например "gpt-4o-mini") или дефолтное
|
|
|
|
|
|
"""
|
|
|
|
|
|
model = await AIModelService.get_active_model(session)
|
|
|
|
|
|
if model:
|
|
|
|
|
|
return model.model_name
|
|
|
|
|
|
return DEFAULT_MODEL
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_active_provider(session: AsyncSession) -> AIProvider:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Получить провайдера активной модели
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
AIProvider (OPENAI или GOOGLE)
|
|
|
|
|
|
"""
|
|
|
|
|
|
model = await AIModelService.get_active_model(session)
|
|
|
|
|
|
if model:
|
|
|
|
|
|
return model.provider
|
|
|
|
|
|
return DEFAULT_PROVIDER
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def get_all_models(session: AsyncSession) -> List[AIModel]:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Получить все доступные модели
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Список всех моделей
|
|
|
|
|
|
"""
|
|
|
|
|
|
result = await session.execute(
|
|
|
|
|
|
select(AIModel).order_by(AIModel.provider, AIModel.model_name)
|
|
|
|
|
|
)
|
|
|
|
|
|
return list(result.scalars().all())
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def set_active_model(session: AsyncSession, model_id: int) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Установить активную модель по ID
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
model_id: ID модели для активации
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
True если успешно, False если модель не найдена
|
|
|
|
|
|
"""
|
|
|
|
|
|
# Проверяем существование модели
|
|
|
|
|
|
result = await session.execute(
|
|
|
|
|
|
select(AIModel).where(AIModel.id == model_id)
|
|
|
|
|
|
)
|
|
|
|
|
|
model = result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
|
|
if not model:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# Деактивируем все модели
|
|
|
|
|
|
await session.execute(
|
|
|
|
|
|
update(AIModel).values(is_active=False)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Активируем выбранную
|
|
|
|
|
|
model.is_active = True
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def set_active_model_by_name(session: AsyncSession, model_name: str) -> bool:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Установить активную модель по названию
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
model_name: Название модели (например "gpt-4o-mini")
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
True если успешно, False если модель не найдена
|
|
|
|
|
|
"""
|
|
|
|
|
|
result = await session.execute(
|
|
|
|
|
|
select(AIModel).where(AIModel.model_name == model_name)
|
|
|
|
|
|
)
|
|
|
|
|
|
model = result.scalar_one_or_none()
|
|
|
|
|
|
|
|
|
|
|
|
if not model:
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# Деактивируем все модели
|
|
|
|
|
|
await session.execute(
|
|
|
|
|
|
update(AIModel).values(is_active=False)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# Активируем выбранную
|
|
|
|
|
|
model.is_active = True
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def create_model(
|
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
|
provider: AIProvider,
|
|
|
|
|
|
model_name: str,
|
|
|
|
|
|
display_name: str,
|
|
|
|
|
|
is_active: bool = False
|
|
|
|
|
|
) -> AIModel:
|
|
|
|
|
|
"""
|
|
|
|
|
|
Создать новую модель
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
|
provider: Провайдер (OPENAI, GOOGLE)
|
|
|
|
|
|
model_name: Техническое название модели
|
|
|
|
|
|
display_name: Отображаемое название
|
|
|
|
|
|
is_active: Активна ли модель
|
|
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
|
Созданная модель
|
|
|
|
|
|
"""
|
|
|
|
|
|
# Если активируем новую модель, деактивируем остальные
|
|
|
|
|
|
if is_active:
|
|
|
|
|
|
await session.execute(
|
|
|
|
|
|
update(AIModel).values(is_active=False)
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
model = AIModel(
|
|
|
|
|
|
provider=provider,
|
|
|
|
|
|
model_name=model_name,
|
|
|
|
|
|
display_name=display_name,
|
|
|
|
|
|
is_active=is_active
|
|
|
|
|
|
)
|
|
|
|
|
|
session.add(model)
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
await session.refresh(model)
|
|
|
|
|
|
return model
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
|
async def ensure_default_models(session: AsyncSession):
|
|
|
|
|
|
"""
|
|
|
|
|
|
Создать дефолтные модели если их нет в БД
|
|
|
|
|
|
"""
|
|
|
|
|
|
result = await session.execute(select(AIModel))
|
|
|
|
|
|
existing = list(result.scalars().all())
|
|
|
|
|
|
|
|
|
|
|
|
if existing:
|
|
|
|
|
|
return # Модели уже есть
|
|
|
|
|
|
|
|
|
|
|
|
# Создаём дефолтные модели
|
|
|
|
|
|
default_models = [
|
|
|
|
|
|
(AIProvider.openai, "gpt-4o-mini", "GPT-4o Mini", True),
|
|
|
|
|
|
(AIProvider.openai, "gpt-5-nano", "GPT-5 Nano", False),
|
|
|
|
|
|
(AIProvider.google, "gemini-2.5-flash-lite", "Gemini 2.5 Flash Lite", False),
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
for provider, name, display, active in default_models:
|
|
|
|
|
|
model = AIModel(
|
|
|
|
|
|
provider=provider,
|
|
|
|
|
|
model_name=name,
|
|
|
|
|
|
display_name=display,
|
|
|
|
|
|
is_active=active
|
|
|
|
|
|
)
|
|
|
|
|
|
session.add(model)
|
|
|
|
|
|
|
|
|
|
|
|
await session.commit()
|
2025-12-08 16:43:08 +03:00
|
|
|
|
|
|
|
|
|
|
@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
|