初始化项目,由ModelHub XC社区提供模型

Model: ramankrishna10/npc-reason
Source: Original Platform
This commit is contained in:
ModelHub XC
2026-07-18 02:51:09 +08:00
commit 626c71c533
33 changed files with 1887 additions and 0 deletions

159
tests/test_verifier.py Normal file
View File

@@ -0,0 +1,159 @@
"""Tests that prove the verifier is trustworthy (it is the metric AND the later RL reward).
Run: pytest tests/test_verifier.py -q
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from verifier.step_verifier import ( # noqa: E402
extract_final_answer,
verify_assertion,
verify_chain,
)
# --------------------------------------------------------------------------- #
# 1. correct + verifiable chain
# --------------------------------------------------------------------------- #
def test_correct_and_verifiable():
text = (
"Tom packs 3 bags with 8 apples each. "
"So the total is <<3*8 = 24>> apples. "
"The answer is \\boxed{24}."
)
r = verify_chain(text, gold_answer=24)
assert r["verifiable"] is True
assert r["correct"] is True
assert r["verified_and_correct"] is True
assert r["n_assertions"] == 1 and r["n_verified"] == 1
assert r["composes_to_final"] is True
# --------------------------------------------------------------------------- #
# 2. one arithmetic error -> not verifiable, the bad step flagged
# --------------------------------------------------------------------------- #
def test_arithmetic_error_flagged():
text = "We compute <<2 + 2 = 5>> and conclude \\boxed{5}."
r = verify_chain(text, gold_answer=5)
assert r["verifiable"] is False
assert r["n_assertions"] == 1 and r["n_verified"] == 0
assert any("2 + 2" in f.get("expr", "") for f in r["failures"])
# --------------------------------------------------------------------------- #
# 3. THE fluency trap: fluent prose, right answer, NO assertions -> not verifiable
# (proves the verifier rewards mechanical grounding, not plausible text)
# --------------------------------------------------------------------------- #
def test_fluency_trap():
text = (
"We carefully add up all the quantities involved, and after thinking it "
"through it is clear the total works out to twenty-four, so the answer is "
"\\boxed{24}."
)
r = verify_chain(text, gold_answer=24)
assert r["has_loadbearing_assertions"] is False
assert r["verifiable"] is False # no load-bearing assertions
assert r["correct"] is True # final answer still matches gold
assert r["verified_and_correct"] is False # the two axes are independent
# --------------------------------------------------------------------------- #
# 4. steps verify but final answer does not compose from them -> not verifiable
# --------------------------------------------------------------------------- #
def test_non_composing_final():
text = (
"First <<10 * 2 = 20>>, then <<20 + 5 = 25>>. "
"Therefore the answer is \\boxed{30}."
)
r = verify_chain(text, gold_answer=30)
assert r["all_assertions_verified"] is True
assert r["n_verified"] == 2
assert r["composes_to_final"] is False
assert r["verifiable"] is False
assert any("compose" in f.get("reason", "") for f in r["failures"])
# --------------------------------------------------------------------------- #
# 5a. verifiable BUT WRONG (internally consistent + composes, final != gold)
# --------------------------------------------------------------------------- #
def test_verifiable_but_wrong():
text = "Clearly <<2 + 2 = 4>>, so \\boxed{4}."
r = verify_chain(text, gold_answer=5)
assert r["verifiable"] is True
assert r["correct"] is False
assert r["verified_and_correct"] is False
# --------------------------------------------------------------------------- #
# 5b. correct BUT UNVERIFIABLE — same as the fluency trap axis, asserted distinctly
# --------------------------------------------------------------------------- #
def test_correct_but_unverifiable():
text = "After some mental arithmetic the answer is \\boxed{42}."
r = verify_chain(text, gold_answer=42)
assert r["correct"] is True
assert r["verifiable"] is False
# --------------------------------------------------------------------------- #
# 6. variable binding threads forward
# --------------------------------------------------------------------------- #
def test_variable_binding():
text = (
"Let total = <<3 * 8 = 24>>. "
"Adding the bonus: <<total + 6 = 30>>. "
"So \\boxed{30}."
)
r = verify_chain(text, gold_answer=30)
assert r["n_assertions"] == 2 and r["n_verified"] == 2
assert r["verifiable"] is True and r["correct"] is True
# --------------------------------------------------------------------------- #
# 7. fail-closed on an unbound symbol (v1 out-of-scope algebra)
# --------------------------------------------------------------------------- #
def test_unbound_symbol_fails_closed():
text = "We have <<x + 2 = 5>> hence \\boxed{3}."
r = verify_chain(text, gold_answer=3)
assert r["n_verified"] == 0
assert r["verifiable"] is False
assert any("unbound" in f.get("reason", "") for f in r["failures"])
# --------------------------------------------------------------------------- #
# 8. tolerance policy: exact rationals + 1e-6 float
# --------------------------------------------------------------------------- #
def test_exact_rational():
# decimals parse as exact rationals, so this is an EXACT match
assert verify_assertion("0.1 + 0.2", "0.3", {}).ok is True
def test_float_tolerance():
r = verify_assertion("2**0.5", "1.4142135", {})
assert r.ok is True # within 1e-6 relative
def test_float_outside_tolerance():
r = verify_assertion("2**0.5", "1.41", {})
assert r.ok is False
def test_thousands_separator():
assert verify_assertion("1,000 + 234", "1,234", {}).ok is True
# --------------------------------------------------------------------------- #
# 9. final-answer extraction priority
# --------------------------------------------------------------------------- #
def test_final_answer_extraction():
assert extract_final_answer("blah \\boxed{7} blah") == "7"
assert extract_final_answer("steps...\n#### 42") == "42"
assert extract_final_answer("so the answer is 13.") == "13"
assert extract_final_answer("no answer here") is None
def test_unparseable_fails_closed():
r = verify_assertion("3 +* 4", "7", {})
assert r.ok is False