Skip to content

Commit da6461e

Browse files
author
lpb-docs
committed
feat(support): battle-tested benchmark prompt set (GSM8K/MATH) + thinking-lift analysis
Replace the 6 trivial prompts with 12 problems adapted from public battle-tested benchmarks, machine-verified answers: - Easy (3): GSM8K-style 2-step word problems — pass in both modes - Medium (4): GSM8K multi-step + MATH L3-style (heads/legs) — non-thinking misses some - Hard (5): MATH L4-5 style — inclusion-exclusion counting, stars and bars, coin probability, exact-value log equation, recurrence Report gains: - 'Test set & sources' section (GSM8K openai/grade-school-math, MATH hendrycks/math attribution + tier expectations) - auto Notes: thinking lift (fails at off, passes on) and persistent failures (fail at all levels = capability gap, not level effect) - intermediate /tmp saves now include fidelity + wire_params First results with the new set (12 problems, 19/19 wire fidelity): - Qwen3.8-27B: off 10/12 -> minimal/low/medium 12/12 (thinking rescues math_log_eq + math_coin_prob); high 11/12 (sampling variance) - Qwen3.6-35B-A3B-MTP: 11/12 at every level; math_log_eq fails at all levels (capability gap) — thinking gives no lift on this model set
1 parent f837ceb commit da6461e

1 file changed

Lines changed: 89 additions & 22 deletions

File tree

