oe-eval-bcb-lite-evaluator / run_bcb_experiment.py
thinkahead's picture
Use batches
30d6c14
Raw
History Blame Contribute Delete
7.29 kB
"""
Run BigCodeBench samples from a JSONL file against the live evaluator
in batches, then report which tasks pass, fail, or timeout.
Usage:
python run_bcb_experiment.py \
--filename /tmp/bigcodebench-instruct-codegen_results-sanitized_calibrated.jsonl \
--batch-size 50
The results-sanitized_calibrated.jsonl file already has the fields the
/evaluate/ endpoint expects (task_id, res_id, test, solution, entry_point,
code_prompt). The requests.jsonl can also be used — this script detects its
nested structure and extracts the needed fields from doc + doc.choices.
"""
import argparse
import json
import sys
import time
import httpx
DEFAULT_URL = "http://localhost:7860/evaluate/"
TIMEOUT = 600 # per-batch timeout in seconds
def load_jsonl(path):
with open(path) as f:
return [json.loads(line) for line in f if line.strip()]
def normalize_sample(raw):
"""
Accept either format:
- results-sanitized_calibrated.jsonl (flat: task_id, res_id, test, solution, ...)
- requests.jsonl (nested: doc.task_id, doc.test, doc.choices[0], ...)
Return a dict suitable for the /evaluate/ endpoint.
"""
if "solution" in raw and "test" in raw:
# Already in the right shape
return {
"task_id": raw["task_id"],
"res_id": raw.get("res_id", 0),
"test": raw["test"],
"solution": raw["solution"],
"entry_point": raw["entry_point"],
"code_prompt": raw.get("code_prompt", ""),
}
# requests.jsonl format — extract from doc + choices
doc = raw.get("doc", {})
choices = doc.get("choices", [])
solution = choices[0] if choices else doc.get("canonical_solution", "")
# Prepend code_prompt to the choice body to form a complete solution
code_prompt = doc.get("code_prompt", "")
full_solution = code_prompt + solution
return {
"task_id": doc["task_id"],
"res_id": raw.get("idx", 0),
"test": doc["test"],
"solution": full_solution,
"entry_point": doc["entry_point"],
"code_prompt": code_prompt,
}
def send_batch(client, url, samples, batch_idx, calibrate, retries=3, backoff=30):
for attempt in range(1, retries + 1):
label = f" Batch {batch_idx}" + (f" (attempt {attempt})" if attempt > 1 else "")
print(f"{label}: sending {len(samples)} samples ...", end=" ", flush=True)
try:
t0 = time.time()
resp = client.post(
url,
json=samples,
params={"calibrate": calibrate, "min_time_limit": 1},
)
elapsed = time.time() - t0
except (httpx.ReadTimeout, httpx.ConnectTimeout, httpx.ConnectError,
httpx.ReadError, httpx.RemoteProtocolError) as exc:
print(f"connection error: {exc}")
if attempt < retries:
wait = backoff * attempt
print(f" Waiting {wait}s before retry ...")
time.sleep(wait)
continue
if resp.status_code == 200:
print(f"OK ({elapsed:.1f}s)")
return resp.json()
print(f"HTTP {resp.status_code} ({elapsed:.1f}s)")
if attempt < retries:
wait = backoff * attempt
print(f" Waiting {wait}s before retry ...")
time.sleep(wait)
print(f" Batch {batch_idx}: all {retries} attempts failed")
return None
def main():
parser = argparse.ArgumentParser(description="Run BCB experiments against live evaluator")
parser.add_argument("--filename", required=True, help="Path to JSONL file")
parser.add_argument("--batch-size", type=int, default=50, help="Samples per request (default 50)")
parser.add_argument("--limit", type=int, default=0, help="Max samples to evaluate (0 = all)")
parser.add_argument("--calibrate", action="store_true", default=False,
help="Enable calibrate mode (prepend code_prompt+'pass')")
parser.add_argument("--url", type=str, default=DEFAULT_URL, help=f"Evaluate endpoint URL (default {DEFAULT_URL})")
parser.add_argument("--output", type=str, default=None, help="Save detailed results to JSONL")
parser.add_argument("--retries", type=int, default=3, help="Retries per batch on failure (default 3)")
parser.add_argument("--backoff", type=int, default=30, help="Base backoff seconds between retries (default 30)")
parser.add_argument("--delay", type=int, default=2, help="Delay seconds between batches (default 2)")
args = parser.parse_args()
raw_data = load_jsonl(args.filename)
samples = [normalize_sample(r) for r in raw_data]
if args.limit > 0:
samples = samples[: args.limit]
evaluate_url = args.url
print(f"Loaded {len(samples)} samples from {args.filename}")
print(f"Endpoint: {evaluate_url}")
print(f"Batch size: {args.batch_size}, calibrate: {args.calibrate}\n")
all_results = [] # flat list of per-sample result dicts
batches = [samples[i : i + args.batch_size] for i in range(0, len(samples), args.batch_size)]
skipped_samples = []
with httpx.Client(timeout=TIMEOUT) as client:
for idx, batch in enumerate(batches, 1):
result = send_batch(client, evaluate_url, batch, idx, args.calibrate,
retries=args.retries, backoff=args.backoff)
if result is None:
skipped_samples.extend(batch)
continue
for task_results in result["eval"].values():
all_results.extend(task_results)
if idx < len(batches) and args.delay > 0:
time.sleep(args.delay)
# --- Summary ---
passed = [r for r in all_results if r["status"] == "pass"]
failed = [r for r in all_results if r["status"] == "fail"]
timed_out = [r for r in all_results if r["status"] == "timeout"]
print("\n" + "=" * 65)
print(f"{'RESULTS SUMMARY':^65}")
print("=" * 65)
print(f" Total evaluated : {len(all_results)}")
print(f" Passed : {len(passed)}")
print(f" Failed : {len(failed)}")
print(f" Timed out : {len(timed_out)}")
if all_results:
print(f" pass@1 : {len(passed) / len(all_results):.4f}")
if skipped_samples:
print(f" Skipped (server errors): {len(skipped_samples)}")
print("=" * 65)
# Show failed task IDs
if failed:
print(f"\nFailed tasks ({len(failed)}):")
for r in sorted(failed, key=lambda x: x["task_id"]):
detail_keys = list(r.get("details", {}).keys())
short = ", ".join(detail_keys[:3])
if len(detail_keys) > 3:
short += f" ... (+{len(detail_keys) - 3} more)"
print(f" {r['task_id']:<30} failing: {short}")
if timed_out:
print(f"\nTimed-out tasks ({len(timed_out)}):")
for r in sorted(timed_out, key=lambda x: x["task_id"]):
print(f" {r['task_id']}")
# Save detailed output
if args.output:
with open(args.output, "w") as f:
for r in all_results:
f.write(json.dumps(r) + "\n")
print(f"\nDetailed results saved to {args.output}")
if __name__ == "__main__":
main()