from typing import Any from pydantic import BaseModel, Field # Определение моделей Pydantic class EvaluationParams(BaseModel): similarity_threshold: float = Field( ..., ge=0.0, le=1.0, description="Порог схожести для fuzzy сравнения (от 0.0 до 1.0)", examples=[0.7] ) top_n_values: list[int] = Field( ..., min_items=1, description="Список значений Top-N для оценки", examples=[[10, 20, 50]] ) use_query_expansion: bool = Field( default=False, description="Использовать ли Query Expansion перед поиском для каждого вопроса", examples=[True] ) top_worst_k: int = Field( default=5, ge=1, description="Количество худших вопросов для вывода", examples=[5] ) class Metrics(BaseModel): macro_precision: float | None macro_recall: float | None macro_f1: float | None weighted_precision: float | None weighted_recall: float | None weighted_f1: float | None micro_precision: float | None micro_recall: float | None micro_f1: float | None assembly_punct_recall_macro: float | None = Field( None, description="Macro-усредненный Recall найденных пунктов в собранном контексте" ) doc_recall_macro: float | None = Field( None, description="Macro-усредненный Recall найденных эталонных документов в собранном контексте" ) avg_spurious_docs: float | None = Field( None, description="Среднее количество 'лишних' документов (найденных, но не ожидаемых) на вопрос" ) class EvaluationResponse(BaseModel): total_found_puncts_overall: int | None = Field( None, alias="Найдено пунктов (всего)" ) total_ground_truth_puncts_overall: int | None = Field( None, alias="Всего пунктов (эталон)" ) human_readable_chunk_micro_recall: float | None = Field( None, alias="% найденных пунктов (чанк присутствует в пункте)" ) human_readable_assembly_micro_recall: float | None = Field( None, alias="% пунктов были найдены в собранной версии" ) human_readable_chunk_macro_recall: float | None = Field( None, alias="В среднем для каждого вопроса найден такой % пунктов" ) human_readable_doc_macro_recall: float | None = Field( None, alias="В среднем для каждого вопроса найден такой % документов" ) human_readable_avg_spurious_docs: float | None = Field( None, alias="В среднем для каждого вопроса найдено N лишних документов, N" ) results: dict[int, Metrics] = Field( ..., description="Словарь с метриками для каждого значения top_n" ) worst_performing_questions: list[dict[str, Any]] | None = Field( None, description="Список вопросов с наихудшими показателями (по Assembly Recall)" )