Spaces:
Paused
Paused
| """Trust metrics β citation attribution, confidence calibration (ECE), and the | |
| regression gate. Deterministic, no model/network.""" | |
| from __future__ import annotations | |
| from auralynq.eval.calibration import answer_correct, calibration_scores | |
| from auralynq.eval.citation_eval import citation_scores | |
| from auralynq.eval.gate import eval_gate | |
| # ββ calibration βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_calibration_perfect_is_zero_ece(): | |
| # bin [0.9,1.0): confidence 0.9, 9/10 correct β acc 0.9 == conf 0.9 β ECE 0 | |
| pairs = [(0.9, True)] * 9 + [(0.9, False)] | |
| s = calibration_scores(pairs) | |
| assert s.accuracy == 0.9 | |
| assert s.ece == 0.0 | |
| def test_calibration_overconfident_has_max_ece(): | |
| # confidence 1.0 everywhere, but only half correct β gap 0.5 in the top bin | |
| pairs = [(1.0, True)] * 5 + [(1.0, False)] * 5 | |
| s = calibration_scores(pairs) | |
| assert s.accuracy == 0.5 | |
| assert s.avg_confidence == 1.0 | |
| assert s.ece == 0.5 | |
| assert s.mce == 0.5 | |
| assert s.brier == 0.5 # mean((1-1)^2 x5, (1-0)^2 x5) = 0.5 | |
| def test_calibration_empty(): | |
| s = calibration_scores([]) | |
| assert s.n == 0 and s.ece == 0.0 | |
| def test_answer_correct(): | |
| assert answer_correct("The capital is Paris.", "Paris") is True | |
| assert ( | |
| answer_correct("Ericsson filed FRAND patent licensing terms", "FRAND patent licensing") | |
| is True | |
| ) | |
| assert answer_correct("The weather is sunny", "Paris") is False | |
| assert answer_correct("", "Paris") is False | |
| # ββ citation attribution ββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def test_citation_precision_penalizes_spurious(): | |
| good = { | |
| "text": "Ericsson filed fair reasonable FRAND patent licensing terms with standards bodies", | |
| "source": "ericsson.pdf", | |
| } | |
| spurious = { | |
| "text": "The weather in Paris was sunny throughout the summer holidays", | |
| "source": "weather.pdf", | |
| } | |
| answer = "Ericsson filed FRAND patent licensing terms." | |
| both = citation_scores([{"answer": answer, "citations": [good, spurious]}]) | |
| only_good = citation_scores([{"answer": answer, "citations": [good]}]) | |
| assert only_good.citation_precision == 1.0 | |
| # the spurious citation drops precision (it doesn't back the answer) | |
| assert both.citation_precision < only_good.citation_precision | |
| assert both.avg_citations == 2.0 | |
| def test_attribution_and_unsupported_rate(): | |
| # one supported claim + one unsupported claim | |
| cites = [ | |
| {"text": "Ericsson filed fair reasonable FRAND patent licensing terms", "source": "e.pdf"} | |
| ] | |
| answer = "Ericsson filed FRAND patent licensing terms. The moon orbits earth every month." | |
| s = citation_scores([{"answer": answer, "citations": cites}]) | |
| assert 0.0 < s.attribution_rate < 1.0 # first claim supported, second not | |
| assert round(s.attribution_rate + s.unsupported_claim_rate, 4) == 1.0 | |
| def test_citation_empty(): | |
| s = citation_scores([]) | |
| assert s.n == 0 and s.citation_precision == 0.0 | |
| # ββ gate ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _report(cit_prec, ece, faith=0.8, recall=0.8, attr=0.8, unsup=0.2): | |
| return { | |
| "agentic": { | |
| "retrieval": {"recall_at_k": recall}, | |
| "ragas": {"faithfulness": faith}, | |
| "citation": { | |
| "citation_precision": cit_prec, | |
| "attribution_rate": attr, | |
| "unsupported_claim_rate": unsup, | |
| }, | |
| "calibration": {"ece": ece}, | |
| } | |
| } | |
| def test_gate_passes_when_healthy(): | |
| g = eval_gate(_report(cit_prec=0.9, ece=0.05)) | |
| assert g["passed"] is True and g["failures"] == [] | |
| def test_gate_fails_on_low_citation_precision_and_high_ece(): | |
| g = eval_gate(_report(cit_prec=0.3, ece=0.4)) | |
| assert g["passed"] is False | |
| failed = {f["metric"] for f in g["failures"]} | |
| assert "citation_precision" in failed | |
| assert "ece" in failed | |
| def test_gate_skips_absent_metrics(): | |
| # a report missing citation/calibration must not spuriously fail | |
| g = eval_gate({"agentic": {"retrieval": {"recall_at_k": 0.9}, "ragas": {"faithfulness": 0.9}}}) | |
| assert g["passed"] is True | |
| metrics = {c["metric"] for c in g["checks"]} | |
| assert "citation_precision" not in metrics | |