Download scripts/needle_eval.py from marcyano/Groot-1-S: direct link, hf CLI and curl.
- Browser
- Download file 4.84 kB
-
https://huggingface.co/marcyano/Groot-1-S/resolve/main/scripts/needle_eval.py
- Command line
-
hf download hf://marcyano/Groot-1-S/scripts/needle_eval.py
-
curl -L -o needle_eval.py https://huggingface.co/marcyano/Groot-1-S/resolve/main/scripts/needle_eval.py
4.84 kB
| """Needle-in-a-haystack: Groot vs Jev on the same requests, accuracy and latency per state length. | |
| python scripts/needle_eval.py --rows needle_eval.jsonl --model runs/x/final [--jev] [--limit-per-length 50] --out needle.json | |
| """ | |
| import argparse | |
| import json | |
| import os | |
| import statistics | |
| import time | |
| from collections import defaultdict | |
| from concurrent.futures import ThreadPoolExecutor | |
| from groot1.data import read_jsonl | |
| def to_question(r): | |
| if r["type"] == "choice": | |
| crit = {} | |
| for o in r["options"]: | |
| name, _, desc = o.partition(": ") | |
| crit[name] = desc or None | |
| return {"type": "choice", "instructions": r["question"], "criteria": crit}, list(crit) | |
| return {"type": "noul", "instructions": r["question"]}, ["false", "true"] | |
| def gold_of(r, names): | |
| return names[max(range(len(r["target"])), key=r["target"].__getitem__)] | |
| def pred_of(ans, kind): | |
| if kind == "choice": | |
| return ans["choice"] | |
| return "true" if ans["noul"] >= 0.5 else "false" | |
| def run_groot(rows, model, device, overrides=None): | |
| from groot1.serve import GrootEngine | |
| from groot1.schema import SystemOneRequest | |
| eng = GrootEngine(model, device=device, overrides=overrides) | |
| out = [] | |
| for r in rows: | |
| q, names = to_question(r) | |
| req = SystemOneRequest(state=r["state"], questions={"q": q}) | |
| t0 = time.perf_counter() | |
| resp = eng.answer(req.state, req.questions) | |
| dt = (time.perf_counter() - t0) * 1000 | |
| a = resp.answers["q"].model_dump() | |
| out.append({"ok": pred_of(a, r["type"]) == gold_of(r, names), "ms": dt, "tokens": resp.usage.input_tokens}) | |
| return out | |
| def run_jev(rows, workers=8): | |
| import httpx | |
| key = os.environ["TYPESAFE_API_KEY"] | |
| def one(r): | |
| q, names = to_question(r) | |
| long = r["needle"]["length"] > 30000 | |
| headers = {"Authorization": f"Bearer {key}", **({"TypeSafe-Danger-Zone-Long-Context": "true"} if long else {})} | |
| body = {"model": "jev-latest", "state": r["state"], "questions": {"q": q}} | |
| for attempt in range(4): | |
| try: | |
| t0 = time.perf_counter() | |
| resp = httpx.post("https://api.typesafe.ai/v1/systemone", json=body, headers=headers, timeout=120) | |
| dt = (time.perf_counter() - t0) * 1000 | |
| if resp.status_code == 429: | |
| time.sleep(2 ** attempt) | |
| continue | |
| if resp.status_code != 200: | |
| return {"ok": False, "ms": dt, "error": f"{resp.status_code} {resp.text[:120]}"} | |
| a = resp.json()["answers"]["q"] | |
| return {"ok": pred_of(a, r["type"]) == gold_of(r, names), "ms": dt, "tokens": resp.json()["usage"]["input_tokens"]} | |
| except Exception as e: # network hiccup | |
| err = str(e) | |
| time.sleep(2 ** attempt) | |
| return {"ok": False, "ms": None, "error": err} | |
| with ThreadPoolExecutor(workers) as ex: | |
| return list(ex.map(one, rows)) | |
| def summarize(rows, res): | |
| by = defaultdict(list) | |
| for r, x in zip(rows, res): | |
| by[r["needle"]["length"]].append(x) | |
| out = {} | |
| for L in sorted(by): | |
| xs = by[L] | |
| ms = [x["ms"] for x in xs if x.get("ms") is not None] | |
| out[L] = {"n": len(xs), "acc": round(sum(x["ok"] for x in xs) / len(xs), 3), "p50_ms": round(statistics.median(ms), 1) if ms else None, | |
| "errors": sum(1 for x in xs if "error" in x)} | |
| return out | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--rows", required=True) | |
| ap.add_argument("--model", default=None) | |
| ap.add_argument("--device", default="cuda") | |
| ap.add_argument("--jev", action="store_true") | |
| ap.add_argument("--limit-per-length", type=int, default=None) | |
| ap.add_argument("--out", default=None) | |
| ap.add_argument("--set", action="append", default=[], help="config override key=value") | |
| args = ap.parse_args() | |
| rows, seen = [], defaultdict(int) | |
| for r in read_jsonl(args.rows): | |
| L = r["needle"]["length"] | |
| if args.limit_per_length and seen[L] >= args.limit_per_length: | |
| continue | |
| seen[L] += 1 | |
| rows.append(r) | |
| report = {} | |
| if args.model: | |
| from groot1.config import parse_overrides | |
| report["groot"] = summarize(rows, run_groot(rows, args.model, args.device, parse_overrides(args.set))) | |
| print("groot", json.dumps(report["groot"]), flush=True) | |
| if args.jev: | |
| res = run_jev(rows) | |
| report["jev"] = summarize(rows, res) | |
| errs = [x["error"] for x in res if "error" in x][:3] | |
| print("jev", json.dumps(report["jev"]), ("errors: " + str(errs)) if errs else "", flush=True) | |
| if args.out: | |
| open(args.out, "w").write(json.dumps(report, indent=2)) | |
| if __name__ == "__main__": | |
| main() | |