From ebc20b792e0e54f7bd797e7000f3f3e5c62e1296 Mon Sep 17 00:00:00 2001
From: Lanre Shittu <136805224+Shizoqua@users.noreply.github.com>
Date: Sat, 7 Nov 2026 11:12:49 +0000
Subject: [PATCH] fix(livemath): score (B), [B] and **B** as the choice they
name
normalize_label stripped trailing ".", ")" and ":" but nothing at the
front, so a model answering (B) was parsed as "(B" and
scored wrong even when B was correct. [B] and **B** failed the same way,
while B, B. and B) passed. hard is the exact-match result, so these
answers got zero reward during training and counted as misses in the
reported accuracy.
Strip surrounding Markdown bold and a leading "(" or "[" along with the
existing trailing punctuation. Wrong labels and free text such as
"Option B" are still not matched.
---
CHANGELOG.md | 2 ++
.../envs/livemathematicianbench/evaluator.py | 3 +-
tests/test_livemath_evaluator.py | 33 +++++++++++++++++++
3 files changed, 37 insertions(+), 1 deletion(-)
create mode 100644 tests/test_livemath_evaluator.py
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3babc82d..0349a2e63 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -147,6 +147,8 @@ All notable changes to SkillOpt are documented here. This project adheres to
- Skip step-level edits whose target runs into a protected slow-update or
appendix region, so a `replace` or `delete` can no longer remove the start
marker and expose the protected content (thanks @Shizoqua).
+- Score LiveMathematicianBench answers written as `(B)`, `[B]`, or `**B**` as
+ the choice they name instead of marking them wrong (thanks @Shizoqua).
### Security
- Prevent managed-identity credentials from being sent to non-Azure or
diff --git a/skillopt/envs/livemathematicianbench/evaluator.py b/skillopt/envs/livemathematicianbench/evaluator.py
index d15db3e3b..feb615fd5 100644
--- a/skillopt/envs/livemathematicianbench/evaluator.py
+++ b/skillopt/envs/livemathematicianbench/evaluator.py
@@ -15,7 +15,8 @@ def extract_answer(text: str) -> str:
def normalize_label(text: str) -> str:
- return str(text).strip().upper().rstrip(".):")
+ """Normalize a choice label, accepting ``B``, ``B.``, ``(B)``, ``[B]`` and ``**B**``."""
+ return str(text).strip().strip("*").strip().lstrip("([").upper().rstrip(".):]")
def parse_choice_label(prediction_text: str, choices: list[dict]) -> str:
diff --git a/tests/test_livemath_evaluator.py b/tests/test_livemath_evaluator.py
new file mode 100644
index 000000000..5efa4762e
--- /dev/null
+++ b/tests/test_livemath_evaluator.py
@@ -0,0 +1,33 @@
+import pytest
+
+from skillopt.envs.livemathematicianbench.evaluator import evaluate
+
+CHOICES = [
+ {"label": "A", "text": "2"},
+ {"label": "B", "text": "3"},
+ {"label": "C", "text": "5"},
+]
+
+
+@pytest.mark.parametrize(
+ "response",
+ [
+ "B",
+ "B.",
+ "B)",
+ "(B)",
+ "[B]",
+ "**B**",
+ "(B) 3",
+ ],
+)
+def test_wrapped_choice_label_is_scored_correct(response):
+ result = evaluate(response, CHOICES[1], CHOICES)
+
+ assert result["predicted_label"] == "B"
+ assert result["em"] == 1.0
+
+
+@pytest.mark.parametrize("response", ["(A)", "Option B", ""])
+def test_wrong_or_unparseable_label_is_not_scored_correct(response):
+ assert evaluate(response, CHOICES[1], CHOICES)["em"] == 0.0