203 lines
7.1 KiB
Python
203 lines
7.1 KiB
Python
|
|
"""
|
|||
|
|
Query API endpoints.
|
|||
|
|
|
|||
|
|
Отправка запросов к RAG backends в двух режимах:
|
|||
|
|
1. Bench mode - batch запросы
|
|||
|
|
2. Backend mode - последовательные запросы
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
from fastapi import APIRouter, Depends, HTTPException, status
|
|||
|
|
from typing import Dict, Any
|
|||
|
|
from app.models.query import BenchQueryRequest, BackendQueryRequest, QueryResponse
|
|||
|
|
from app.interfaces.db_api_client import DBApiClient
|
|||
|
|
from app.services.rag_service import RagService
|
|||
|
|
from app.dependencies import get_db_client, get_current_user
|
|||
|
|
import httpx
|
|||
|
|
import logging
|
|||
|
|
from datetime import datetime
|
|||
|
|
import uuid
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
router = APIRouter(prefix="/query", tags=["query"])
|
|||
|
|
|
|||
|
|
|
|||
|
|
def get_rag_service() -> RagService:
|
|||
|
|
"""Dependency для получения RAG service."""
|
|||
|
|
return RagService()
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/bench", response_model=QueryResponse)
|
|||
|
|
async def bench_query(
|
|||
|
|
request: BenchQueryRequest,
|
|||
|
|
current_user: dict = Depends(get_current_user),
|
|||
|
|
db_client: DBApiClient = Depends(get_db_client),
|
|||
|
|
rag_service: RagService = Depends(get_rag_service)
|
|||
|
|
):
|
|||
|
|
"""
|
|||
|
|
Отправить batch запрос к RAG backend (bench mode).
|
|||
|
|
|
|||
|
|
Отправляет все вопросы одним запросом.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
request: Запрос с окружением и списком вопросов
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
QueryResponse: Ответ от RAG backend с metadata
|
|||
|
|
"""
|
|||
|
|
user_id = current_user["user_id"]
|
|||
|
|
environment = request.environment.lower()
|
|||
|
|
|
|||
|
|
# Валидация окружения
|
|||
|
|
if environment not in ['ift', 'psi', 'prod']:
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail="Invalid environment. Must be one of: ift, psi, prod"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# Получить настройки пользователя из DB API
|
|||
|
|
user_settings_response = await db_client.get_user_settings(user_id)
|
|||
|
|
env_settings = user_settings_response.settings.get(environment)
|
|||
|
|
|
|||
|
|
if not env_settings:
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail=f"Settings not found for environment: {environment}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Проверить что apiMode = bench
|
|||
|
|
if env_settings.apiMode != 'bench':
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail=f"Environment {environment} is not configured for bench mode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Сгенерировать request_id
|
|||
|
|
request_id = str(uuid.uuid4())
|
|||
|
|
|
|||
|
|
logger.info(
|
|||
|
|
f"User {user_id} sending bench query to {environment}: "
|
|||
|
|
f"{len(request.questions)} questions, request_id={request_id}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Отправить запрос к RAG backend
|
|||
|
|
env_settings_dict = env_settings.model_dump()
|
|||
|
|
response_data = await rag_service.send_bench_query(
|
|||
|
|
environment=environment,
|
|||
|
|
questions=request.questions,
|
|||
|
|
user_settings=env_settings_dict,
|
|||
|
|
request_id=request_id
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Формируем ответ
|
|||
|
|
return QueryResponse(
|
|||
|
|
request_id=request_id,
|
|||
|
|
timestamp=datetime.utcnow().isoformat() + "Z",
|
|||
|
|
environment=environment,
|
|||
|
|
response=response_data
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
except httpx.HTTPStatusError as e:
|
|||
|
|
logger.error(f"RAG backend error for bench query: {e}")
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_502_BAD_GATEWAY,
|
|||
|
|
detail=f"RAG backend returned error: {e.response.status_code}"
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Unexpected error in bench query: {e}")
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|||
|
|
detail="Internal server error"
|
|||
|
|
)
|
|||
|
|
finally:
|
|||
|
|
await rag_service.close()
|
|||
|
|
|
|||
|
|
|
|||
|
|
@router.post("/backend", response_model=QueryResponse)
|
|||
|
|
async def backend_query(
|
|||
|
|
request: BackendQueryRequest,
|
|||
|
|
current_user: dict = Depends(get_current_user),
|
|||
|
|
db_client: DBApiClient = Depends(get_db_client),
|
|||
|
|
rag_service: RagService = Depends(get_rag_service)
|
|||
|
|
):
|
|||
|
|
"""
|
|||
|
|
Отправить запросы к RAG backend (backend mode).
|
|||
|
|
|
|||
|
|
Отправляет вопросы по одному с возможностью сброса сессии.
|
|||
|
|
|
|||
|
|
Args:
|
|||
|
|
request: Запрос с окружением, вопросами и флагом reset_session
|
|||
|
|
|
|||
|
|
Returns:
|
|||
|
|
QueryResponse: Список ответов от RAG backend с metadata
|
|||
|
|
"""
|
|||
|
|
user_id = current_user["user_id"]
|
|||
|
|
environment = request.environment.lower()
|
|||
|
|
|
|||
|
|
# Валидация окружения
|
|||
|
|
if environment not in ['ift', 'psi', 'prod']:
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail="Invalid environment. Must be one of: ift, psi, prod"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# Получить настройки пользователя из DB API
|
|||
|
|
user_settings_response = await db_client.get_user_settings(user_id)
|
|||
|
|
env_settings = user_settings_response.settings.get(environment)
|
|||
|
|
|
|||
|
|
if not env_settings:
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail=f"Settings not found for environment: {environment}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Проверить что apiMode = backend
|
|||
|
|
if env_settings.apiMode != 'backend':
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|||
|
|
detail=f"Environment {environment} is not configured for backend mode"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Сгенерировать request_id
|
|||
|
|
request_id = str(uuid.uuid4())
|
|||
|
|
|
|||
|
|
logger.info(
|
|||
|
|
f"User {user_id} sending backend query to {environment}: "
|
|||
|
|
f"{len(request.questions)} questions, request_id={request_id}, "
|
|||
|
|
f"reset_session={request.reset_session}"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Отправить запросы к RAG backend
|
|||
|
|
env_settings_dict = env_settings.model_dump()
|
|||
|
|
response_data = await rag_service.send_backend_query(
|
|||
|
|
environment=environment,
|
|||
|
|
questions=request.questions,
|
|||
|
|
user_settings=env_settings_dict,
|
|||
|
|
reset_session=request.reset_session
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Формируем ответ
|
|||
|
|
return QueryResponse(
|
|||
|
|
request_id=request_id,
|
|||
|
|
timestamp=datetime.utcnow().isoformat() + "Z",
|
|||
|
|
environment=environment,
|
|||
|
|
response={"answers": response_data} # Оборачиваем в объект
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
except httpx.HTTPStatusError as e:
|
|||
|
|
logger.error(f"RAG backend error for backend query: {e}")
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_502_BAD_GATEWAY,
|
|||
|
|
detail=f"RAG backend returned error: {e.response.status_code}"
|
|||
|
|
)
|
|||
|
|
except Exception as e:
|
|||
|
|
logger.error(f"Unexpected error in backend query: {e}")
|
|||
|
|
raise HTTPException(
|
|||
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|||
|
|
detail="Internal server error"
|
|||
|
|
)
|
|||
|
|
finally:
|
|||
|
|
await rag_service.close()
|