Files
contracts-flask/deploy/contracts.py

90 lines
3.5 KiB
Python

"""Контракты стадий пайплайна — frozen dataclass на выходах, dict на входах."""
from dataclasses import dataclass, field
from typing import Optional
from datetime import date
@dataclass(frozen=True)
class ParseResult:
"""Выход parse.py — результат парсинга документа."""
status: str # 'parsed' | 'error'
element_count: int # количество извлечённых элементов
elements: list # [{type, text/rows}, ...]
error: Optional[str] = None # сообщение об ошибке (если status='error')
@classmethod
def from_row(cls, row: dict):
"""Из строки БД (documents)."""
elements = row.get("elements_json", [])
return cls(
status=row.get("status", "error"),
element_count=len(elements) if isinstance(elements, list) else 0,
elements=elements,
error=row.get("error_message"),
)
@dataclass(frozen=True)
class ClassifyResult:
"""Выход classify.py — результат классификации документа."""
doc_type: str # 'contract' | 'supplement' | 'specification' | 'garbage' | 'other'
own_number: Optional[str] = None
parent_number: Optional[str] = None
doc_date: Optional[str] = None
counterparty: Optional[str] = None
@classmethod
def from_llm(cls, raw: dict):
"""Из сырого ответа LLM."""
return cls(
doc_type=raw.get("doc_type", "other"),
own_number=raw.get("own_number") or None,
parent_number=raw.get("parent_number") or None,
doc_date=raw.get("doc_date") or None,
counterparty=raw.get("counterparty") or None,
)
@dataclass(frozen=True)
class BatchGroupingResult:
"""Выход grouping.py — результат группировки всего батча."""
groups: list = field(default_factory=list) # list[GroupingResult]
unresolved: list = field(default_factory=list) # документы без группы
@dataclass(frozen=True)
class GroupingResult:
"""Выход grouping.py — результат группировки по контрактам."""
contract_number: str # нормализованный номер договора
counterparty: Optional[str] = None
documents: list = field(default_factory=list) # [{doc_id, filename, doc_type, ...}]
@classmethod
def from_row(cls, row: dict):
return cls(
contract_number=row.get("contract_number", ""),
counterparty=row.get("counterparty"),
documents=row.get("documents", []),
)
@dataclass(frozen=True)
class CompareOp:
"""Одна операция сравнения (ADD/UPDATE/DELETE/UNRESOLVED)."""
action: str # 'ADD' | 'UPDATE' | 'DELETE' | 'UNRESOLVED'
new_row: Optional[dict] = None
new_values: Optional[dict] = None
target_hash: Optional[str] = None
comment: str = ""
@classmethod
def from_llm(cls, raw: dict):
"""Из сырого ответа LLM."""
return cls(
action=raw.get("action", "UNRESOLVED"),
new_row=raw.get("new_row") or None,
new_values=raw.get("new_values") or None,
target_hash=raw.get("target_hash") or raw.get("target_id"),
comment=raw.get("comment", ""),
)