"""Claim-schema aware deterministic numeric validation.""" from __future__ import annotations import re from copy import deepcopy from numeric_core import validate_numeric _EXACT_SPAN_KINDS = {"frequency", "effect_size"} def _norm(value: str) -> str: return re.sub(r"\s+", " ", str(value or "")).strip().casefold() def validate_claim_numbers( claim_text: str, evidence_text: str, numeric_facts: list[dict], *, relative_tolerance: float, ) -> tuple[str, list[dict], list[str]]: facts = deepcopy(numeric_facts) contains_number = bool(re.search(r"\d", claim_text)) or bool(facts) if not contains_number: return "not_applicable", facts, ["The claim contains no numeric assertion."] full_result = validate_numeric( claim_text, evidence_text, rel_tol=relative_tolerance, ) reasons: list[str] = [] all_ok = bool(full_result.get("numeric_ok")) for fact in facts: raw_text = str(fact.get("raw_text") or "") kind = str(fact.get("kind") or "") raw_present = bool(raw_text) and _norm(raw_text) in _norm(evidence_text) if kind in _EXACT_SPAN_KINDS: fact_ok = raw_present else: fact_result = validate_numeric( raw_text, evidence_text, rel_tol=relative_tolerance, ) fact_ok = raw_present and bool(fact_result.get("numeric_ok")) fact["status"] = "validated" if fact_ok else "conflicted" fact["correction"] = None if not fact_ok: all_ok = False reasons.append( f"Numeric fact '{raw_text or kind}' is absent from or inconsistent with the source quote." ) if all_ok: reasons.append("All numeric assertions match the exact source quote; no correction was applied.") return "validated", facts, reasons reasons.append("At least one numeric assertion is unsupported or inconsistent; silent correction is forbidden.") return "conflicted", facts, reasons