File size: 2,129 Bytes
5143557
 
 
 
 
 
 
 
 
eed1cab
5143557
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
eed1cab
5143557
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
"""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