RTX 5090 · llama.cpp + llama-swap · Q8 · ctx 262,144
CASE FILE GEMMA-4 · DISTILLATION TEARDOWN
FINDING CLAIM UNSUPPORTED CODE + REASONING · EXECUTED
The "distilled from a giant teacher" hypothesis

A distilled 12B was supposed to out-reason the stock one.

It didn't — not even at its own job. This 12B was distilled for Python coding, from >1-trillion-parameter frontier teachers (Composer 2.5 + Claude Fable 5). Yet Google's plain gemma-4-12B beat it on executable code — 96% vs 89% pass@1 on HumanEval — and never lost a reasoning category besides. Here is exactly what we ran, and how we know.

HumanEval · distilled
89%
pass@1, executed
HumanEval · stock
96%
pass@1, executed
Won on its own game?
no
−7 pts on code
Reasoning cells to stock
4 / 4
+ all of coding
Listen instead — AI narration in the author's voice
Exhibit A · the contestants

Same size. Same quant. One claim to fame.

Both are 12B parameters at Q8 — so this isolates the distillation effect, not a size advantage. The only difference that should matter is where the weights learned to think.

Subject

Distilled 12B

  • TeachersComposer 2.5 + Fable 5
  • Distilled forverifiable Python
  • Parameters12 B · Q8
  • Pitch"a better reasoner"
vs
Control

Stock 12B

gemma-4-12b-it-UD-Q8_K_XL.gguf
  • OriginGoogle release
  • Parameters12 B
  • QuantQ8 (UD)
  • Pitchthe baseline
Exhibit B · the battery

249 problems across five test sets.

Every item is identical for both models, seed-fixed, with the served model logged per answer. One set is the model's home turf; the other four ask whether the distillation bought reasoning that transfers. We show all of it — on-domain and off — so the result can't be waved away as the wrong test.

On-domain what the model card says it is for
164
HumanEval
The model completes each Python function; its code is then executed against hidden unit tests in a sandbox. pass@1, greedy. This is distilled-for territory — full results in Exhibit D.
Off-domain does any of the teacher's reasoning transfer?
30
GSM8K
Grade-school multi-step arithmetic word problems.
20
MATH-500
Competition math — algebra, geometry, number theory.
20
BBH logic
Logical-deduction ordering puzzles. Pure reasoning, no math.
15
MMLU-Pro
Hard science & law, 10-way multiple choice.
164 HumanEval  +  30 GSM8K  +  20 MATH-500  +  20 BBH  +  15 MMLU-Pro 249 items · both models · seed 1234 · provenance logged
// PROTOCOLone model resident at a time · single GPU · served-model id logged per item — no self-comparison
reasoning track85 items · identical CoT prompt + "ANSWER: <x>" contract · temp 0.6 · run thinking OFF and ON → a 2×2
coding track164 items · model writes the function · greedy (temp 0) · code extracted and EXECUTED against hidden tests
scoringnumeric / symbolic-equiv for math · letter-match for MC · pass/fail by unit-test execution for code
Exhibit C · the verdict

Stock wins the 2×2. No exceptions.

Overall accuracy, distilled vs stock, under each thinking condition. The bars fill to score.

Direct answer — thinking OFF
Distilled88%
Stock94%
75/85 vs 80/85stock +6 pts
Deliberate — thinking ON
Distilled85%
Stock91%
72/85 vs 77/85stock +6 pts
▸ stock's 91% is a floor — 6 items overran the 16k-token budget (false zeros), so its true score is higher.
Categorydist · offstock · offdist · onstock · on
BBH · logic100%100%100%100%
GSM8K · math90%97%97%97%
MATH-50085%95%70%90%
MMLU-Pro73%80%60%67%
OVERALL88%94%85%91%floor
Exhibit D · home turf

So we tested it on its own game.

Reasoning is off-domain for this model — its card calls it a fine-tune on verifiable Python coding, distilled from solutions that were run against tests and kept only if they passed. So the fair test isn't trivia; it's code. We ran HumanEval — 164 problems, each candidate executed against hidden unit tests in a sandbox. pass@1, greedy. It lost here too — and this time the gap is statistically real.