support/thinking-benchmark.py

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -261,29 +261,54 @@ def api_call(model: str, payload: dict, server: str, api_key: str, timeout: int
261261
# ─── Prompts & scoring ────────────────────────────────────────────────────────
262262

263263
PROMPTS = {
264-
("math_easy", "easy",
265-
"A gardener plants 4 rows of 6 tomato plants, then adds 3 more rows of 6 each. How many total?"),
266-
("logic_easy", "easy",
267-
"All dogs can swim. Max is a dog. Can Max swim? Answer yes or no."),
268-
269-
("math_med", "medium",
270-
"Solve: number * 3 + 5 = 320 - (number * 4). What is x? Show the equation."),
271-
("logic_med", "medium",
272-
"Alice Bob Charlie each have number 1 2 or 3. Alice not largest/smallest. Bob > Charlie. Charlie != 3."),
273-
274-
("math_hard", "hard",
275-
"A farmer has sheep. If he sells 140, half as many left as if he didn't sell any. Then buys 2 more. How many?"),
276-
("causal_med", "medium",
277-
"It is 95F outside. You leave a scoop of vanilla ice cream on the sidewalk for 30 minutes. What happens and why?"),
264+
# ── Easy (GSM8K-style, 2 steps — expect pass in both modes) ──
265+
("gsm_clips", "easy",
266+
"Natalia sold clips to 48 of her friends in April, and then she sold half as many clips in May. How many clips did Natalia sell altogether?"),
267+
("gsm_babysit", "easy",
268+
"Weng earns $12 an hour for babysitting. Yesterday she just worked 50 minutes. How much did she earn?"),
269+
("gsm_letter", "easy",
270+
"James writes a 3-page letter to 2 different friends twice a week. How many pages does he write a year?"),
271+
272+
# ── Medium (GSM8K multi-step / MATH L3 — expect non-thinking to miss some) ──
273+
("gsm_wallet", "medium",
274+
"Betty is saving money for a new wallet which costs $100. Betty has only half of the money she needs. Her parents decided to give her $15 for that purpose, and her grandparents twice as much. How much more money does Betty need?"),
275+
("gsm_book", "medium",
276+
"Julie is reading a 120-page book. Yesterday she was able to read 12 pages and today she read twice as many pages as yesterday. If she wants to read half of the remaining pages tomorrow, how many pages should she read?"),
277+
("gsm_flowers", "medium",
278+
"Mark planted 10 yellow flowers. He planted 80% more purple flowers than yellow flowers. He also planted 25% as many green flowers as the total of yellow and purple flowers combined. How many flowers did Mark plant in total?"),
279+
("math_heads_legs", "medium",
280+
"A farmer has chickens and rabbits in a field. There are 40 heads and 100 legs in total. How many rabbits does the farmer have?"),
281+
282+
# ── Hard (MATH L4-5 style — expect only thinking to pass) ──
283+
("math_div_or", "hard",
284+
"How many positive integers less than 1000 are divisible by 7 or 11?"),
285+
("math_balls_boxes", "hard",
286+
"In how many ways can 3 indistinguishable balls be placed into 4 distinguishable boxes?"),
287+
("math_coin_prob", "hard",
288+
"A fair coin is flipped 5 times. What is the probability of getting exactly 3 heads? Express your answer as a fraction in lowest terms."),
289+
("math_log_eq", "hard",
290+
"Solve for x: log base 2 of x plus log base 2 of (x minus 2) equals 4. Give the exact value of x."),
291+
("math_sequence", "hard",
292+
"A sequence starts 2, 3, 5, 9, 17 and each term after the first is one less than twice the previous term. What is the sixth term of the sequence?"),
278293
}
279294

295+
# Expected answer keywords — used for automated correctness checking.
296+
# Numeric keywords are matched with digit-boundary guards (no substring
297+
# false-positives like "10" inside "100"). Sources: GSM8K (openai/grade-school-math)
298+
# and MATH (hendrycks/math) style problems, adapted for single-shot scoring.
280299
ANSWER_KEYWORDS = {
281-
"math_easy": ["42"],
282-
"logic_easy": ["yes"],
283-
"math_med": ["120", "=120", "= 120", "x = 120", "x=120"],
284-
"logic_med": ["alice", "bob", "charlie"],
285-
"math_hard": ["142"],
286-
"causal_med": ["melt", "melting"],
300+
"gsm_clips": ["72"],
301+
"gsm_babysit": ["10", "$10"],
302+
"gsm_letter": ["624"],
303+
"gsm_wallet": ["5", "$5"],
304+
"gsm_book": ["42"],
305+
"gsm_flowers": ["35"],
306+
"math_heads_legs": ["10"],
307+
"math_div_or": ["220"],
308+
"math_balls_boxes": ["20"],
309+
"math_coin_prob": ["5/16"],
310+
"math_log_eq": ["\u221a17", "sqrt(17)"],
311+
"math_sequence": ["33"],
287312
}
288313

289314

@@ -505,7 +530,11 @@ def run_benchmark(models, levels, runs, server, api_key, mode="full", report_pat
505530
ts = time.strftime("%Y%m%d-%H%M%S")
506531
sf = f"/tmp/thinking_bench_{model.replace('-', '_')}_{level}_{ts}.json"
507532
with open(sf, "w") as f:
508-
json.dump({"versions": versions, "records": all_results}, f, indent=2)
533+
json.dump({"versions": versions, "records": all_results,
534+
"fidelity": [{"model": m, "level": l, "label": lb, "ok": ok, "detail": dt}
535+
for m, l, lb, ok, dt in fidelity],
536+
"wire_params": {f"{m}|{l}": p for (m, l), p in wire_params.items()}},
537+
f, indent=2)
509538

510539
status = "OK" if scoring["correct_answer"] else ("?" if scoring["valid_response"] else "NO")
511540
rc_display = f"{record['reasoning_chars']:5d}r" if record['reasoning_chars'] > 0 else " 0r"
@@ -518,7 +547,10 @@ def run_benchmark(models, levels, runs, server, api_key, mode="full", report_pat
518547
timestamp = time.strftime("%Y%m%d-%H%M%S")
519548
model_file = f"/tmp/thinking_bench_{model.replace('-', '_')}_{timestamp}.json"
520549
with open(model_file, "w") as f:
521-
json.dump({"versions": versions, "records": all_results}, f, indent=2)
550+
json.dump({"versions": versions, "records": all_results,
551+
"fidelity": [{"model": m, "level": l, "label": lb, "ok": ok, "detail": dt}
552+
for m, l, lb, ok, dt in fidelity],
553+
"wire_params": {f"{m}|{l}": p for (m, l), p in wire_params.items()}}, f, indent=2)
522554
print(f" Saved {len(all_results)} records to {model_file}")
523555

524556
# ─── Summary ──────────────────────────────────────────────────────────
@@ -731,6 +763,25 @@ def write_report(path: str, models, levels, summaries, fidelity, versions, all_r
731763
a(f"- **Varying prompts:** {', '.join(sorted(varying))} scored differently across "
732764
f"cells — consistent with sampling variance on near-boundary problems, not a "
733765
f"level effect (answers are stochastic at temperature 1.0).")
766+
# thinking lift: prompts failed at off but passed at some on-level
767+
for model in models:
768+
off_fails = {r["prompt_key"] for r in all_results
769+
if r["model"] == model and r["level"] == "off"
770+
and r.get("success") and not r["correct_answer"]}
771+
if not off_fails:
772+
continue
773+
rescued, still = [], []
774+
for pk in sorted(off_fails):
775+
on_passes = [r["level"] for r in all_results
776+
if r["model"] == model and r["prompt_key"] == pk and r["level"] != "off"
777+
and r.get("success") and r["correct_answer"]]
778+
(rescued if on_passes else still).append(pk)
779+
if rescued:
780+
a(f"- **{model}: thinking lift** — {', '.join(rescued)} fail(s) at off but "
781+
f"pass with thinking ON; this is the measurable benefit of reasoning mode.")
782+
if still:
783+
a(f"- **{model}: persistent failure** — {', '.join(still)} fails at ALL levels "
784+
f"(off and on): a model capability gap on that problem, not a thinking-level effect.")
734785

735786
failed = [r for r in all_results if not r["success"] and not r.get("template_rejection")]
736787
if failed:
@@ -756,6 +807,22 @@ def write_report(path: str, models, levels, summaries, fidelity, versions, all_r
756807

757808
failed = [r for r in all_results if not r["success"] and not r.get("template_rejection")]
758809

810+
a("")
811+
a("## Test set & sources")
812+
a("")
813+
a(f"{len(PROMPTS)} single-shot problems with deterministic answers, adapted from "
814+
"battle-tested public benchmarks:")
815+
a("")
816+
a("- **Easy (3):** GSM8K-style 2-step word problems "
817+
"(openai/grade-school-math) — expected to pass in both modes")
818+
a("- **Medium (4):** GSM8K multi-step + MATH L3-style "
819+
"(hendrycks/math) — non-thinking mode misses some")
820+
a("- **Hard (5):** MATH L4-L5 style (combinatorics, number theory, "
821+
"probability, exact-value algebra) — expected to require thinking")
822+
a("")
823+
a("Scoring: keyword match with digit-boundary guards; all answers "
824+
"machine-verified before inclusion. Prompts and keywords live in "
825+
"`support/thinking-benchmark.py` (PROMPTS / ANSWER_KEYWORDS).")
759826
a("")
760827
a("_Generated by `support/thinking-benchmark.py --report`. Re-run after every pi / "
761828
"plugin / server version bump and diff against the previous report._")

0 commit comments

Comments
 (0)