"""Общие модели данных — контракт между агентами (Pydantic v2). `RawRow` и `ParsedDocument` — выход любого парсера (граница агентов B → C). `MatchResult` — выход сопоставления (агент C). `*Out`-модели — выдача API (агент D), её же потребляет фронт (агент E). Любая правка этих типов проходит через оркестратора. """ from __future__ import annotations from datetime import date from pydantic import BaseModel, Field # Карта тарифов: ключ — тип цены, значение — сумма в KZT. # Возможные ключи: resident, nonresident, cis, far, insurance, partner. PriceTiers = dict[str, float] class RawRow(BaseModel): """Одна сырая строка прайса — единый выход любого парсера.""" service_name_raw: str service_code_source: str | None = None prices: PriceTiers = Field(default_factory=dict) unit: str | None = None section: str | None = None # заголовок секции — контекст специальности class ParsedDocument(BaseModel): """Результат разбора одного файла прайса.""" file_name: str file_format: str # pdf / scan_pdf / docx / xlsx / xls partner_name: str | None = None city: str | None = None effective_date: date | None = None rows: list[RawRow] = Field(default_factory=list) raw_content: str = "" parse_log: list[str] = Field(default_factory=list) class MatchResult(BaseModel): """Результат сопоставления строки со справочником.""" service_id: str | None = None method: str | None = None # code / exact / embedding / fuzzy / manual confidence: float = 0.0 # Лучший кандидат для очереди верификации, даже если ниже порога автосопоставления. suggested_service_id: str | None = None suggested_score: float = 0.0 # --- выдача API --- class ServiceOut(BaseModel): service_id: str specialty: str name_ru: str tarificator_code: str | None = None class PartnerOut(BaseModel): partner_id: str name: str city: str | None = None is_active: bool = True class PriceOut(BaseModel): partner_id: str partner_name: str price_resident_kzt: float | None = None price_nonresident_kzt: float | None = None prices: PriceTiers = Field(default_factory=dict) effective_date: date | None = None class Stats(BaseModel): """Сводка для дашборда и отчёта о качестве.""" documents_total: int = 0 documents_done: int = 0 items_total: int = 0 auto_matched_pct: float = 0.0 unmatched_total: int = 0