refactor: fail-loud unit grading and stricter orphan pair drop

Address review: replace bare KeyError with a contextual ValueError invariant
check in _grade_unit (fail-loud, no catch/skip/fallback); tighten
_drop_orphan_pairs to require exactly one original + one mirror with no extra
illegal-role records (total==2); clarify InferenceResult docstring to unit-grained
semantics. Add tests for missing-prediction descriptive error and extra-illegal-role
pair drop.
This commit is contained in:
2026-07-15 07:05:37 -04:00
parent 61bfa0e633
commit 8a69a54078
2 changed files with 101 additions and 20 deletions
@@ -238,6 +238,45 @@ class TestUnitLevelAggregation:
assert result.correct == 1
assert any("orphan" in msg for msg in captured), "孤儿 pair 未告警(静默)"
def test_pair_with_extra_illegal_role_dropped(self) -> None:
"""pair_id 下混入额外非法 role 记录(total>2)→ 整对剔除、不计入 total。"""
questions = [
_make_question("s1", answer="B"),
_make_question("po", answer="B", pair_id="p", question_role="pair_original"),
_make_question("pm", answer="A", pair_id="p", question_role="pair_mirror"),
# 共享 pair_id 的额外非法记录(重复 original 角色)
_make_question("px", answer="C", pair_id="p", question_role="pair_original"),
]
records = [
_make_record("s1", prediction="B", answer="B"),
_make_record("po", prediction="B", answer="B"),
_make_record("pm", prediction="A", answer="A"),
_make_record("px", prediction="C", answer="C"),
]
captured: list[str] = []
sink_id = logger.add(captured.append, level="WARNING", format="{message}")
try:
result = _aggregate_results(records, questions, "run-illegal-role")
finally:
logger.remove(sink_id)
assert result.total == 1 # 仅 single 存活,非法配对整对剔除
assert result.correct == 1
assert any("total=3" in msg for msg in captured), "非法配对未告警(静默)"
def test_unit_missing_prediction_raises_descriptive_error(self) -> None:
"""unit 缺 prediction → 描述性 ValueErrorfail-loud,非静默、非裸 KeyError)。
故意破坏聚合不变量(questions 含 s2 但 records 无 s2),验证带上下文报错。
"""
questions = [
_make_question("s1", answer="B"),
_make_question("s2", answer="A"),
]
records = [_make_record("s1", prediction="B", answer="B")] # 缺 s2 的 record
with pytest.raises(ValueError, match=r"缺 prediction.*聚合不变量被破坏"):
_aggregate_results(records, questions, "run-broken-invariant")
def test_empty_records_returns_zero(self) -> None:
"""空 records/questions → 零值结果。"""
result = _aggregate_results([], [], "run-empty")