""" runner_patch.py — swap HyperExpertRunner onto FastGenerator WITHOUT editing runtime_adapters.py. Reversible, A/B-able. from runner_patch import install, uninstall install(runner) # runner._generate now uses cached decode (sliding) uninstall(runner) # back to stock GATED: only install after divergence_quality.py says benign AND help/shatter + smoke re-run matches native within noise. Preserves every stock control byte-for-byte (verified identical output with rep_pen=1.3 + no_repeat_3gram + pad masking on a real runner instance): the loop body is stock _generate's; only the logits source changes (full forward per step -> FastGenerator prefill/step). Deltas set before the call are picked up automatically (snapshot-at-prefill). One stated semantic difference: stock _generate rolls a sliding window when len(prompt)+new exceeds block_size; a cache cannot roll, so we pre-crop the prompt to (block_size - max_new). Your harness already budgets context so this should never trigger; if it does, the crop is logged. """ import types from time import perf_counter from fast_generate import FastGenerator def _generate_fast(self, prompt_text: str, max_new_tokens: int) -> str: torch = self.torch g = self.gen from runtime_adapters import _banned_ngram_tokens, log ids = self.tok.encode(prompt_text)[: self.block_size] if len(ids) + max_new_tokens > self.block_size: log(f"fast decode: pre-cropping prompt {len(ids)} -> " f"{self.block_size - max_new_tokens} (cache cannot roll)", tag="GENERATE") ids = ids[-(self.block_size - max_new_tokens):] idx = torch.tensor([ids], dtype=torch.long, device=self.device) t0 = perf_counter() fg = FastGenerator(self.adapted.model) # sliding; sees live deltas logits_next = fg.prefill(idx) new_ids: list[int] = [] n_blocked = 0 for _ in range(max_new_tokens): logits = logits_next.float() # (1, V) if g.mask_pad_vocab and self.tok_vocab and self.tok_vocab < logits.size(-1): logits[:, self.tok_vocab:] = -float("inf") if g.repetition_penalty and g.repetition_penalty != 1.0: seen = torch.unique(idx[0]) vals = logits[0, seen] logits[0, seen] = torch.where(vals > 0, vals / g.repetition_penalty, vals * g.repetition_penalty) if g.no_repeat_ngram_size and g.no_repeat_ngram_size >= 2: banned = _banned_ngram_tokens(idx[0].tolist(), g.no_repeat_ngram_size) if banned: n_blocked += len(banned) logits[0, list(banned)] = -float("inf") if g.temperature <= 0: nxt = int(logits.argmax(-1)) else: lg = logits / g.temperature if g.top_k: v, _ = torch.topk(lg, min(g.top_k, lg.size(-1))) lg[lg < v[:, [-1]]] = -float("inf") nxt = int(torch.multinomial(torch.softmax(lg, -1), 1)) new_ids.append(nxt) idx = torch.cat([idx, torch.tensor([[nxt]], device=self.device)], dim=1) if g.stop_on_eos and nxt in self.stop_ids: new_ids.pop() break logits_next = fg.step(torch.tensor([[nxt]], device=self.device)) extra = (f", rep_pen={g.repetition_penalty}" if g.repetition_penalty != 1.0 else "") extra += (f", no_repeat_{g.no_repeat_ngram_size}gram(blocked {n_blocked})" if g.no_repeat_ngram_size >= 2 else "") log(f"generate[FAST]: prompt {len(ids)} tok -> +{len(new_ids)} tok " f"in {perf_counter()-t0:.2f}s " f"({'greedy' if g.temperature <= 0 else f'T={g.temperature}'}{extra})", tag="GENERATE") return self.tok.decode(new_ids) def install(runner): if not hasattr(runner, "_generate_stock"): runner._generate_stock = runner._generate runner._generate = types.MethodType(_generate_fast, runner) return runner def uninstall(runner): if hasattr(runner, "_generate_stock"): runner._generate = runner._generate_stock return runner