HumanEval — pass@1, executed
Distilled89.0%
Stock96.3%
146/164 vs 158/164stock +7.3 pts
Per-problem, head-to-head
Solved only by stock14
Solved only by distilled2
discordant 14 : 2McNemar p ≈ 0.006
▸ unlike the reasoning margins, this clears significance — not just directional.
// HARNESS164 HumanEval tasks · model completes the function · we run its code against the hidden tests
sandboxreliability_guard + macOS seatbelt (deny network) + 10s timeout · self-test: 164/164 canonical pass
decodinggreedy (temp 0) · 0 truncations on either model · served-model id logged per item
failures17/18 distilled misses were real running code that failed tests — not format or truncation artifacts
Exhibit E · chain of custody

How we know it's real, not an artifact.

A clean-looking benchmark can hide four different lies. Each was checked before the verdict was trusted.

We measured ability, not format

A reasoning-distilled model loves to show its work, which can fake a lead. So both models got the identical chain-of-thought prompt and the same ANSWER: contract — a distilled win would mean reasoning, not just verbosity.

We proved which model actually answered

An earlier config quietly collapsed both model IDs onto a single resident file. After that, every response's served-model id was logged per item — so we can prove the distilled model wasn't benchmarked against itself. Provenance is in the results file.

We caught the token-budget trap

With thinking on, the first run's MMLU-Pro cratered to 47% — but two answers had simply run out of tokens mid-thought and scored zero with no ANSWER:. We raised the budget to 16k and re-ran. A silent truncation would have invented a fake collapse.

We gave the distilled model its best shot

The whole pitch of distilling from a reasoning teacher lives in the thinking channel — so we tested it on. Both models genuinely produced reasoning traces. The distilled model still lost, and thinking lowered its score rather than raising it.

Exhibit F · the tell

When it thought harder, it talked itself out of it.

On two free-response MATH problems, the distilled model answered correctly when replying directly — then, with thinking on, deliberated its way to a multiple-choice letter on a question that had no options at all.

math-3 · gold 2  |  direct 2 ✓  |  thinking "B" ✗
math-6 · gold 1/3  |  direct 1/3 ✓  |  thinking "C" ✗
Exhibit G · cost of thought

More tokens. Not more right.

Average tokens spent per MMLU-Pro answer with thinking on. Stock deliberates ~5× longer — and still only reaches a floor of 67%. Length is not the lever.

Distilled · thinking on1,742 tok
Stock · thinking on8,971 tok
Distilled · direct401 tok
Stock · direct887 tok

The distilled model's one genuine difference is brevity — it answers in a fraction of the tokens. That conciseness kept it close on easy problems, but it isn't a reasoning advantage: on the hard ones it still fell behind. Distillation here compressed the model, it didn't sharpen its reasoning.

Exhibit H · what this does & doesn't prove

The honest fine print.

Sample size

On the reasoning suite (N = 85), the 6-point gaps are ~1.4σ — directional, not significant in isolation; confidence there comes from the same direction repeating across all four cells. The coding result is the firm one: N = 164, significant on its own (p ≈ 0.006).

Scope & domain

We tested both off-domain (reasoning) and on-domain (HumanEval — what it was built for). It lost both. So this isn't an off-domain gotcha — but it's still one checkpoint, one quant; not a universal law that distillation can't help.

The floor

Stock's thinking-on score (91%) is suppressed by 6 truncated items. Fixing them only moves stock up — it never flips the result.

To harden it further

Add MBPP+ and HumanEval+ (extra hidden tests), plus a thinking-on coding pass. The HumanEval gap is already significant (p ≈ 0.006) — more data widens it, it doesn't flip.

Exhibit I · reproduce it

The whole harness, open.

Four scripts, the exact seeded items both models saw, and every per-item prediction tagged with the served_model that produced it. Read the code below, or take the bundle and re-score it yourself. The only redaction is the llama-swap host and a local path.

