ttishere's picture
Publish code/eval
76d1db9 verified
Raw History Blame Contribute Delete
4.94 kB
"""Chat-aware HumanEval / MBPP for LLaDA-8B-Instruct.
WHY THIS EXISTS
lm-eval ships humaneval and mbpp as raw-COMPLETION tasks: the harness executes
`doc["prompt"] + response` (humaneval) or the response verbatim (mbpp, which has no
filter at all). An SFT chat model does not complete -- it answers. Measured on
LLaDA-8B-Instruct, 2026-08-18, dense, humaneval doc 0:
"To implement the function `has_close_elements`, we need to iterate through the
list of numbers and check each pair ... Here's the implementation of the
function:\n\n```python\nfrom typing import List\n"
Two independent failures in that one string:
1. Prose. Concatenated onto the function signature it is not valid Python, so every
item raises SyntaxError -> pass@1 = 0 (which is exactly what the smoke run gave).
2. Truncation. humaneval's stock until list is ["\nclass", "\ndef", "\n#", "\nif",
"\nprint"] -- written for a model that completes a function body and then starts
the next top-level statement. Here the model emits "```python\nfrom typing import
List\n" and the NEXT thing it writes is "\ndef has_close_elements", so the stock
stop sequence decapitates the answer inside its own code fence.
So the instruct variants (a) stop on <|eot_id|>, the actual end-of-turn marker, instead
of on Python keywords, and (b) run the response through the extractor below before it
reaches code_eval.
Note LLaDA generation is diffusion, not autoregressive: gen_length tokens are always
produced and `until` only post-trims the decoded string. Dropping the keyword stops
therefore costs nothing in compute -- it only stops us from cutting the answer.
FALLBACK POLICY: if no fenced block is found, fall back to the stock raw-completion
behaviour. That way a response that happens to be a bare completion still scores, and
this file can never do worse than the stock task.
"""
import re
import evaluate as hf_evaluate
try:
_code_eval = hf_evaluate.load("code_eval")
_code_eval.compute(references=["assert add(2, 3)==5"],
predictions=[["def add(a,b): return a*b"]], k=[1])
except Exception as e:
raise e
# ```python ... ``` / ```py ... ``` / ``` ... ```
_FENCE = re.compile(r"```(?:python|py)?[ \t]*\r?\n(.*?)(?:```|\Z)", re.DOTALL)
# strip any special tokens that survived the harness decode
_SPECIAL = re.compile(r"<\|(?:eot_id|endoftext|startoftext|start_header_id|end_header_id)\|>")
def _fenced_blocks(text):
return [b for b in (m.group(1) for m in _FENCE.finditer(text or "")) if b.strip()]
def _pick_block(text, must_contain=None):
"""Best fenced code block, or None.
Prefers a block that defines the thing we are about to call; among equals prefers
the LAST one, since a chat answer often shows a sketch first and the final version
last. Falls back to the longest block when nothing matches must_contain.
"""
blocks = _fenced_blocks(_SPECIAL.sub("", text))
if not blocks:
return None
if must_contain:
named = [b for b in blocks if must_contain in b]
if named:
return named[-1]
defs = [b for b in blocks if re.search(r"^\s*(def|class|import|from)\s", b, re.M)]
if defs:
return defs[-1]
return max(blocks, key=len)
def build_predictions_chat(resps, docs):
"""humaneval: full program to execute, per (doc, sample)."""
out = []
for resp, doc in zip(resps, docs):
prompt, entry = doc["prompt"], doc.get("entry_point") or ""
preds = []
for r in resp:
block = _pick_block(r, must_contain=f"def {entry}" if entry else None)
if block and (not entry or f"def {entry}" in block):
preds.append(block) # fence already carries imports + def
else:
preds.append(prompt + _SPECIAL.sub("", r or "")) # stock behaviour
out.append(preds)
return out
def extract_code_mbpp(resps, docs):
"""mbpp: the response IS the program (its reference is the assert list)."""
out = []
for resp, _doc in zip(resps, docs):
preds = []
for r in resp:
block = _pick_block(r)
preds.append(block if block else _SPECIAL.sub("", r or ""))
out.append(preds)
return out
def pass_at_k(references, predictions, k=None):
assert k is not None
if isinstance(k, int):
k = [k]
return _code_eval.compute(references=references, predictions=predictions, k=k)[0]
def pass_at_1(references, predictions):
return _code_eval.compute(references=references, predictions=[predictions],
k=[1])[0]["pass@1"]
def list_fewshot_samples():
"""mbpp 3-shot exemplars -- byte-identical to lm_eval/tasks/mbpp/utils.py so the
only difference between mbpp and mbpp_instruct is the chat handling."""
from lm_eval.tasks.mbpp.utils import list_fewshot_samples as _stock
return _stock()