from __future__ import annotations from typing import Literal, Optional from pydantic import BaseModel, Field import uuid class Rule(BaseModel): type: Literal["numeric_threshold", "count_threshold", "certification_present", "document_present"] field: str operator: Literal[">=", "<=", "==", "exists"] value: float | int | None = None unit: str | None = None class Criterion(BaseModel): id: str title: str category: Literal["financial", "technical", "compliance"] mandatory: bool description: str rule: Rule query_hints: list[str] source_page: int source_clause: str class Evidence(BaseModel): bidder_id: str doc_name: str page: int text: str source_type: Literal["text_pdf", "tesseract", "vision_llm"] ocr_confidence: float | None = None class Source(BaseModel): doc_name: str page: int snippet: str source_type: Literal["text_pdf", "tesseract", "vision_llm"] class Verdict(BaseModel): verdict_id: str = Field(default_factory=lambda: f"V-{uuid.uuid4().hex[:8]}") bidder_id: str criterion_id: str verdict: Literal["eligible", "not_eligible", "needs_review"] extracted_value: str | None = None normalized_value: float | int | None = None source: Source | None = None llm_confidence: float = 0.0 ocr_confidence: float | None = None combined_confidence: float = 0.0 reason: str = "" model_version: str = "" timestamp: str = "" review_status: Literal["pending", "approved", "edited", "rejected"] = "pending" class AuditEntry(BaseModel): id: int | None = None ts: str action: str actor: str model_version: str | None = None bidder_id: str | None = None criterion_id: str | None = None payload_json: str | None = None