Spaces:
Runtime error
Runtime error
File size: 3,829 Bytes
865bc90 | 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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | """Post-generation output validator & abstention layer.
Deterministic, pure-Python gate that runs AFTER extraction/generation and
REJECTS any output that smuggles in non-evidence-grounded language:
* forbidden hedging / synthesis phrases (``appears to``, ``likely``, ``overall``…),
* fabricated metrics (percentages, confidence/authenticity/consistency scores),
* unsupported severity escalation.
A forbidden token only survives if it is present verbatim in the cited evidence
(STEP: ABSOLUTE GROUNDING POLICY). The layer never rewrites text — it reports
violations and the caller abstains (drops the record). No network, fully
unit-testable.
"""
from __future__ import annotations
import re
from app.extraction.citation_validator import (
BANNED_SEVERITY_TERMS,
normalize_for_match,
)
# Hedging / synthesis / evaluation vocabulary that must never be introduced.
FORBIDDEN_PHRASES: frozenset[str] = frozenset({
"appears to", "seems to", "likely", "suggests", "indicates",
"approximately reliable", "overall", "authenticity", "consistency score",
"confidence score", "reliability score", "assessment", "evaluation",
"in summary", "in conclusion", "executive summary", "we recommend",
"it is recommended", "concerning", "potentially hazardous",
})
# Fabricated-metric patterns: percentages and explicit scoring statements.
_PERCENT_RE = re.compile(r"\b\d{1,3}(?:\.\d+)?\s?%")
_SCORE_RE = re.compile(
r"\b(?:authenticity|reliability|consistency|confidence|quality)\s+"
r"(?:score|rating|level|index)\b",
re.IGNORECASE,
)
def _present_in_evidence(term: str, evidence_norm: str) -> bool:
return term in evidence_norm
def find_violations(text: str, evidence: str = "") -> list[str]:
"""Return a list of violation descriptions for ``text``.
A forbidden phrase / severity term is only a violation when it is NOT
present verbatim in ``evidence``. Percentages and scoring statements are
violations unless the identical token appears in evidence.
"""
if not text:
return []
text_norm = normalize_for_match(text)
evidence_norm = normalize_for_match(evidence or "")
violations: list[str] = []
for phrase in FORBIDDEN_PHRASES:
if phrase in text_norm and not _present_in_evidence(phrase, evidence_norm):
violations.append(f"forbidden phrase {phrase!r}")
for term in BANNED_SEVERITY_TERMS:
if term in text_norm and not _present_in_evidence(term, evidence_norm):
violations.append(f"unsupported severity term {term!r}")
for m in _PERCENT_RE.findall(text):
token = normalize_for_match(m)
if token not in evidence_norm:
violations.append(f"fabricated percentage {m.strip()!r}")
for m in _SCORE_RE.findall(text):
if normalize_for_match(m) not in evidence_norm:
violations.append(f"fabricated metric {m.strip()!r}")
return violations
def is_clean(text: str, evidence: str = "") -> bool:
"""True when ``text`` introduces no forbidden/fabricated content."""
return not find_violations(text, evidence)
def should_abstain(
text: str,
evidence: str = "",
*,
min_confidence: float = 1.0,
confidence: float = 1.0,
evidence_aligned: bool = True,
source_attributed: bool = True,
) -> bool:
"""Abstention logic (STEP: ABSTENTION LOGIC).
Returns True (drop the record / RETURN NOTHING) when any of:
* confidence is below ``min_confidence``,
* evidence alignment failed,
* source attribution failed,
* the text contains forbidden/fabricated content not grounded in evidence.
"""
if confidence < min_confidence:
return True
if not evidence_aligned:
return True
if not source_attributed:
return True
return not is_clean(text, evidence)
|