pybytecode-v3-1.5b / harness /generate.py
coolblaze03's picture
Add files using upload-large-folder tool
0b19a1b verified
Raw
History Blame Contribute Delete
3.79 kB
#!/usr/bin/env python3
"""Generate predictions from an OpenAI-compatible endpoint. The ONLY script here that needs a model.
Every published number reproduces from the cached generations without ever running this — see
harness/README.md. This exists so the cache can be regenerated, or a different model evaluated.
PYBYTECODE_ENDPOINT=http://localhost:1234/v1 PYBYTECODE_MODEL=my-model \\
./generate.py --bench ../benchmarks/csn-3.12-licensed/bench.jsonl --out gen.jsonl
Resumable: an existing --out is read first and completed rows are skipped, so an interrupted run
costs nothing. Output is streamed and flushed per row, never accumulated in memory.
"""
from __future__ import annotations
import argparse
import json
import sys
import urllib.error
import urllib.request
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from common import load_bench # noqa: E402
from config import API_KEY, ENDPOINT, MODEL # noqa: E402
INSTRUCTION = (
"Decompile this Python 3.12 bytecode disassembly back into the original Python source code. "
"Output only the source code."
)
def complete(prompt: str, temperature: float, max_tokens: int, seed: int | None) -> str:
body = {
"model": MODEL,
"messages": [{"role": "user", "content": prompt}],
"temperature": temperature,
"max_tokens": max_tokens,
}
if seed is not None:
body["seed"] = seed
req = urllib.request.Request(
f"{ENDPOINT.rstrip('/')}/chat/completions",
data=json.dumps(body).encode(),
headers={"Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}"},
)
with urllib.request.urlopen(req, timeout=600) as r:
return json.loads(r.read())["choices"][0]["message"]["content"]
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--bench", required=True)
ap.add_argument("--out", required=True)
ap.add_argument("--temperature", type=float, default=0.0)
ap.add_argument("--samples", type=int, default=1, help=">1 emits an `s` field per sample")
ap.add_argument("--max-tokens", type=int, default=2048)
ap.add_argument("--limit", type=int, default=0)
a = ap.parse_args()
bench, _ = load_bench(a.bench)
out_path = Path(a.out)
done: set[tuple[int, int]] = set()
if out_path.exists():
for line in out_path.read_text().splitlines():
if line.strip():
r = json.loads(line)
done.add((r["i"], r.get("s", 0)))
print(f"resuming: {len(done)} generations already present", file=sys.stderr)
todo = sorted(bench)[: a.limit or None]
print(f"endpoint={ENDPOINT} model={MODEL} rows={len(todo)} samples={a.samples}", file=sys.stderr)
with out_path.open("a") as f:
for i in todo:
for s in range(a.samples):
if (i, s) in done:
continue
prompt = f"{INSTRUCTION}\n\n{bench[i]['input']}"
try:
got = complete(prompt, a.temperature, a.max_tokens, None if a.samples == 1 else s)
except (urllib.error.URLError, OSError) as e:
raise SystemExit(
f"cannot reach {ENDPOINT}: {e}\n"
"Set PYBYTECODE_ENDPOINT to your OpenAI-compatible server, or use the "
"cached generations and skip this script entirely."
)
rec = {"i": i, "got": got}
if a.samples > 1:
rec["s"] = s
f.write(json.dumps(rec) + "\n")
f.flush()
if i % 25 == 0:
print(f" {i}/{len(todo)}", file=sys.stderr, flush=True)
if __name__ == "__main__":
main()