prep_data.py Builds the reasoning set → data/*.jsonl (GSM8K 30 · MATH-500 20 · BBH 20 · MMLU-Pro 15), seed 1234 78 lines
#!/usr/bin/env python3
"""Fetch seeded reasoning-eval samples -> data/*.jsonl. Network only (no model server)."""
import json, random, os, sys
from datasets import load_dataset

OUT = os.path.join(os.path.dirname(__file__), "data")
os.makedirs(OUT, exist_ok=True)
SEED = 1234

# (name, n) — directional defaults; override via argv: prep_data.py gsm8k=100 math=50 ...
DEFAULTS = {"gsm8k": 30, "math": 20, "bbh": 20, "gpqa": 15}
overrides = dict(kv.split("=") for kv in sys.argv[1:] if "=" in kv)
N = {k: int(overrides.get(k, v)) for k, v in DEFAULTS.items()}

def save(name, rows):
    p = os.path.join(OUT, f"{name}.jsonl")
    with open(p, "w") as f:
        for r in rows: f.write(json.dumps(r) + "\n")
    print(f"  saved {len(rows):>4} -> {p}")

def sample(ds, n):
    idx = list(range(len(ds)))
    random.Random(SEED).shuffle(idx)
    return [ds[i] for i in idx[:n]]

print("Fetching eval samples (seed=%d)..." % SEED)

# GSM8K — answer after '#### '
try:
    ds = load_dataset("openai/gsm8k", "main", split="test")
    rows = [{"id": f"gsm8k-{i}", "category": "gsm8k",
             "question": r["question"],
             "answer": r["answer"].split("####")[-1].strip().replace(",", "")}
            for i, r in enumerate(sample(ds, N["gsm8k"]))]
    save("gsm8k", rows)
except Exception as e:
    print(f"  GSM8K FAILED: {e}")

# MATH-500 — clean ungated test set with explicit 'answer' field
try:
    ds = load_dataset("HuggingFaceH4/MATH-500", split="test")
    rows = [{"id": f"math-{i}", "category": "math",
             "question": r["problem"], "answer": str(r["answer"]).strip(),
             "level": r.get("level"), "type": r.get("subject")}
            for i, r in enumerate(sample(ds, N["math"]))]
    save("math", rows)
except Exception as e:
    print(f"  MATH FAILED: {e}")

# BBH — use a multi-step subset (causal_judgement is y/n; use 'logical_deduction_three_objects' MC)
try:
    ds = load_dataset("lukaemon/bbh", "logical_deduction_three_objects", split="test")
    rows = [{"id": f"bbh-{i}", "category": "bbh",
             "question": r["input"], "answer": r["target"].strip("()").strip()}
            for i, r in enumerate(sample(ds, N["bbh"]))]
    save("bbh", rows)
except Exception as e:
    print(f"  BBH FAILED: {e} (may need a different config name)")

# MMLU-Pro — ungated, 10-option hard knowledge+reasoning (harder than MMLU/GPQA-easy)
try:
    ds = load_dataset("TIGER-Lab/MMLU-Pro", split="test")
    # focus on reasoning-heavy categories
    hard = ds.filter(lambda r: r["category"] in
                     {"physics", "engineering", "math", "chemistry", "law"})
    rows = []
    for i, r in enumerate(sample(hard, N["gpqa"])):
        labels = "ABCDEFGHIJ"
        q = r["question"] + "\n" + "\n".join(
            f"{labels[k]}) {opt}" for k, opt in enumerate(r["options"]))
        rows.append({"id": f"mmlupro-{i}", "category": "mmlu_pro",
                     "question": q, "answer": r["answer"], "subject": r["category"]})
    save("mmlu_pro", rows)
except Exception as e:
    print(f"  MMLU-Pro SKIPPED: {e}")

print("Done. Files in", OUT)
prep_code.py Builds the coding set → data_code/humaneval.jsonl (164 problems) 31 lines
#!/usr/bin/env python3
"""Fetch HumanEval -> data_code/humaneval.jsonl.

Each row: task_id, prompt (signature+docstring), canonical_solution (body),
test (defines check()), entry_point (function name).

Usage:
  python3 prep_code.py            # all 164
  python3 prep_code.py 20         # first 20 (smoke test)
"""
import json, os, sys
from datasets import load_dataset

HERE = os.path.dirname(__file__)
OUT = os.path.join(HERE, "data_code"); os.makedirs(OUT, exist_ok=True)

def main():
    n = int(sys.argv[1]) if len(sys.argv) > 1 else 0  # 0 = all
    ds = load_dataset("openai/openai_humaneval", split="test")
    rows = [{"task_id": r["task_id"], "prompt": r["prompt"],
             "canonical_solution": r["canonical_solution"],
             "test": r["test"], "entry_point": r["entry_point"]} for r in ds]
    if n: rows = rows[:n]
    p = os.path.join(OUT, "humaneval.jsonl")
    with open(p, "w") as f:
        for r in rows: f.write(json.dumps(r) + "\n")
    print(f"wrote {len(rows)} -> {p}")

if __name__ == "__main__":
    main()
run_eval.py Reasoning runner — identical CoT prompt + ANSWER: contract · scoring · --compare · served-model provenance 163 lines
#!/usr/bin/env python3
"""Run reasoning eval against an OpenAI-compatible /v1/chat/completions endpoint.

Usage:
  python3 run_eval.py --base http://LLAMA_SWAP_HOST:PORT --model <id> --tag distilled
  python3 run_eval.py --base ... --model <id> --tag stock --key sk-...
Then compare:
  python3 run_eval.py --compare results/distilled.jsonl results/stock.jsonl
"""
import json, os, re, time, argparse, urllib.request, glob
from concurrent.futures import ThreadPoolExecutor

HERE = os.path.dirname(__file__)
DATA = os.path.join(HERE, "data")
RES = os.path.join(HERE, "results"); os.makedirs(RES, exist_ok=True)

# Identical for both models — measures reasoning ABILITY, not default verbosity.
SYS = ("You are a careful problem solver. Think step by step, then end your reply "
       "with a single final line in exactly this format:\nANSWER: <your final answer>\n"
       "For multiple-choice, the final answer is the single letter of the correct option.")

def load_items():
    items = []
    for f in sorted(glob.glob(os.path.join(DATA, "*.jsonl"))):
        with open(f) as fh:
            items += [json.loads(l) for l in fh if l.strip()]
    return items

def call(base, model, key, question, max_tokens, temp, timeout, top_p=0.95, think=False):
    payload = {"model": model, "temperature": temp, "top_p": top_p,
               "max_tokens": max_tokens,
               "messages": [{"role": "system", "content": SYS},
                            {"role": "user", "content": question}]}
    if think:
        payload["chat_template_kwargs"] = {"enable_thinking": True}
    body = json.dumps(payload).encode()
    req = urllib.request.Request(base.rstrip("/") + "/v1/chat/completions", data=body,
                                 headers={"Content-Type": "application/json",
                                          **({"Authorization": f"Bearer {key}"} if key else {})})
    t0 = time.time()
    with urllib.request.urlopen(req, timeout=timeout) as r:
        d = json.load(r)
    dt = time.time() - t0
    msg = d["choices"][0]["message"]
    text = msg.get("content") or msg.get("reasoning_content") or ""
    usage = d.get("usage", {})
    return text, usage.get("completion_tokens"), dt, d.get("model")

def extract(text):
    m = list(re.finditer(r"ANSWER:\s*(.+)", text, re.I))
    if m: return m[-1].group(1).strip()
    b = list(re.finditer(r"\\boxed\{([^}]*)\}", text))
    if b: return b[-1].group(1).strip()
    return text.strip().splitlines()[-1].strip() if text.strip() else ""

def norm_num(s):
    s = s.replace(",", "").replace("$", "").replace("\\", "").strip().rstrip(".")
    m = re.search(r"-?\d+\.?\d*", s)
    return m.group(0) if m else None

def norm_text(s):
    return re.sub(r"[\s${}\\]", "", s).lower()

def score(cat, pred, gold):
    p = extract(pred) if False else pred  # pred already extracted by caller
    if cat in ("bbh", "mmlu_pro"):
        up = p.upper()
        m = re.search(r"\b([A-J])\b", up) or re.search(r"([A-J])\)", up) or re.search(r"[A-J]", up)
        return bool(m) and m.group(1 if m.re.groups else 0) == gold.strip().upper()
    if cat == "gsm8k":
        a, b = norm_num(p), norm_num(gold)
        if a is None or b is None: return False
        try: return abs(float(a) - float(b)) < 1e-6
        except: return a == b
    # math: numeric if possible, else normalized string
    a, b = norm_num(p), norm_num(gold)
    if a is not None and b is not None:
        try:
            if abs(float(a) - float(b)) < 1e-6: return True
        except: pass
    return norm_text(p) == norm_text(gold)

def run(args):
    items = load_items()
    out = os.path.join(RES, f"{args.tag}.jsonl")
    print(f"[{args.tag}] {len(items)} items -> {args.model} @ {args.base}")

    def work(it):
        try:
            text, ctoks, dt, served = call(args.base, args.model, args.key, it["question"],
                                           args.max_tokens, args.temp, args.timeout, args.top_p,
                                           args.think)
            pred = extract(text)
            ok = bool(score(it["category"], pred, it["answer"]))
            return {**{k: it[k] for k in ("id", "category", "answer")},
                    "pred": pred, "correct": ok, "completion_tokens": ctoks,
                    "latency_s": round(dt, 2), "served_model": served, "raw": text}
        except Exception as e:
            return {**{k: it[k] for k in ("id", "category", "answer")},
                    "pred": None, "correct": False, "error": str(e)}

    rows = []
    with ThreadPoolExecutor(max_workers=args.concurrency) as ex:
        for i, r in enumerate(ex.map(work, items), 1):
            rows.append(r)
            flag = "ok " if r["correct"] else ("ERR" if r.get("error") else "x  ")
            print(f"  [{i:>3}/{len(items)}] {flag} {r['id']:<14} "
                  f"gold={str(r['answer'])[:18]:<18} pred={str(r['pred'])[:24]}")
    with open(out, "w") as f:
        for r in rows: f.write(json.dumps(r) + "\n")
    served = sorted({r.get("served_model") for r in rows if r.get("served_model")})
    print(f"\nSERVED MODEL(S) this run: {served}")
    summarize(rows, args.tag)
    print(f"saved -> {out}")

def summarize(rows, tag):
    cats = {}
    for r in rows:
        c = cats.setdefault(r["category"], {"n": 0, "ok": 0, "tok": 0, "tn": 0})
        c["n"] += 1; c["ok"] += int(r["correct"])
        if r.get("completion_tokens"): c["tok"] += r["completion_tokens"]; c["tn"] += 1
    print(f"\n=== {tag} ===")
    print(f"{'category':<12}{'acc':>10}{'avg_tok':>10}")
    tot_n = tot_ok = 0
    for c, v in sorted(cats.items()):
        tot_n += v["n"]; tot_ok += v["ok"]
        at = (v["tok"] / v["tn"]) if v["tn"] else 0
        print(f"{c:<12}{v['ok']:>3}/{v['n']:<3}{100*v['ok']/v['n']:>5.0f}%{at:>10.0f}")
    print(f"{'OVERALL':<12}{tot_ok:>3}/{tot_n:<3}{100*tot_ok/tot_n:>5.0f}%")

def compare(a, b):
    def load(p): return {r["id"]: r for r in (json.loads(l) for l in open(p))}
    A, B = load(a), load(b)
    ta, tb = os.path.basename(a).split(".")[0], os.path.basename(b).split(".")[0]
    cats = {}
    for i in A:
        if i not in B: continue
        c = cats.setdefault(A[i]["category"], [0, 0, 0])
        c[0] += 1; c[1] += int(A[i]["correct"]); c[2] += int(B[i]["correct"])
    print(f"{'category':<12}{ta:>10}{tb:>10}{'Δ':>8}")
    TA = TB = TN = 0
    for c, (n, x, y) in sorted(cats.items()):
        TN += n; TA += x; TB += y
        print(f"{c:<12}{100*x/n:>9.0f}%{100*y/n:>9.0f}%{100*(x-y)/n:>+7.0f}")
    print(f"{'OVERALL':<12}{100*TA/TN:>9.0f}%{100*TB/TN:>9.0f}%{100*(TA-TB)/TN:>+7.0f}")
    print(f"\nN={TN}. Note: ~±{int(100*(0.5/ (TN**0.5))) }pt rough 1-sigma per model; "
          "treat small gaps as noise.")

if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--base"); ap.add_argument("--model"); ap.add_argument("--key")
    ap.add_argument("--tag", default="run")
    ap.add_argument("--max-tokens", type=int, default=8192, dest="max_tokens")
    ap.add_argument("--temp", type=float, default=0.6)
    ap.add_argument("--top-p", type=float, default=0.95, dest="top_p")
    ap.add_argument("--timeout", type=int, default=300)
    ap.add_argument("--think", action="store_true", help="send chat_template_kwargs enable_thinking=true")
    ap.add_argument("--concurrency", type=int, default=4)
    ap.add_argument("--compare", nargs=2)
    a = ap.parse_args()
    if a.compare: compare(*a.compare)
    else: run(a)
run_code.py Coding runner — generate → extract → execute in sandbox vs hidden tests · --selftest gate · pass@1 200 lines
#!/usr/bin/env python3
"""Execution-graded code eval (HumanEval pass@1) against an OpenAI-compatible endpoint.

The model is asked to complete a function; we extract its code block, assemble it with
the task's hidden test, and RUN it under a layered sandbox:
  - reliability_guard: neuters destructive syscalls (os.system, subprocess, rmtree, unlink, ...)
  - sandbox-exec '(allow default)(deny network*)': blocks all network (macOS Seatbelt)
  - fresh temp cwd, PYTHONDONTWRITEBYTECODE, hard timeout w/ SIGKILL

ALWAYS run --selftest first: it executes the CANONICAL solutions and must score ~100%.
If it doesn't, the executor is broken and no model number means anything.

Usage:
  python3 run_code.py --selftest
  python3 run_code.py --base http://LLAMA_SWAP_HOST:PORT --model <distilled-id> --tag code-distilled --key $API_KEY
  python3 run_code.py --base http://LLAMA_SWAP_HOST:PORT --model <stock-id>     --tag code-stock     --key $API_KEY
  python3 run_code.py --compare results_code/code-distilled.jsonl results_code/code-stock.jsonl
"""
import json, os, re, sys, time, argparse, tempfile, subprocess, urllib.request
from concurrent.futures import ThreadPoolExecutor

HERE = os.path.dirname(__file__)
DATA = os.path.join(HERE, "data_code")
RES = os.path.join(HERE, "results_code"); os.makedirs(RES, exist_ok=True)

SYS = ("You are an expert Python programmer. Complete the function described by the user. "
       "You may briefly reason about edge cases, complexity, and approach, but you MUST end "
       "your reply with the complete, runnable function inside a single ```python code block. "
       "Include the full function definition (signature and body) and any needed imports. "
       "Do not include example usage, explanation after the code, or tests.")

# Neutered inside the executed subprocess, before any candidate code runs.
GUARD = r"""
import os, shutil, subprocess, builtins, faulthandler
faulthandler.disable()
builtins.exit = None
builtins.quit = None
for _n in ("system","popen","remove","removedirs","rmdir","unlink","rename","renames",
           "replace","truncate","kill","killpg","fork","forkpty","chmod","chown","chroot"):
    if hasattr(os, _n): setattr(os, _n, None)
shutil.rmtree = None; shutil.move = None
subprocess.Popen = None; subprocess.run = None; subprocess.call = None
"""

def load_items():
    with open(os.path.join(DATA, "humaneval.jsonl")) as f:
        return [json.loads(l) for l in f if l.strip()]

# ---------- generation ----------
def call(base, model, key, prompt, max_tokens, temp, timeout, think=False):
    payload = {"model": model, "temperature": temp, "top_p": 1.0, "max_tokens": max_tokens,
               "messages": [{"role": "system", "content": SYS},
                            {"role": "user", "content": prompt}]}
    if think:
        payload["chat_template_kwargs"] = {"enable_thinking": True}
    req = urllib.request.Request(base.rstrip("/") + "/v1/chat/completions",
                                 data=json.dumps(payload).encode(),
                                 headers={"Content-Type": "application/json",
                                          **({"Authorization": f"Bearer {key}"} if key else {})})
    t0 = time.time()
    with urllib.request.urlopen(req, timeout=timeout) as r:
        d = json.load(r)
    dt = time.time() - t0
    ch = d["choices"][0]
    msg = ch["message"]
    text = msg.get("content") or msg.get("reasoning_content") or ""
    usage = d.get("usage", {})
    return text, usage.get("completion_tokens"), ch.get("finish_reason"), dt, d.get("model")

def extract_code(text):
    blocks = re.findall(r"```(?:python|py)?\s*\n(.*?)```", text, re.S)
    if blocks:
        return blocks[-1]
    m = re.search(r"(?:^|\n)(?:import |from |def |class )", text)
    return text[m.start():] if m else text

# ---------- execution ----------
def build_program(item, code):
    ep = item["entry_point"]
    has_def = re.search(r"\bdef\s+" + re.escape(ep) + r"\b", code)
    body = code if has_def else (item["prompt"] + "\n" + code)
    return body + "\n\n" + item["test"] + f"\n\ncheck({ep})\n"

def execute(program, timeout, use_sandbox=True):
    src = GUARD + "\n" + program
    with tempfile.TemporaryDirectory() as td:
        fp = os.path.join(td, "cand.py")
        with open(fp, "w") as f:
            f.write(src)
        py = [sys.executable, "-I", "-B", fp]
        cmd = (["sandbox-exec", "-p", "(version 1)(allow default)(deny network*)"] + py) if use_sandbox else py
        env = {**os.environ, "PYTHONDONTWRITEBYTECODE": "1", "OMP_NUM_THREADS": "1"}
        try:
            r = subprocess.run(cmd, cwd=td, env=env, timeout=timeout,
                               stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
            return (r.returncode == 0), ("" if r.returncode == 0 else r.stderr.decode("utf-8", "replace")[-240:])
        except subprocess.TimeoutExpired:
            return False, "timeout"
        except Exception as e:
            return False, f"exec-error: {e}"

# ---------- self-test: canonical solutions must pass ----------
def selftest(timeout, use_sandbox):
    items = load_items()
    print(f"[selftest] executing {len(items)} CANONICAL solutions "
          f"(sandbox={'on' if use_sandbox else 'off'})")
    ok = 0; fails = []
    for it in items:
        prog = build_program(it, it["canonical_solution"])
        passed, err = execute(prog, timeout, use_sandbox)
        ok += passed
        if not passed: fails.append((it["task_id"], err))
    print(f"[selftest] {ok}/{len(items)} canonical solutions pass")
    for tid, err in fails[:10]:
        print(f"   FAIL {tid}: {err}")
    if ok < len(items):
        print("\n!! executor not clean — fix before trusting model numbers.")
        if use_sandbox:
            print("   (retry with --no-sandbox to check if Seatbelt is the cause)")
    return ok == len(items)

# ---------- run a model ----------
def run(args):
    items = load_items()
    if args.limit: items = items[:args.limit]
    out = os.path.join(RES, f"{args.tag}.jsonl")
    print(f"[{args.tag}] {len(items)} items -> {args.model} @ {args.base}")

    def work(it):
        try:
            text, ctoks, finish, dt, served = call(args.base, args.model, args.key, it["prompt"],
                                                    args.max_tokens, args.temp, args.timeout, args.think)
            code = extract_code(text)
            passed, err = execute(build_program(it, code), args.exec_timeout, not args.no_sandbox)
            return {"task_id": it["task_id"], "entry_point": it["entry_point"],
                    "passed": bool(passed), "finish_reason": finish, "exec_err": err,
                    "completion_tokens": ctoks, "latency_s": round(dt, 2),
                    "served_model": served, "raw": text}
        except Exception as e:
            return {"task_id": it["task_id"], "entry_point": it["entry_point"],
                    "passed": False, "error": str(e)}

    rows = []
    with ThreadPoolExecutor(max_workers=args.concurrency) as ex:
        for i, r in enumerate(ex.map(work, items), 1):
            rows.append(r)
            flag = "ok " if r["passed"] else ("ERR" if r.get("error") else "x  ")
            trunc = " [TRUNC]" if r.get("finish_reason") == "length" else ""
            print(f"  [{i:>3}/{len(items)}] {flag} {r['task_id']:<16} {r.get('exec_err','')[:40]}{trunc}")
    with open(out, "w") as f:
        for r in rows: f.write(json.dumps(r) + "\n")

    served = sorted({r.get("served_model") for r in rows if r.get("served_model")})
    npass = sum(r["passed"] for r in rows)
    trunc = sum(1 for r in rows if r.get("finish_reason") == "length")
    avgtok = ([r["completion_tokens"] for r in rows if r.get("completion_tokens")] or [0])
    print(f"\nSERVED MODEL(S) this run: {served}")
    print(f"=== {args.tag} ===")
    print(f"pass@1: {npass}/{len(rows)} = {100*npass/len(rows):.1f}%")
    print(f"avg completion tokens: {sum(avgtok)//len(avgtok)}   truncated (finish=length): {trunc}")
    if trunc:
        print(f"  !! {trunc} items hit max_tokens — likely false zeros. Re-run with higher --max-tokens.")
    print(f"saved -> {out}")

def compare(a, b):
    def load(p): return {r["task_id"]: r for r in (json.loads(l) for l in open(p))}
    A, B = load(a), load(b)
    ta, tb = os.path.basename(a).split(".")[0], os.path.basename(b).split(".")[0]
    ids = [i for i in A if i in B]
    pa = sum(A[i]["passed"] for i in ids); pb = sum(B[i]["passed"] for i in ids)
    n = len(ids)
    print(f"{'metric':<16}{ta:>16}{tb:>16}")
    print(f"{'pass@1':<16}{100*pa/n:>15.1f}%{100*pb/n:>15.1f}%")
    print(f"{'solved':<16}{pa:>16}{pb:>16}  (N={n})")
    only_a = [i for i in ids if A[i]["passed"] and not B[i]["passed"]]
    only_b = [i for i in ids if B[i]["passed"] and not A[i]["passed"]]
    print(f"\nsolved only by {ta}: {len(only_a)}  -> {', '.join(only_a[:12])}")
    print(f"solved only by {tb}: {len(only_b)}  -> {', '.join(only_b[:12])}")
    sig = int(100 * (0.5 / (n ** 0.5)))
    print(f"\nN={n}. Rough ±{sig}pt 1-sigma per model; treat small gaps as directional.")

if __name__ == "__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--base"); ap.add_argument("--model"); ap.add_argument("--key")
    ap.add_argument("--tag", default="code-run")
    ap.add_argument("--max-tokens", type=int, default=6144, dest="max_tokens")
    ap.add_argument("--temp", type=float, default=0.0)
    ap.add_argument("--timeout", type=int, default=300, help="HTTP timeout per request")
    ap.add_argument("--exec-timeout", type=int, default=10, dest="exec_timeout", help="per-candidate run timeout")
    ap.add_argument("--think", action="store_true")
    ap.add_argument("--concurrency", type=int, default=4)
    ap.add_argument("--limit", type=int, default=0)
    ap.add_argument("--no-sandbox", action="store_true", help="drop sandbox-exec (reliability_guard only)")
    ap.add_argument("--selftest", action="store_true")
    ap.add_argument("--compare", nargs=2)
    a = ap.parse_args()
    if a.selftest: sys.exit(0 if selftest(a.exec_timeout, not a.no_sandbox) else 1)
    elif a.compare: compare(*a.compare)
    else: run(a)
Download the full bundledistill-eval-harness.tar.gz · 312 KB · scripts + seeded items + per-item outputs