"""Core models and protocol for DataForge repairers.""" from __future__ import annotations from typing import Literal, Protocol from pydantic import BaseModel, Field from dataforge.detectors.base import Issue, Schema from dataforge.table import TableLike from dataforge.transactions.txn import CellFix ProvenanceLiteral = Literal["deterministic", "llm_cache", "llm_live"] AttemptStatusLiteral = Literal[ "accepted", "denied", "escalated", "rejected", "unknown", "attempted_not_fixed", ] class ProposedFix(BaseModel): """A repair proposal emitted by a repairer. Args: fix: The cell mutation to apply. reason: Human-readable explanation of why this repair is proposed. confidence: Repair confidence in the range [0.0, 1.0]. provenance: Where the proposed value came from. """ fix: CellFix reason: str = Field(min_length=1) confidence: float = Field(ge=0.0, le=1.0) provenance: ProvenanceLiteral model_config = {"frozen": True} class RepairAttempt(BaseModel): """Recorded outcome for one issue-repair attempt.""" issue: Issue attempt_number: int = Field(ge=1) fix: ProposedFix | None = None status: AttemptStatusLiteral reason: str = Field(min_length=1) unsat_core: tuple[str, ...] = Field(default_factory=tuple) model_config = {"frozen": True} class RetryContext(BaseModel): """Hints passed back to a repairer after a failed attempt.""" issue: Issue previous_attempts: tuple[RepairAttempt, ...] = Field(default_factory=tuple) rejected_values: frozenset[str] = Field(default_factory=frozenset) hints: tuple[str, ...] = Field(default_factory=tuple) model_config = {"frozen": True} class Repairer(Protocol): """Structural protocol implemented by every repairer.""" def propose( self, issue: Issue, df: TableLike, schema: Schema | None, retry_context: RetryContext | None = None, ) -> ProposedFix | None: """Return a repair proposal for an issue, or ``None`` if unavailable.""" ... # pragma: no cover