Download scripts/needle_code_bench.py from poolside-laguna-hackathon/kv-landlords: direct link, hf CLI and curl.
- Browser
- Download file 13.8 kB
-
https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/needle_code_bench.py
- Command line
-
hf download hf://poolside-laguna-hackathon/kv-landlords/scripts/needle_code_bench.py
-
curl -L -o needle_code_bench.py https://huggingface.co/poolside-laguna-hackathon/kv-landlords/resolve/main/scripts/needle_code_bench.py
13.8 kB
| """Long-range NEEDLE retrieval for code: BF16 vs INT4-KIVI on Laguna-XS.2. | |
| The meaningful long-context test for a KV-cache quantizer: can the model attend | |
| back to a UNIQUE definition placed FAR earlier in the context after the cache | |
| has been quantized to INT4? We build a long synthetic Python "codebase" of | |
| filler functions padding to a target token length, embed a single | |
| non-guessable needle at a controlled DEPTH near the start, and at the very end | |
| ask the model to recall it. Grading is by EXACT value / executed equality -- | |
| the answer is only right if the model actually attended to the early definition | |
| (it cannot be guessed). | |
| Two needle kinds (both exact-graded): | |
| * "const" : ``MAGIC_SEED_<id> = <7-digit prime-ish int>`` -> ask the value. | |
| * "transform" : ``def secret_transform_<id>(x): return x * A + B`` -> ask the | |
| model to compute ``secret_transform_<id>(N)``; correct answer | |
| is the exact integer A*N+B. | |
| For each (context_length, depth) cell we run several trials (distinct needle | |
| values / filler) and report retrieval accuracy for BF16 and INT4-KIVI plus | |
| their agreement. Batch-1 greedy only (the regime Int4KiviCache supports). | |
| Usage | |
| ----- | |
| .venv/bin/python scripts/needle_code_bench.py --lengths 8000,16000 \ | |
| --depths 0.05 --trials 3 --max-new 24 | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import random | |
| import re | |
| import sys | |
| import time | |
| import torch | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, DynamicCache | |
| ROOT = "/home/alex/poolside-hackathon-kv-quant" | |
| sys.path.insert(0, ROOT) | |
| from int4_kivi.hf_cache import Int4KiviCache | |
| MODEL = "poolside/Laguna-XS.2" | |
| # --------------------------------------------------------------------------- # | |
| # Synthetic codebase / filler | |
| # --------------------------------------------------------------------------- # | |
| _VERBS = ["compute", "process", "build", "merge", "filter", "scan", "encode", | |
| "decode", "reduce", "expand", "rotate", "shuffle", "balance", "score", | |
| "align", "cluster", "sample", "weight", "smooth", "sharpen"] | |
| _NOUNS = ["matrix", "vector", "buffer", "token", "graph", "tensor", "window", | |
| "stream", "record", "payload", "segment", "lattice", "kernel", | |
| "gradient", "index", "shard", "bucket", "frame", "channel", "node"] | |
| def _filler_fn(rng: random.Random, i: int) -> str: | |
| """A plausible, in-distribution Python helper that carries no needle info.""" | |
| v = rng.choice(_VERBS) | |
| n = rng.choice(_NOUNS) | |
| a = rng.randint(2, 9) | |
| b = rng.randint(1, 20) | |
| return ( | |
| f"def {v}_{n}_{i}(items):\n" | |
| f' """{v.capitalize()} the {n} {i} with a small affine pass."""\n' | |
| f" out = []\n" | |
| f" for k, it in enumerate(items):\n" | |
| f" out.append(it * {a} + {b} + k)\n" | |
| f" return out\n" | |
| ) | |
| def _make_needle(rng: random.Random, kind: str): | |
| """Return (needle_source, question_text, expected_answer_str, meta).""" | |
| nid = rng.randint(100000, 999999) | |
| if kind == "const": | |
| # 8-digit non-guessable value. | |
| val = rng.randint(10_000_000, 99_999_999) | |
| src = ( | |
| f"# Important runtime configuration constant.\n" | |
| f"MAGIC_SEED_{nid} = {val}\n" | |
| ) | |
| q = ( | |
| f"In the reference code above there is a constant named " | |
| f"MAGIC_SEED_{nid}. Reply with ONLY its exact integer value and " | |
| f"nothing else." | |
| ) | |
| return src, q, str(val), {"nid": nid, "val": val} | |
| elif kind == "transform": | |
| # Use a unique multiplier constant so recall is non-guessable. We ask | |
| # the model to recall the *added* offset literal, which is a pure | |
| # attend-back retrieval (no arithmetic), keeping this a clean needle | |
| # test rather than a reasoning test. | |
| a = rng.choice([3, 7, 11, 13]) | |
| b = rng.randint(1_000_000, 9_999_999) # 7-digit, non-guessable | |
| src = ( | |
| f"# Domain-specific transform used by the pipeline.\n" | |
| f"def secret_transform_{nid}(x):\n" | |
| f" return x * {a} + {b}\n" | |
| ) | |
| q = ( | |
| f"In the reference code above there is a function named " | |
| f"secret_transform_{nid} whose body is `return x * {a} + OFFSET`. " | |
| f"Reply with ONLY the exact integer OFFSET value and nothing else." | |
| ) | |
| return src, q, str(b), {"nid": nid, "a": a, "b": b} | |
| else: | |
| raise ValueError(kind) | |
| # --------------------------------------------------------------------------- # | |
| # Build a context of ~target_tokens with the needle at a given depth fraction | |
| # --------------------------------------------------------------------------- # | |
| def build_context(tok, target_tokens: int, depth: float, kind: str, | |
| rng: random.Random): | |
| """Return (reference_code_text, question, expected_str, meta, n_pre_tokens). | |
| ``depth`` is the fraction of the *filler* placed BEFORE the needle (0.0 = | |
| needle right at the start, 0.5 = needle in the middle). We grow filler | |
| functions until the whole reference block reaches target_tokens. | |
| """ | |
| needle_src, question, expected, meta = _make_needle(rng, kind) | |
| # Generate a large pool of filler, then split around the needle by depth. | |
| fillers: list[str] = [] | |
| i = 0 | |
| # Build until the joined text (incl. needle) is at/above target. | |
| # Estimate ~ a few tokens per char; just append and recount periodically. | |
| pre, post = [], [] | |
| # First, mass-produce filler text and measure. | |
| block_parts: list[str] = [] | |
| while True: | |
| fn = _filler_fn(rng, i) | |
| block_parts.append(fn) | |
| i += 1 | |
| if i % 40 == 0: | |
| joined = "\n".join(block_parts) | |
| ntok = tok(joined, return_tensors="pt").input_ids.shape[1] | |
| if ntok >= target_tokens: | |
| break | |
| if i > 200000: # safety | |
| break | |
| # Now place needle at the requested depth among the filler functions. | |
| n_fn = len(block_parts) | |
| split = int(n_fn * depth) | |
| parts = block_parts[:split] + [needle_src] + block_parts[split:] | |
| reference = "\n".join(parts) | |
| # Trim to roughly target_tokens (keep the needle!). We trim from the END so | |
| # the needle (near the front for small depth) is preserved. | |
| ids = tok(reference, return_tensors="pt").input_ids[0] | |
| if ids.shape[0] > target_tokens * 1.15: | |
| ids = ids[: int(target_tokens * 1.15)] | |
| reference = tok.decode(ids, skip_special_tokens=True) | |
| # Re-confirm needle survived; if trimmed off (shouldn't for depth<0.7), | |
| # re-insert near the front. | |
| if meta_token(meta, kind) not in reference: | |
| reference = needle_src + "\n" + reference | |
| n_pre = tok(reference, return_tensors="pt").input_ids.shape[1] | |
| return reference, question, expected, meta, n_pre | |
| def meta_token(meta, kind: str) -> str: | |
| if kind == "const": | |
| return f"MAGIC_SEED_{meta['nid']}" | |
| return f"secret_transform_{meta['nid']}" | |
| # --------------------------------------------------------------------------- # | |
| # Prompt + generation | |
| # --------------------------------------------------------------------------- # | |
| def build_input_ids(tok, reference: str, question: str, device): | |
| sys_msg = ( | |
| "You are a careful code assistant. You will be shown a long Python " | |
| "reference codebase, then asked one question about a specific symbol " | |
| "defined in it. Answer using ONLY the information in the reference." | |
| ) | |
| user_msg = ( | |
| "Reference codebase:\n\n" | |
| f"```python\n{reference}\n```\n\n" | |
| f"{question}" | |
| ) | |
| msgs = [ | |
| {"role": "system", "content": sys_msg}, | |
| {"role": "user", "content": user_msg}, | |
| ] | |
| return tok.apply_chat_template( | |
| msgs, add_generation_prompt=True, return_tensors="pt", return_dict=False, | |
| ).to(device) | |
| def generate(model, input_ids, max_new, mode, config): | |
| cache = Int4KiviCache(config=config) if mode == "int4" else DynamicCache() | |
| out = model.generate( | |
| input_ids, max_new_tokens=max_new, past_key_values=cache, | |
| use_cache=True, do_sample=False, num_beams=1, | |
| ) | |
| return out[0, input_ids.shape[1]:].tolist(), cache | |
| def grade(response: str, expected: str) -> bool: | |
| """Exact-value match: the expected integer must appear as a standalone | |
| number in the response (and be the first number, to avoid echoing the | |
| function's coefficients).""" | |
| nums = re.findall(r"-?\d+", response.replace(",", "")) | |
| if not nums: | |
| return False | |
| # Correct if the expected value is the first integer emitted, OR appears | |
| # and no other distinct integer precedes it. We accept "first integer". | |
| return nums[0] == expected | |
| # --------------------------------------------------------------------------- # | |
| # Main sweep | |
| # --------------------------------------------------------------------------- # | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--lengths", type=str, default="8000,16000,32000") | |
| ap.add_argument("--depths", type=str, default="0.05") | |
| ap.add_argument("--trials", type=int, default=3) | |
| ap.add_argument("--kinds", type=str, default="const,transform") | |
| ap.add_argument("--max-new", type=int, default=24) | |
| ap.add_argument("--seed", type=int, default=1234) | |
| args = ap.parse_args() | |
| lengths = [int(x) for x in args.lengths.split(",") if x] | |
| depths = [float(x) for x in args.depths.split(",") if x] | |
| kinds = [k for k in args.kinds.split(",") if k] | |
| print(f"[load] {MODEL} ...", flush=True) | |
| tok = AutoTokenizer.from_pretrained(MODEL) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| MODEL, dtype=torch.bfloat16, device_map="auto" | |
| ) | |
| model.eval() | |
| device = next(model.parameters()).device | |
| print(f"\n{'':#<92}") | |
| print(" NEEDLE-IN-CODEBASE RETRIEVAL (exact-value graded, greedy, batch-1)") | |
| print(f"{'':#<92}") | |
| header = (f" {'len':>7} {'depth':>6} {'kind':>10} {'ctx':>8} " | |
| f"{'BF16':>10} {'INT4':>10} {'agree':>7} {'ratio':>6} {'t/gen':>6}") | |
| print(header) | |
| print(f"{'':-<92}") | |
| rows = [] | |
| example = None # (length, kind, question, expected, bf16_resp, int4_resp) | |
| for length in lengths: | |
| for depth in depths: | |
| for kind in kinds: | |
| rng = random.Random(args.seed + length + int(depth * 1000) | |
| + hash(kind) % 1000) | |
| bf_ok = iq_ok = agree = 0 | |
| ctxs, ratios, times = [], [], [] | |
| for t in range(args.trials): | |
| reference, question, expected, meta, n_pre = build_context( | |
| tok, length, depth, kind, rng | |
| ) | |
| input_ids = build_input_ids(tok, reference, question, device) | |
| ctx = int(input_ids.shape[1]) | |
| ctxs.append(ctx) | |
| res = {} | |
| for mode in ("bf16", "int4"): | |
| t0 = time.time() | |
| ids, cache = generate(model, input_ids, args.max_new, | |
| mode, model.config) | |
| dt = time.time() - t0 | |
| resp = tok.decode(ids, skip_special_tokens=True) | |
| ok = grade(resp, expected) | |
| res[mode] = (ok, resp, dt) | |
| if mode == "int4": | |
| try: | |
| ratios.append(cache.compression_ratio_vs_bf16()) | |
| except Exception: | |
| pass | |
| times.append(dt) | |
| bf_ok += res["bf16"][0] | |
| iq_ok += res["int4"][0] | |
| agree += (res["bf16"][0] == res["int4"][0]) | |
| if example is None and res["int4"][0] and res["bf16"][0]: | |
| example = (ctx, kind, question, expected, | |
| res["bf16"][1].strip()[:120], | |
| res["int4"][1].strip()[:120]) | |
| n = args.trials | |
| ctx_m = sum(ctxs) // len(ctxs) | |
| ratio_m = (sum(ratios) / len(ratios)) if ratios else 0.0 | |
| t_m = (sum(times) / len(times)) if times else 0.0 | |
| print(f" {length:>7} {depth:>6.2f} {kind:>10} {ctx_m:>8} " | |
| f"{bf_ok:>4}/{n:<5} {iq_ok:>4}/{n:<5} {agree:>3}/{n:<3} " | |
| f"{ratio_m:>5.2f}x {t_m:>5.1f}s", flush=True) | |
| rows.append((length, depth, kind, ctx_m, bf_ok, iq_ok, agree, n, | |
| ratio_m)) | |
| print(f"{'':-<92}") | |
| # Aggregate by length. | |
| print("\n RETRIEVAL ACCURACY BY CONTEXT LENGTH (summed over depths/kinds):") | |
| by_len: dict[int, list[int]] = {} | |
| for (length, depth, kind, ctx, bf, iq, ag, n, ratio) in rows: | |
| acc = by_len.setdefault(length, [0, 0, 0, 0]) | |
| acc[0] += bf; acc[1] += iq; acc[2] += ag; acc[3] += n | |
| print(f" {'target_len':>11} {'BF16':>14} {'INT4-KIVI':>14} {'agreement':>12}") | |
| for length in sorted(by_len): | |
| bf, iq, ag, n = by_len[length] | |
| print(f" {length:>11} {bf:>6}/{n:<4} ({100*bf/n:>3.0f}%) " | |
| f"{iq:>6}/{n:<4} ({100*iq/n:>3.0f}%) {ag:>4}/{n:<4} " | |
| f"({100*ag/n:>3.0f}%)") | |
| if example: | |
| ctx, kind, q, exp, bf_r, iq_r = example | |
| print(f"\n{'':-<92}") | |
| print(f" REPRESENTATIVE NEEDLE (ctx={ctx} tok, kind={kind})") | |
| print(f" Q: {q}") | |
| print(f" expected: {exp}") | |
| print(f" BF16 ->: {bf_r!r}") | |
| print(f" INT4 ->: {iq_r!r}") | |
| print(f"{'':-<92}") | |
| print("\n[done]", flush=True) | |
| if __name__ == "__main__": | |
| main() | |