| """Confidence Calibrator tests — 100% coverage target (load-bearing per Specs §7.6).""" |
|
|
| from __future__ import annotations |
|
|
| import pytest |
|
|
| from orchestrator.calibrator import ( |
| DEMOTION_COLD_START, |
| DEMOTION_PARTIAL, |
| DEMOTION_VALIDATION_FAILED, |
| LLM_DISCOUNT_FACTOR, |
| TIER_HIGH, |
| TIER_MEDIUM, |
| W_ACCURACY, |
| W_EVIDENCE, |
| W_LLM, |
| W_RULE_MATCH, |
| CalibrationInputs, |
| calibrate, |
| compute_evidence_convergence, |
| ) |
|
|
| |
|
|
|
|
| def _inputs(**overrides: object) -> CalibrationInputs: |
| """Default inputs: all signals at 0.8, no demotions.""" |
| base: dict[str, object] = { |
| "llm_self_report": 0.8, |
| "evidence_convergence": 0.8, |
| "subreddit_accuracy": 0.8, |
| "rule_match_strength": 0.8, |
| "validation_passed": True, |
| "cold_start": False, |
| "is_partial": False, |
| } |
| base.update(overrides) |
| return CalibrationInputs(**base) |
|
|
|
|
| |
|
|
|
|
| def test_weights_sum_to_one() -> None: |
| assert pytest.approx(1.0) == W_LLM + W_EVIDENCE + W_ACCURACY + W_RULE_MATCH |
|
|
|
|
| |
|
|
|
|
| class TestLLMDiscount: |
| def test_high_confidence_discounted(self) -> None: |
| """LLM reporting 1.0 should be compressed toward 0.5.""" |
| r = calibrate(_inputs(llm_self_report=1.0)) |
| |
| |
| |
| |
| r_naive = calibrate(_inputs(llm_self_report=0.8)) |
| |
| assert r.calibrated_confidence > r_naive.calibrated_confidence |
| |
|
|
| def test_low_confidence_preserved(self) -> None: |
| """LLM reporting 0.2 should stay low after discount.""" |
| r = calibrate(_inputs(llm_self_report=0.2)) |
| |
| |
| high = calibrate(_inputs(llm_self_report=0.8)) |
| assert r.calibrated_confidence < high.calibrated_confidence |
|
|
| def test_midpoint_unchanged(self) -> None: |
| """LLM reporting 0.5 should stay at 0.5 after discount.""" |
| |
| |
| r = calibrate(_inputs(llm_self_report=0.5)) |
| r_mid = calibrate(_inputs(llm_self_report=0.5)) |
| assert r.calibrated_confidence == r_mid.calibrated_confidence |
|
|
|
|
| |
|
|
|
|
| class TestTierAssignment: |
| def test_high_tier(self) -> None: |
| |
| r = calibrate(_inputs( |
| llm_self_report=1.0, |
| evidence_convergence=1.0, |
| subreddit_accuracy=1.0, |
| rule_match_strength=1.0, |
| )) |
| assert r.tier == "HIGH" |
| assert r.calibrated_confidence >= TIER_HIGH |
|
|
| def test_medium_tier(self) -> None: |
| r = calibrate(_inputs( |
| llm_self_report=0.6, |
| evidence_convergence=0.7, |
| subreddit_accuracy=0.6, |
| rule_match_strength=0.7, |
| )) |
| assert r.tier == "MEDIUM" |
| assert TIER_MEDIUM <= r.calibrated_confidence < TIER_HIGH |
|
|
| def test_low_tier(self) -> None: |
| r = calibrate(_inputs( |
| llm_self_report=0.1, |
| evidence_convergence=0.2, |
| subreddit_accuracy=0.3, |
| rule_match_strength=0.1, |
| )) |
| assert r.tier == "LOW" |
| assert r.calibrated_confidence < TIER_MEDIUM |
|
|
| def test_boundary_high(self) -> None: |
| """Exactly at TIER_HIGH boundary should be HIGH.""" |
| |
| |
| |
| |
| |
| |
| |
| r = calibrate(_inputs( |
| llm_self_report=0.912, |
| evidence_convergence=0.912, |
| subreddit_accuracy=0.912, |
| rule_match_strength=0.912, |
| )) |
| assert r.tier == "HIGH" |
|
|
| def test_boundary_medium(self) -> None: |
| """Just below TIER_HIGH should be MEDIUM.""" |
| r = calibrate(_inputs( |
| llm_self_report=0.8, |
| evidence_convergence=0.8, |
| subreddit_accuracy=0.8, |
| rule_match_strength=0.8, |
| )) |
| |
| assert r.tier == "MEDIUM" |
|
|
|
|
| |
|
|
|
|
| class TestDemotions: |
| def _base_high(self) -> CalibrationInputs: |
| """Inputs that produce HIGH confidence without demotions.""" |
| return _inputs( |
| llm_self_report=1.0, |
| evidence_convergence=1.0, |
| subreddit_accuracy=1.0, |
| rule_match_strength=1.0, |
| ) |
|
|
| def test_validation_failed_demotion(self) -> None: |
| base = calibrate(self._base_high()) |
| demoted = calibrate(CalibrationInputs( |
| **{**self._base_high().__dict__, "validation_passed": False} |
| )) |
| assert demoted.calibrated_confidence == pytest.approx( |
| base.calibrated_confidence * DEMOTION_VALIDATION_FAILED, abs=0.001 |
| ) |
|
|
| def test_partial_demotion(self) -> None: |
| base = calibrate(self._base_high()) |
| demoted = calibrate(CalibrationInputs( |
| **{**self._base_high().__dict__, "is_partial": True} |
| )) |
| assert demoted.calibrated_confidence == pytest.approx( |
| base.calibrated_confidence * DEMOTION_PARTIAL, abs=0.001 |
| ) |
|
|
| def test_cold_start_demotion(self) -> None: |
| base = calibrate(self._base_high()) |
| demoted = calibrate(CalibrationInputs( |
| **{**self._base_high().__dict__, "cold_start": True} |
| )) |
| assert demoted.calibrated_confidence == pytest.approx( |
| base.calibrated_confidence * DEMOTION_COLD_START, abs=0.001 |
| ) |
|
|
| def test_demotions_stack(self) -> None: |
| """All three demotions applied simultaneously.""" |
| base = calibrate(self._base_high()) |
| all_demoted = calibrate(CalibrationInputs( |
| llm_self_report=1.0, |
| evidence_convergence=1.0, |
| subreddit_accuracy=1.0, |
| rule_match_strength=1.0, |
| validation_passed=False, |
| cold_start=True, |
| is_partial=True, |
| )) |
| expected = ( |
| base.calibrated_confidence |
| * DEMOTION_VALIDATION_FAILED |
| * DEMOTION_PARTIAL |
| * DEMOTION_COLD_START |
| ) |
| assert all_demoted.calibrated_confidence == pytest.approx(expected, abs=0.001) |
|
|
| def test_demotions_can_push_tier_down(self) -> None: |
| """A HIGH-confidence result can be demoted to MEDIUM or LOW.""" |
| base = calibrate(self._base_high()) |
| assert base.tier == "HIGH" |
|
|
| demoted = calibrate(CalibrationInputs( |
| **{**self._base_high().__dict__, "validation_passed": False} |
| )) |
| assert demoted.tier != "HIGH" |
|
|
|
|
| |
|
|
|
|
| class TestClamping: |
| def test_zero_inputs_clamp_to_zero(self) -> None: |
| r = calibrate(_inputs( |
| llm_self_report=0.0, |
| evidence_convergence=0.0, |
| subreddit_accuracy=0.0, |
| rule_match_strength=0.0, |
| )) |
| assert r.calibrated_confidence >= 0.0 |
|
|
| def test_max_inputs_clamp_to_one(self) -> None: |
| r = calibrate(_inputs( |
| llm_self_report=1.0, |
| evidence_convergence=1.0, |
| subreddit_accuracy=1.0, |
| rule_match_strength=1.0, |
| )) |
| assert r.calibrated_confidence <= 1.0 |
|
|
|
|
| |
|
|
|
|
| class TestCalibrationResult: |
| def test_result_is_frozen(self) -> None: |
| r = calibrate(_inputs()) |
| with pytest.raises(AttributeError): |
| r.calibrated_confidence = 0.5 |
|
|
| def test_result_carries_breakdown(self) -> None: |
| r = calibrate(_inputs( |
| llm_self_report=0.9, |
| evidence_convergence=0.7, |
| subreddit_accuracy=0.6, |
| rule_match_strength=0.8, |
| )) |
| assert r.llm_self_report == 0.9 |
| assert r.evidence_convergence == 0.7 |
| assert r.subreddit_accuracy == 0.6 |
| assert r.rule_match_strength == 0.8 |
|
|
|
|
| |
|
|
|
|
| class TestFormulaVerification: |
| def test_known_calculation(self) -> None: |
| """Manually verify the formula for a specific input set.""" |
| inputs = _inputs( |
| llm_self_report=0.95, |
| evidence_convergence=0.88, |
| subreddit_accuracy=0.87, |
| rule_match_strength=0.96, |
| ) |
| r = calibrate(inputs) |
|
|
| |
| llm_discounted = 0.5 + (0.95 - 0.5) * LLM_DISCOUNT_FACTOR |
| expected = ( |
| W_LLM * llm_discounted |
| + W_EVIDENCE * 0.88 |
| + W_ACCURACY * 0.87 |
| + W_RULE_MATCH * 0.96 |
| ) |
| |
| assert r.calibrated_confidence == pytest.approx(expected, abs=0.001) |
|
|
| def test_canned_verdict_scenario(self) -> None: |
| """The canned verdict's breakdown (0.95, 0.88, 0.87, 0.96) should |
| produce a HIGH-tier result close to the canned 0.92.""" |
| r = calibrate(_inputs( |
| llm_self_report=0.95, |
| evidence_convergence=0.88, |
| subreddit_accuracy=0.87, |
| rule_match_strength=0.96, |
| )) |
| |
| |
| assert r.tier in {"MEDIUM", "HIGH"} |
| |
| assert 0.7 < r.calibrated_confidence < 1.0 |
|
|
|
|
| |
|
|
|
|
| class TestComputeEvidenceConvergence: |
| def test_empty_signals(self) -> None: |
| assert compute_evidence_convergence([]) == 0.0 |
|
|
| def test_single_signal(self) -> None: |
| assert compute_evidence_convergence([0.8]) == pytest.approx(0.8) |
|
|
| def test_average_of_signals(self) -> None: |
| assert compute_evidence_convergence([0.6, 0.8, 1.0]) == pytest.approx(0.8) |
|
|
| def test_all_zeros(self) -> None: |
| assert compute_evidence_convergence([0.0, 0.0]) == 0.0 |
|
|
| def test_all_ones(self) -> None: |
| assert compute_evidence_convergence([1.0, 1.0, 1.0]) == pytest.approx(1.0) |
|
|