SPB Git

spb/wp30_uqo

Public
26.1 KB · 379 lines python
1"""Trial planner and executor for single-handoff experiments (Tracks A and B; one process; laptop).23A *condition* is a method + parameters + question-access regime. A *trial* is (condition, model pair, document, question).4Question-blind messages/caches are built once per (condition, document) and reused across that document's questions —5this is the reuse scenario, and the build cost is recorded on every trial as `doc_build_ms` with `reuse_count` = number of6questions that shared it. The runner is resumable: successful trial keys are skipped; failures are kept and retried once.7"""8from __future__ import annotations9import json, time, traceback, yaml, sys, os10from pathlib import Path11import numpy as np12import mlx.core as mx1314from .util import ROOT, read_json, write_json, sha256_json, sha256_text, utc_now, Timer, read_jsonl15from .records import new_record, write_record, trial_key16from .data.prepare import load_doc, load_key17from .scoring import score_synthetic, score_public18from .data.exec_tasks import run_hidden_tests19from .prompts import SYSTEM_RECEIVER, SYSTEM_RECEIVER_NOMSG, receiver_user_text, receiver_question_only20from .backends.mlx_backend import MLXModel, deep_copy_cache, memory_snapshot, reset_peak21from .cache.sparse import quantize_cache, cache_tensor_bytes, to_sparse_all22from .cache.selection import select_cache23from .cache.serialize import build_manifest, serialize_cache, deserialize_cache, codec_compress, codec_decompress24from .methods import text as TM2526FIDELITY = {"C0": "n/a", "C2": "custom", "C3": "custom", "A0": "custom", "A1": "custom", "A2": "custom", "A3": "custom", "A4": "custom", "A5": "custom", "A6": "custom", "A7": "custom",27            "A8": "official", "B0": "custom", "B1": "custom", "B2": "custom", "B2b": "inspired", "B3": "inspired", "B4": "custom",28            "B5": "faithful_reimplementation", "B6": "faithful_reimplementation", "B9": "custom"}2930import platform as _pf31HARDWARE_ID = {"MacBookPro-de-simon-pierre-2.local": "laptop-m5max-48g", "MacStudnpierre2": "m3u96a-m3ultra-96g", "m3u96b": "m3u96b-m3ultra-96g"}.get(_pf.node().split(".")[0] if _pf.node().startswith("MacStud") or _pf.node().startswith("m3u") else _pf.node(), _pf.node())3233def _load_models(cfg):34    mcfg = yaml.safe_load(open(ROOT / "configs/models.yaml"))["models"]35    cache = {}36    def get(alias):37        if alias not in cache:38            cache[alias] = MLXModel(mcfg[alias]["repo"])39        return cache[alias]40    return get, mcfg4142def _select_items(cfg):43    man = read_json(ROOT / "data/manifests/splits.json")44    items = [it for it in man["items"] if it["split"] in cfg["splits"] and it["family"] in cfg["families"]]45    if cfg.get("lengths"):46        items = [it for it in items if it.get("target_tokens") is None or it["target_tokens"] in cfg["lengths"]]47    lim = cfg.get("limit_docs_per_family")48    if lim:49        out = []50        for fam in cfg["families"]:51            out += [it for it in items if it["family"] == fam][:lim]52        items = out53    return items5455def _cond_id(c: dict) -> str:56    parts = [c["method"]]57    for k in sorted(c):58        if k not in ("method", "access", "note", "families", "max_questions"):59            parts.append(f"{k}={c[k]}")60    if "access" in c:61        parts.append(c["access"])62    return "|".join(parts)6364def plan(config_path: str, output: str) -> int:65    cfg = yaml.safe_load(open(config_path))66    items = _select_items(cfg)67    rows = []68    pairs = cfg["model_pairs"]69    for it in items:70        doc = load_doc(it["document_id"], it["family"])71        nq0 = len(doc["questions"]) if not cfg.get("max_questions_per_doc") else min(len(doc["questions"]), cfg["max_questions_per_doc"])72        if cfg.get("question_range"): nq0 = len(doc["questions"][cfg["question_range"][0]:cfg["question_range"][1]])73        for pair in pairs:74            for c in cfg["conditions"]:75                if c.get("families") and it["family"] not in c["families"]:76                    continue77                nq = min(nq0, c["max_questions"]) if c.get("max_questions") else nq078                for seed in cfg.get("seeds", [0]):79                    rows.append({"experiment_id": cfg["experiment_id"], "condition": _cond_id(c), "method": c["method"], "access": c.get("access", "none"),80                                 "sender": pair["sender"], "receiver": pair["receiver"], "family": it["family"], "document_id": it["document_id"],81                                 "split": it["split"], "target_tokens": it.get("target_tokens"), "n_questions": nq, "seed": seed, "expected_trials": nq})82    import csv83    Path(output).parent.mkdir(parents=True, exist_ok=True)84    with open(output, "w", newline="") as f:85        w = csv.DictWriter(f, fieldnames=list(rows[0].keys())); w.writeheader(); w.writerows(rows)86    tot = sum(r["expected_trials"] for r in rows)87    print(f"plan: {len(items)} documents, {len(cfg['conditions'])} conditions, {len(pairs)} model pairs -> {len(rows)} doc-conditions, {tot} trials -> {output}")88    return 08990# --------------------------------------------------------------------------------------------------------------------91class DocContext:92    """Per-document receiver state: tokenized prefix and dense cache (built lazily once)."""93    def __init__(self, receiver: MLXModel, doc_text: str, family: str):94        self.receiver = receiver; self.doc_text = doc_text; self.family = family95        self._dense = None; self.build_ms = None; self.prefix_ids = None; self.full_ids_fn = None96        self.msg_cache = {}97        self.sys = SYSTEM_RECEIVER98    def prefix_tokens(self):99        """Receiver prefix = chat tokens up to (and including) the document and the blank line before 'Question:'.100        Computed as the longest common token prefix between the prefix text and a full prompt with a canonical question."""101        if self.prefix_ids is None:102            pre_txt = self.receiver.tokenizer.apply_chat_template([{"role": "system", "content": self.sys}, {"role": "user", "content": "Document:\n" + self.doc_text + "\n\n"}], add_generation_prompt=False, tokenize=False)103            pre = self.receiver.encode(pre_txt)104            full = self.receiver.chat_prompt_tokens([{"role": "system", "content": self.sys}, {"role": "user", "content": receiver_user_text("Document", self.doc_text, "What is stated?", self.family)}])105            n = 0106            while n < min(len(pre), len(full)) and pre[n] == full[n]:107                n += 1108            self.prefix_ids = full[:n]109        return self.prefix_ids110    def split_ids(self, question: str):111        """Full chat tokens for (doc, question); returns (prefix_ids, query_ids) with prefix a true token prefix."""112        full = self.receiver.chat_prompt_tokens([{"role": "system", "content": self.sys}, {"role": "user", "content": receiver_user_text("Document", self.doc_text, question, self.family)}])113        pre = self.prefix_tokens()114        if full[:len(pre)] != pre:115            raise RuntimeError("prefix_boundary: question tokenization altered the document prefix")116        return pre, full[len(pre):]117    def dense_cache(self, prefix_ids):118        if self._dense is None or self._dense_len != len(prefix_ids):119            reset_peak()120            c, t = self.receiver.prefill(prefix_ids)121            self._dense = c; self._dense_len = len(prefix_ids); self.build_ms = t["prefill_ms"]; self.build_peak = int(mx.get_peak_memory())122        return self._dense123124def _score(family: str, pred: str, qkey: dict, key: dict) -> dict:125    if family == "exec":126        r = run_hidden_tests(pred, key["hidden_tests"])127        return {"correct": r["correct"], "pass_rate": r["pass_rate"], "error": r.get("error")}128    if family.startswith("public_"):129        return score_public(pred, [qkey["answer"]] + list(qkey.get("aliases", [])))130    return score_synthetic(pred, qkey)131132def run(config_path: str, resume: bool = False, limit: int | None = None, dry_run: bool = False) -> int:133    cfg = yaml.safe_load(open(config_path))134    res = yaml.safe_load(open(ROOT / "configs/resources.yaml"))135    protocol_hash = cfg.get("protocol_hash") or "unfrozen"136    config_hash = sha256_json(cfg)137    out_path = ROOT / "runs/raw" / f"{cfg['experiment_id']}.jsonl"138    done = set()139    if resume and out_path.exists():140        for r in read_jsonl(out_path):141            if r["status"] == "success":142                done.add(r["trial_id"])143    items = _select_items(cfg)144    get_model, mcfg = _load_models(cfg)145    gen_cfg = cfg.get("generation", {})146    gen_max = gen_cfg.get("max_tokens", 48)147    n_done = n_run = n_fail = 0148    log_path = ROOT / "runs/logs" / f"{cfg['experiment_id']}.log"149    log_path.parent.mkdir(parents=True, exist_ok=True)150    def log(msg):151        line = f"{utc_now()} {msg}"152        print(line, flush=True)153        with open(log_path, "a") as f: f.write(line + "\n")154    log(f"run start experiment={cfg['experiment_id']} config_hash={config_hash[:12]} protocol={protocol_hash[:12]} items={len(items)} resume={resume} already_done={len(done)}")155    if dry_run:156        return plan(config_path, str(ROOT / "protocol" / f"{cfg['experiment_id']}_dryrun.csv"))157    t_start = time.time()158    for it in items:159        fam = it["family"]; doc = load_doc(it["document_id"], fam); key = load_key(it["document_id"], fam)160        qs = doc["questions"][: cfg.get("max_questions_per_doc") or None]161        if cfg.get("question_range"):   # exploratory extensions: a slice of the question list, e.g. [5, 15]162            a, b = cfg["question_range"]; qs = doc["questions"][a:b]163        gen_max = gen_cfg.get("per_family", {}).get(fam, gen_cfg.get("max_tokens", 48))164        qkeys = {q["question_id"]: q for q in key["questions"]}165        for pair in cfg["model_pairs"]:166            receiver = get_model(pair["receiver"]); sender = get_model(pair["sender"])167            dc = DocContext(receiver, doc["text"], fam)168            for cond in cfg["conditions"]:169                if cond.get("families") and fam not in cond["families"]:170                    continue171                cid = _cond_id(cond); method = cond["method"]; access = cond.get("access", "none")172                if method.startswith("B") or method.startswith("C"):173                    access = cond.get("access", "blind")174                for seed in cfg.get("seeds", [0]):175                    # trials for this (doc, cond, pair, seed)176                    pending = []177                    for q in qs[: cond.get("max_questions") or None]:178                        tk = trial_key(cfg.get("trial_key_experiment_id", cfg["experiment_id"]), it["document_id"], q["question_id"], method, {k: v for k, v in cond.items() if k not in ("method", "families", "max_questions")},179                                       pair["sender"], pair["receiver"], access, protocol_hash, seed)180                        if tk in done:181                            n_done += 1; continue182                        pending.append((q, tk))183                    if not pending:184                        continue185                    if limit is not None and n_run >= limit:186                        log(f"limit {limit} reached"); return 0187                    # ---- build the question-blind message / cache once per document (reuse scenario)188                    shared = None; shared_err = None189                    if access in ("blind", "none"):190                        try:191                            shared = _build_message(method, cond, doc["text"], None, sender, receiver, dc, seed)192                        except Exception as e:193                            shared_err = e194                            log(f"BUILD FAIL {cid} {it['document_id']}: {type(e).__name__}: {str(e)[:200]}")195                    for q, tk in pending:196                        rec = _run_one(cfg, cond, cid, method, access, pair, it, doc, q, qkeys.get(q["question_id"]), key, sender, receiver, dc, seed, tk, shared, shared_err, gen_max, protocol_hash, config_hash, len(pending))197                        write_record(rec, out_path)198                        n_run += 1199                        if rec["status"] != "success": n_fail += 1200                        sc = rec["task_scores"] or {}201                        log(f"{cid:>40s} {it['document_id']:>28s} {q['question_id']:>14s} {rec['status']:>10s} corr={sc.get('correct', sc.get('f1'))} bytes={rec['serialized_payload_bytes']} ms={rec['total_wall_ms'] and round(rec['total_wall_ms'])} ans={json.dumps((rec['prediction_artifact'] or '')[:60])}")202                        if limit is not None and n_run >= limit:203                            log(f"limit {limit} reached"); return 0204            mx.clear_cache()205    log(f"run end: ran={n_run} skipped_done={n_done} failed={n_fail} elapsed_s={round(time.time()-t_start)}")206    return 0207208def _build_message(method, cond, doc_text, question, sender, receiver, dc: DocContext, seed):209    """Returns dict with message (Track A) or cache payload info (Track B); encode timings included."""210    t = Timer(); t.start("encode")211    out = {"method": method}212    if method == "A0":213        out["msg"] = TM.a0_no_message()214    elif method == "A1":215        out["msg"] = TM.a1_full(doc_text)216    elif method == "A2":217        out["msg"] = TM.a2_truncate(doc_text, cond["budget_tokens"], receiver.tokenizer)218    elif method == "A3":219        out["msg"] = TM.a3_extractive(doc_text, cond["budget_tokens"], receiver.tokenizer, question)220    elif method == "A4":221        out["msg"] = TM.a4_summary(doc_text, cond["budget_tokens"], sender, question)222    elif method == "A5":223        out["msg"] = TM.a5_structured(doc_text, cond["budget_tokens"], sender, question)224        if hasattr(dc, "msg_cache"): dc.msg_cache[("A5", cond["budget_tokens"], question)] = out["msg"]225    elif method == "A6":226        key5 = ("A5", cond["budget_tokens"], question)227        ev = dc.msg_cache.get(key5) if hasattr(dc, "msg_cache") else None228        out["msg"] = TM.a6_references(doc_text, cond["budget_tokens"], sender, question, evidence_msg=ev)229        out["reused_a5"] = ev is not None230    elif method == "A7":231        out["msg"] = TM.a7_codec(doc_text, cond.get("codec", "zstd"))232    elif method == "A8":233        out["msg"] = TM.a8_llmlingua(doc_text, cond["budget_tokens"], receiver.tokenizer, question, device=cond.get("device", "mps"))234    elif method.startswith("C"):235        # Track C: cross-checkpoint transfer. Sender cache built on the receiver's exact prefix token ids (shared tokenizer).236        from .mapper import LinearKVMapper, identity_transfer_negative_control237        pre = dc.prefix_tokens()238        assert sender.info.tokenizer_hash == receiver.info.tokenizer_hash, "C methods need a shared tokenizer"239        t.start("sender_prefill"); s_cache, tp = sender.prefill(pre); t.stop("sender_prefill")240        t.start("compress")241        if method == "C0":242            lm = [int(round(lr * (sender.info.num_layers - 1) / (receiver.info.num_layers - 1))) for lr in range(receiver.info.num_layers)]243            c = identity_transfer_negative_control(s_cache, receiver.info, lm); info = {"negative_control": True}244        else:245            mp = LinearKVMapper.load(ROOT / cond["mapper"], sender.info, receiver.info)246            c = mp.apply(s_cache); info = {"mapper": cond["mapper"]}247            if method == "C3":248                window = dc.split_ids(question)[1] if question is not None else receiver.encode("\n\nSummarize the key facts, identifiers, amounts, dates and statuses of this document.")249                c, sel = select_cache(receiver, c, cond.get("policy", "attention"), cond["keep_frac"], window_token_ids=window, seed=seed, window_keep=cond.get("window_keep", 32)); info.update(sel)250        mx.eval([x for cc in c for x in (cc.keys, cc.values)]); mx.synchronize()251        t.stop("compress")252        t.start("serialize")253        man = build_manifest(receiver.info, pre, c, "doc", method, {k: v for k, v in cond.items() if k != "method"}, {"selection_info": {k: v for k, v in info.items() if k != "layer_budgets"}})254        payload = serialize_cache(c, man)255        t.stop("serialize")256        out.update({"cache": c, "payload": payload, "payload_bytes": len(payload), "raw_payload_bytes": len(payload), "cache_bytes": cache_tensor_bytes(c), "manifest": man,257                    "compress_ms": t.spans["compress"], "serialize_ms": t.spans["serialize"], "codec": "none", "select_info": info, "doc_build_ms": tp["prefill_ms"], "doc_build_peak": int(mx.get_peak_memory()),258                    "prefix_tokens": len(pre), "dense_bytes": cache_tensor_bytes(s_cache), "sender_prefill_ms": tp["prefill_ms"]})259        del s_cache260    elif method.startswith("B"):261        pre = dc.prefix_tokens()262        dense = dc.dense_cache(pre)263        out["doc_build_ms"] = dc.build_ms; out["doc_build_peak"] = dc.build_peak; out["prefix_tokens"] = len(pre)264        out["dense_bytes"] = cache_tensor_bytes(dense)265        t.start("compress")266        if method == "B0":267            c = to_sparse_all(deep_copy_cache(dense)); info = {}268        elif method == "B1":269            c = to_sparse_all(deep_copy_cache(dense)); info = {"serialize_roundtrip": True}270        elif method == "B2":271            c = quantize_cache(dense, cond["bits"], cond.get("group_size", 64)); info = {"bits": cond["bits"]}272        elif method in ("B3", "B4", "B5", "B6", "B9"):273            policy = {"B3": "sink_recent", "B4": "random", "B5": "attention", "B6": "pyramid", "B9": cond.get("policy", "sink_recent")}[method]274            window = None275            if policy in ("attention", "pyramid"):276                if question is not None:277                    window = dc.split_ids(question)[1]          # question-aware: the fresh query tokens are the observation window278                else:279                    window = receiver.encode("\n\nSummarize the key facts, identifiers, amounts, dates and statuses of this document.")  # question-blind generic instruction280            c, info = select_cache(receiver, dense, policy, cond["keep_frac"], window_token_ids=window, seed=seed, sinks=cond.get("sinks", 4), window_keep=cond.get("window_keep", 32))281            if method == "B9":282                c = quantize_cache(c, cond["bits"], cond.get("group_size", 64)); info["bits"] = cond["bits"]283        else:284            raise NotImplementedError(method)285        mx.eval([x for cc in c for x in (cc.keys if isinstance(cc.keys, tuple) else (cc.keys,))])286        mx.synchronize()287        t.stop("compress")288        t.start("serialize")289        man = build_manifest(receiver.info, pre, c, "doc", method, {k: v for k, v in cond.items() if k != "method"}, {"selection_info": {k: v for k, v in info.items() if k != "layer_budgets"}})290        payload = serialize_cache(c, man)291        t.stop("serialize")292        codec = cond.get("codec", "none")293        comp, comp_ms = codec_compress(payload, codec) if codec != "none" else (payload, 0.0)294        out.update({"cache": c, "payload": comp, "payload_bytes": len(comp), "raw_payload_bytes": len(payload), "cache_bytes": cache_tensor_bytes(c), "manifest": man,295                    "compress_ms": t.spans.get("compress", 0.0), "serialize_ms": t.spans["serialize"] + comp_ms, "codec": codec, "select_info": info})296    else:297        raise NotImplementedError(method)298    t.stop("encode")299    out["encode_ms"] = t.spans["encode"]300    return out301302def _run_one(cfg, cond, cid, method, access, pair, it, doc, q, qkey, key, sender, receiver, dc, seed, tk, shared, shared_err, gen_max, protocol_hash, config_hash, reuse_count):303    fam = it["family"]304    rec = new_record(experiment_id=cfg["experiment_id"], trial_id=tk, run_group_id=cfg["experiment_id"] + ":" + cid, protocol_hash=protocol_hash, config_hash=config_hash,305                     task_family=fam, document_id=it["document_id"], document_hash=it["document_hash"], question_id=q["question_id"], split=it["split"], cluster_id=it["cluster_id"],306                     sender_model_revision=f"{sender.info.repo_id}@{sender.info.revision}", receiver_model_revision=f"{receiver.info.repo_id}@{receiver.info.revision}",307                     tokenizer_hashes={"sender": sender.info.tokenizer_hash, "receiver": receiver.info.tokenizer_hash},308                     backend_versions={"mlx": mx.__version__, "mlx_lm": receiver.info.mlx_lm_version}, hardware_id=HARDWARE_ID, device_state={"mlx_active_before": int(mx.get_active_memory())},309                     weight_precision="bf16", method_id=method, implementation_fidelity=FIDELITY.get(method, "custom"),310                     compression_config={k: v for k, v in cond.items() if k != "method"}, question_access=access, topology="single_handoff", handoff_index=1 if method != "A0" else 0,311                     reuse_count=reuse_count if access in ("blind", "none") else 1, cache_hit=(access in ("blind", "none") and method.startswith("B")), cache_compatibility_hash=None,312                     random_seed=seed, query_tokens=None, message_tokens=None, message_utf8_bytes=None, cache_tensor_bytes=None, serialized_payload_bytes=None,313                     input_usage={}, output_usage={}, provider_cached_usage=None, process_memory=None, backend_memory=None, peak_prefill_memory=None, peak_decode_memory=None,314                     mapper_memory=None, cost_with_source=None, prediction_artifact=None, evidence_artifact=None, reference_key_id=f"keys/{fam}/{it['document_id']}.json",315                     task_scores=None, status="implementation_error", error_code=None, log_artifact=f"runs/logs/{cfg['experiment_id']}.log", payload_hash=None, output_hash=None)316    rec["source_tokens_sender"] = len(sender.encode(doc["text"])); rec["source_tokens_receiver"] = len(receiver.encode(doc["text"]))317    t_total0 = time.perf_counter()318    try:319        if shared_err is not None:320            raise shared_err321        built = shared if shared is not None else _build_message(method, cond, doc["text"], q["question"], sender, receiver, dc, seed)322        spans = {"encode_ms": built["encode_ms"]}323        reset_peak()324        if method.startswith("A"):325            msg = built["msg"]326            if method == "A6":327                material, rinfo = TM.a6_retrieve(msg, q["question"], cond.get("max_fetch_bytes", 2000), cond.get("max_lookups", 6))328                rec["fetched_bytes"] = rinfo["fetched_bytes"]; rec["evidence_artifact"] = rinfo329            else:330                material = msg.text331            if method == "A0":332                ids = receiver.chat_prompt_tokens([{"role": "system", "content": SYSTEM_RECEIVER_NOMSG}, {"role": "user", "content": receiver_question_only(q["question"], fam)}])333            else:334                ids = receiver.chat_prompt_tokens([{"role": "system", "content": SYSTEM_RECEIVER}, {"role": "user", "content": receiver_user_text(msg.material_label, material, q["question"], fam)}])335            spans["decode_payload_ms"] = msg.decode_ms336            g = receiver.generate_text(ids, max_tokens=gen_max)337            spans.update({"receiver_prefill_ms": g["prefill_ms"], "ttft_ms": g["ttft_ms"], "decode_ms": g["decode_ms"]})338            rec.update({"message_tokens": len(receiver.encode(material)), "message_utf8_bytes": msg.utf8_bytes, "serialized_payload_bytes": msg.payload_bytes,339                        "side_information_bytes": msg.side_information_bytes, "query_tokens": len(ids), "input_usage": {"receiver_prompt_tokens": len(ids), **msg.sender_usage},340                        "payload_hash": sha256_text(material)})341            if msg.meta: rec["compression_config"] = {**rec["compression_config"], "realized": {k: v for k, v in msg.meta.items() if k not in ("question_aware",)}}342            if method == "A5": rec["evidence_artifact"] = {"n_facts": msg.meta.get("n_facts"), "unresolved_quotes": msg.meta.get("unresolved_quotes"), "parse_ok": msg.meta.get("parse_ok")}343        else:344            pre, query_ids = dc.split_ids(q["question"])345            assert len(pre) == built["prefix_tokens"], "prefix mismatch"346            c = built["cache"]347            spans.update({"doc_prefill_ms": built["doc_build_ms"], "compress_ms": built["compress_ms"], "serialize_ms": built["serialize_ms"]})348            t = Timer(); t.start("load")349            if method == "B1" or cond.get("roundtrip", False):350                raw, dms = codec_decompress(built["payload"], built["codec"]) if built["codec"] != "none" else (built["payload"], 0.0)351                c_use, man = deserialize_cache(raw, receiver.info, built["manifest"]["prefix_token_hash"])352                rec["cache_compatibility_hash"] = man["prefix_token_hash"][:16]353            else:354                c_use = deep_copy_cache(c)355            t.stop("load"); spans["load_ms"] = t.spans["load"]356            g = receiver.generate_from_cache(c_use, query_ids, max_tokens=gen_max)357            spans.update({"receiver_prefill_ms": 0.0, "query_prefill_ms": g["query_prefill_ms"], "ttft_ms": g["ttft_ms"], "decode_ms": g["decode_ms"]})358            rec.update({"message_tokens": built["prefix_tokens"], "message_utf8_bytes": len(doc["text"].encode("utf-8")), "serialized_payload_bytes": built["payload_bytes"],359                        "cache_tensor_bytes": built["cache_bytes"], "side_information_bytes": built["payload_bytes"] - built["cache_bytes"] if built["codec"] == "none" else None,360                        "query_tokens": len(query_ids), "input_usage": {"receiver_prefix_tokens": built["prefix_tokens"], "dense_cache_bytes": built["dense_bytes"], "query_tokens": len(query_ids)},361                        "payload_hash": sha256_text(str(len(built["payload"]))) , "peak_prefill_memory": built["doc_build_peak"]})362            rec["compression_config"] = {**rec["compression_config"], "realized": {k: v for k, v in built["select_info"].items() if k not in ("layer_budgets",)}}363        pred = g["text"]364        rec["prediction_artifact"] = pred; rec["output_hash"] = sha256_text(pred); rec["output_usage"] = {"output_tokens": g["output_tokens"]}365        rec["ttft_ms"] = spans["ttft_ms"]; rec["timing_spans"] = spans366        rec["total_wall_ms"] = (time.perf_counter() - t_total0) * 1000367        rec["peak_decode_memory"] = int(mx.get_peak_memory()); rec["backend_memory"] = memory_snapshot(); rec["process_memory"] = rec["backend_memory"]["process_rss_bytes"]368        if qkey is None:369            rec["status"] = "implementation_error"; rec["error_code"] = "missing_key"370        else:371            rec["task_scores"] = _score(fam, pred, qkey, key)372            rec["status"] = "success" if pred.strip() else "invalid_output"373    except Exception as e:374        rec["status"] = "oom" if "memory" in str(e).lower() else "implementation_error"375        rec["error_code"] = f"{type(e).__name__}: {str(e)[:300]}"376        rec["total_wall_ms"] = (time.perf_counter() - t_total0) * 1000377        traceback.print_exc()378    return rec379
Files · 2ccc6b7e52385b2438c0f3e62e88226009fb4f9f
Menu

Theme