From 38200b9e1f69d1c27430a1ad38a14cfd7768386b Mon Sep 17 00:00:00 2001 From: determine123 Date: Tue, 20 Oct 2026 22:05:53 +0800 Subject: [PATCH] fix(eval): ignore thinking answers when scoring GPQA --- eval/gpqa/bench.py | 7 ++-- tests/eval/test_gpqa_answer_extraction.py | 46 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 tests/eval/test_gpqa_answer_extraction.py diff --git a/eval/gpqa/bench.py b/eval/gpqa/bench.py index 0ba4ae8..e12f6f2 100644 --- a/eval/gpqa/bench.py +++ b/eval/gpqa/bench.py @@ -117,13 +117,14 @@ def extract_answer_with_source(text: str) -> tuple[str | None, str]: """Extract the answer letter by priority and report which path matched.""" if not text: return None, "none" - matches = _ANSWER_LINE_RE.findall(text) + final_text = text.rsplit("", 1)[-1] + matches = _ANSWER_LINE_RE.findall(final_text) if matches: return matches[-1].upper(), "answer_line" - matches = _BOXED_RE.findall(text) + matches = _BOXED_RE.findall(final_text) if matches: return matches[-1].upper(), "boxed" - tail = text.rsplit("", 1)[-1] if "" in text else text[-400:] + tail = final_text if "" in text else text[-400:] matches = _STANDALONE_RE.findall(tail) if matches: return matches[-1].upper(), "tail_letter" diff --git a/tests/eval/test_gpqa_answer_extraction.py b/tests/eval/test_gpqa_answer_extraction.py new file mode 100644 index 0000000..b3eba86 --- /dev/null +++ b/tests/eval/test_gpqa_answer_extraction.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +import importlib +from pathlib import Path +from types import ModuleType + +import pytest + +pytestmark = [pytest.mark.ci, pytest.mark.ci_npu] + + +@pytest.fixture +def gpqa(monkeypatch: pytest.MonkeyPatch) -> ModuleType: + monkeypatch.syspath_prepend(str(Path(__file__).resolve().parents[2] / "eval")) + return importlib.import_module("gpqa.bench") + + +@pytest.mark.parametrize( + ("text", "expected"), + [ + ("\nANSWER: A\n\n\\boxed{B}", ("B", "boxed")), + ("\nANSWER: A\n\nB", ("B", "tail_letter")), + ("\\boxed{A}\nB", ("B", "tail_letter")), + ("\nANSWER: A\n\nI cannot answer this question.", (None, "none")), + ("\\boxed{A}", (None, "none")), + ("\nANSWER: A\n\nANSWER: C", ("C", "answer_line")), + ("\nANSWER: A\n\nANSWER: B\n\n\\boxed{D}", ("D", "boxed")), + ("ANSWER: A\nANSWER: B", ("B", "answer_line")), + ("\\boxed{C}", ("C", "boxed")), + ("The final choice is D.", ("D", "tail_letter")), + ("ANSWER: B\n\\boxed{C}", ("B", "answer_line")), + ("", (None, "none")), + ], +) +def test_extract_answer_ignores_thinking(gpqa: ModuleType, text: str, expected: tuple[str | None, str]) -> None: + assert gpqa.extract_answer_with_source(text) == expected + + +@pytest.mark.parametrize(("gold", "correct"), [("A", False), ("B", True)]) +def test_score_rollout_uses_final_answer(gpqa: ModuleType, gold: str, correct: bool) -> None: + rollout = {"task_id": "gpqa-test", "sample_idx": 0, "raw_text": "\nANSWER: A\n\n\\boxed{B}"} + row = gpqa.score_rollout(rollout, {"answer": gold}) + assert row["pred"] == "B" + assert row["extract_source"] == "boxed" + assert row["correct"] is correct + assert row["raw_text"] == rollout["raw_text"]