| """Citation validation: strip any [bracketed] citation not in the retrieved set. |
| |
| Guards against hallucinated case names. A citation is kept only if its text |
| matches (substring, case-insensitive) one of the retrieved source case names. |
| """ |
| from __future__ import annotations |
|
|
| import re |
|
|
| _CITATION_RE = re.compile(r"\[([^\[\]]+)\]") |
|
|
|
|
| def _matches_any(cited: str, allowed: list[str]) -> bool: |
| c = cited.strip().lower() |
| if not c: |
| return False |
| for name in allowed: |
| n = name.lower() |
| |
| |
| if c == n or c in n or n in c: |
| return True |
| return False |
|
|
|
|
| def validate_answer(answer: str, sources: list[dict]) -> dict: |
| """Drop hallucinated citations. Return {answer, removed:[...]}. |
| |
| Kept citations stay bracketed. Unknown ones are unwrapped (text retained, |
| brackets removed) and reported in `removed`. |
| """ |
| allowed = [s["case_name"] for s in sources] |
| removed: list[str] = [] |
|
|
| def _sub(m: re.Match) -> str: |
| cited = m.group(1) |
| if _matches_any(cited, allowed): |
| return m.group(0) |
| removed.append(cited) |
| return cited |
|
|
| cleaned = _CITATION_RE.sub(_sub, answer) |
| return {"answer": cleaned, "removed": removed} |
|
|
|
|
| if __name__ == "__main__": |
| srcs = [{"case_name": "Jimmy Jahangir Madan vs Bolly Cariyappa Hindley By L.Rs on 10 August, 2001"}] |
| raw = ( |
| "The application under Section 302 was held not maintainable " |
| "[Jimmy Jahangir Madan vs Bolly Cariyappa Hindley By L.Rs on 10 August, 2001]. " |
| "A different rule applied [Fake Hallucinated Case v Nobody 2099]." |
| ) |
| out = validate_answer(raw, srcs) |
| print("CLEANED:\n", out["answer"]) |
| print("\nREMOVED:", out["removed"]) |
|
|