"""Deterministic claim-vs-evidence verifier for the numeric spine. A 7.8M model cannot reliably copy values, so the hard comparison is done by rules instead of generation. The model's job is confined to what it actually can do: identifying the claim and the evidence segments. This module: 1. extracts values from the claim side and the evidence side, 2. compares them deterministically, 3. returns a verdict that cannot be hallucinated: supports / refutes / not enough information / unclear. For any case it cannot resolve (no clean numeric pair), it says so -- it never fabricates an answer. """ import re def _nums(text): return re.findall(r"\b\d+(?:,\d{3})*\.?\d*%?\b", text) def _years(text): return re.findall(r"\b(?:19|20)\d{2}\b", text) def _times(text): return re.findall(r"\b\d{1,2}:\d{2}\b", text) def _clean(v): return v.replace(",", "").replace("%", "") def _vals(text): return sorted({_clean(v) for v in (_nums(text) + _years(text) + _times(text))}) def deterministic_verdict(doc): """doc: the analyst prompt (claim + evidence). Returns a verdict dict.""" m = re.split(r"\bEvidence:\s*", doc, flags=re.IGNORECASE) claim, evidence = m[0], (m[1] if len(m) > 1 else "") cv = _vals(claim) ev = _vals(evidence) if not cv and not ev: return {"verdict": "not enough information", "kind": "no-values", "confidence": "LOW", "explain": "no numeric value to compare"} if cv and not ev: return {"verdict": "not enough information", "kind": "claim-only", "confidence": "HIGH", "explain": "evidence has no numeric value to compare"} if not cv: return {"verdict": "unclear", "kind": "no-claim-value", "confidence": "LOW", "explain": "claim has no numeric value"} if set(cv) == set(ev): return {"verdict": "supports", "kind": "equal", "confidence": "HIGH", "explain": f"claim value {sorted(cv)} equals evidence value {sorted(ev)}"} if not (set(cv) & set(ev)): return {"verdict": "refutes", "kind": "differ", "confidence": "HIGH", "explain": f"claim value {sorted(cv)} differs from evidence value {sorted(ev)}"} return {"verdict": "unclear", "kind": "partial", "confidence": "LOW", "explain": f"claim {sorted(cv)} partially overlaps evidence {sorted(ev)}"}