from typing import Annotated, Any from fastapi import APIRouter, Depends, File, HTTPException, UploadFile import common.dependencies as DI from common import auth from components.services.search_metrics import SearchMetricsService from schemas.evaluation import EvaluationParams, EvaluationResponse # Создание роутера router = APIRouter(prefix="/evaluate", tags=["Evaluation"]) # Важно: добавить импорт logger, если его нет import logging logger = logging.getLogger(__name__) # Определение эндпоинта @router.post( "/from_file/{dataset_id}", response_model=EvaluationResponse, summary="Оценка RAG по файлу", description="Загружает XLSX файл с вопросами/ответами и рассчитывает метрики RAG (Precision, Recall, F1) для указанного dataset_id и различных значений top_n. Опционально применяет Query Expansion." ) async def evaluate_rag_from_file( dataset_id: int, params: Annotated[EvaluationParams, Depends()], file: Annotated[UploadFile, File(description="XLSX файл с колонками 'id', 'question', 'text' (эталонные ответы через \\n)")], metrics_service: Annotated[SearchMetricsService, Depends(DI.get_search_metrics_service)], current_user: Annotated[any, Depends(auth.get_current_user)], # Защита эндпоинта ) -> Any: # Возвращаем Any, т.к. сервис возвращает dict, а FastAPI валидирует по response_model """Эндпоинт для оценки RAG. - Принимает ID датасета в пути. - Принимает параметры оценки (порог, top_n, use_query_expansion) и файл как multipart/form-data. - Вызывает SearchMetricsService для выполнения расчетов. - Возвращает рассчитанные метрики. """ try: # --- Вызываем сервис, он теперь возвращает полный словарь --- evaluation_full_results = await metrics_service.evaluate_from_file( file=file, dataset_id=dataset_id, similarity_threshold=params.similarity_threshold, top_n_values=params.top_n_values, use_query_expansion=params.use_query_expansion, top_worst_k=params.top_worst_k # Передаем новый параметр ) # --- Просто возвращаем результат сервиса --- # FastAPI сам проверит его по схеме EvaluationResponse return evaluation_full_results except HTTPException as e: # Просто пробрасываем HTTP ошибки дальше raise e except Exception as e: # Логирование ошибки может быть полезно здесь logger.exception("Internal server error during evaluation endpoint execution.") # Пример логирования # Ловим другие возможные ошибки во время оценки # Логгер уже есть в SearchMetricsService raise HTTPException(status_code=500, detail=f"Internal server error during evaluation: {e}")