|
35 | 35 | restored_prefill_cache, |
36 | 36 | ) |
37 | 37 |
|
| 38 | +# #region agent log (Phase-1 long-gen degeneration debug; remove after fix) |
| 39 | +import json as _kjson |
| 40 | +import sys as _ksys |
| 41 | + |
| 42 | + |
| 43 | +def _kdbg(ev: str, **kw: Any) -> None: |
| 44 | + """Emit one compact NDJSON line to stderr (captured by the git-bus bridge).""" |
| 45 | + try: |
| 46 | + rec = {"ev": ev, **kw} |
| 47 | + _ksys.stderr.write("KDBG " + _kjson.dumps(rec, separators=(",", ":")) + "\n") |
| 48 | + _ksys.stderr.flush() |
| 49 | + except Exception: |
| 50 | + pass |
| 51 | + |
| 52 | + |
| 53 | +def _kdbg_rep(toks: List[int], k: int = 32) -> Dict[str, Any]: |
| 54 | + """Cheap degeneration signal over the last ``k`` generated tokens: |
| 55 | + unique fraction + longest single-token run (repetition collapse spikes it).""" |
| 56 | + w = toks[-k:] |
| 57 | + if not w: |
| 58 | + return {"win": 0} |
| 59 | + n = len(w) |
| 60 | + uniq = len(set(w)) |
| 61 | + run = best = 1 |
| 62 | + for a, b in zip(w, w[1:]): |
| 63 | + run = run + 1 if a == b else 1 |
| 64 | + if run > best: |
| 65 | + best = run |
| 66 | + return {"win": n, "uniq_frac": round(uniq / n, 3), |
| 67 | + "rep_frac": round(1.0 - uniq / n, 3), "max_run": best} |
| 68 | + |
| 69 | + |
| 70 | +def _kdbg_cache(cache: Any) -> Dict[str, Any]: |
| 71 | + """Summarize per-layer cache state: pick the first sliding (RotatingKVCache) |
| 72 | + and first full (KVCache) layer and report global offset, physical resident |
| 73 | + seq-len, max_size and keep (sink) so we can correlate window-eviction with |
| 74 | + the restored-coverage boundary. Also returns layer-class counts.""" |
| 75 | + sliding = full = None |
| 76 | + counts: Dict[str, int] = {} |
| 77 | + for c in (cache or []): |
| 78 | + cls = type(c).__name__ |
| 79 | + counts[cls] = counts.get(cls, 0) + 1 |
| 80 | + keys = getattr(c, "keys", None) |
| 81 | + info = { |
| 82 | + "cls": cls, |
| 83 | + "off": int(getattr(c, "offset", 0)), |
| 84 | + "phys": int(keys.shape[2]) if keys is not None else 0, |
| 85 | + "ms": (int(getattr(c, "max_size")) if getattr(c, "max_size", None) is not None else None), |
| 86 | + "keep": (int(getattr(c, "keep")) if getattr(c, "keep", None) is not None else None), |
| 87 | + } |
| 88 | + if "Rotating" in cls and sliding is None: |
| 89 | + sliding = info |
| 90 | + elif "Rotating" not in cls and full is None: |
| 91 | + full = info |
| 92 | + return {"counts": counts, "sliding": sliding, "full": full} |
| 93 | + |
| 94 | + |
| 95 | +def _kdbg_lost(cache: Any, restored: Any, prompt_len: int) -> Optional[Dict[str, Any]]: |
| 96 | + """Phase-1 Q2: count sliding-layer positions evicted DURING decode that have |
| 97 | + NO restored K/V. For the first RotatingKVCache: positions [keep, evict_hi) |
| 98 | + are no longer resident, where evict_hi = offset - (max_size - keep). Of those, |
| 99 | + any not in the (prompt-only) restored coverage are 'lost' (no K/V anywhere).""" |
| 100 | + for c in (cache or []): |
| 101 | + if "Rotating" not in type(c).__name__: |
| 102 | + continue |
| 103 | + ms = getattr(c, "max_size", None) |
| 104 | + if ms is None: |
| 105 | + return None |
| 106 | + off = int(getattr(c, "offset", 0)) |
| 107 | + keep = int(getattr(c, "keep", 0) or 0) |
| 108 | + ms = int(ms) |
| 109 | + evict_hi = off - (ms - keep) # exclusive upper bound of evicted region |
| 110 | + evicted_n = max(0, evict_hi - keep) |
| 111 | + rset = restored if isinstance(restored, set) else set() |
| 112 | + lost = sum(1 for p in range(keep, evict_hi) if p not in rset) |
| 113 | + return {"off": off, "ms": ms, "keep": keep, "evict_hi": evict_hi, |
| 114 | + "evicted_n": evicted_n, "restored_in_evicted": evicted_n - lost, |
| 115 | + "lost": lost, "prompt_len": int(prompt_len), |
| 116 | + "window_slid_off_prompt": bool(evict_hi > prompt_len)} |
| 117 | + return None |
| 118 | +# #endregion |
| 119 | + |
38 | 120 |
|
39 | 121 | # --------------------------------------------------------------------------- # |
40 | 122 | # Component A: capture verifier aux-layer hidden states (no transformers |
@@ -193,6 +275,27 @@ def prefill( |
193 | 275 | cache_factory=factory, |
194 | 276 | ) |
195 | 277 | self._past_len = len(prompt_ids) |
| 278 | + # #region agent log (Phase-1) |
| 279 | + try: |
| 280 | + ev = sorted(int(p) for p in evicted_positions) |
| 281 | + rk_layers = sorted(int(k) for k in restored_k_per_layer.keys()) |
| 282 | + # Stash restored coverage for the decode loop's lost-position check. |
| 283 | + self._dbg_restored_positions = set(ev) |
| 284 | + self._dbg_prompt_len = int(len(prompt_ids)) |
| 285 | + _kdbg( |
| 286 | + "prefill", |
| 287 | + prompt_len=len(prompt_ids), |
| 288 | + evicted_count=len(ev), |
| 289 | + evicted_lo=(ev[0] if ev else None), |
| 290 | + evicted_hi=(ev[-1] if ev else None), |
| 291 | + restored_layers=rk_layers, |
| 292 | + restored_layer_count=len(rk_layers), |
| 293 | + full_kv=bool(self._full_kv), |
| 294 | + cache=_kdbg_cache(self._cache), |
| 295 | + ) |
| 296 | + except Exception: |
| 297 | + pass |
| 298 | + # #endregion |
196 | 299 |
|
197 | 300 | def forward_block(self, tokens: Sequence[int]) -> Any: |
198 | 301 | """Incremental verify of ``tokens`` against the restored cache. Returns |
@@ -402,6 +505,7 @@ def fused_specdecode_generate_mlx_trim( |
402 | 505 | ctx_len = C |
403 | 506 | try: |
404 | 507 | while len(generated) < gen_tokens: |
| 508 | + _kblk_t0 = time.perf_counter() # agent log (Phase-1) |
405 | 509 | L = min(block_size, gen_tokens - len(generated)) |
406 | 510 | base = adapter._past_len |
407 | 511 | t_build = time.perf_counter() |
@@ -440,6 +544,23 @@ def fused_specdecode_generate_mlx_trim( |
440 | 544 | commit = check[:accepted] |
441 | 545 | generated += commit |
442 | 546 | accepts.append(accepted) |
| 547 | + # #region agent log (Phase-1) |
| 548 | + _kdbg( |
| 549 | + "block", |
| 550 | + loop="mlx_trim", |
| 551 | + blk=len(accepts) - 1, |
| 552 | + gen=len(generated), |
| 553 | + past_len=adapter._past_len, |
| 554 | + accepted=accepted, |
| 555 | + L=int(check_ids.shape[0]), |
| 556 | + dt_ms=round((time.perf_counter() - _kblk_t0) * 1e3, 1), |
| 557 | + rep=_kdbg_rep(generated), |
| 558 | + lost=_kdbg_lost(adapter._cache, |
| 559 | + getattr(adapter, "_dbg_restored_positions", set()), |
| 560 | + getattr(adapter, "_dbg_prompt_len", 0)), |
| 561 | + cache=_kdbg_cache(adapter._cache), |
| 562 | + ) |
| 563 | + # #endregion |
443 | 564 | adapter.next_token_logits = next_row |
444 | 565 | aux_rows = adapter._last_aux_mx |
445 | 566 | # KEEP accepted (positions base..base+accepted-1), TRIM rejected. |
@@ -675,6 +796,7 @@ def fused_specdecode_generate( |
675 | 796 | fallback_to_greedy = False |
676 | 797 | try: |
677 | 798 | while len(generated) < gen_tokens: |
| 799 | + _kblk_t0 = time.perf_counter() # agent log (Phase-1) |
678 | 800 | L = min(block_size, gen_tokens - len(generated)) |
679 | 801 | cstart = adapter._past_len |
680 | 802 | bonus = int(argmax_fn(adapter.next_token_logits)) |
@@ -731,6 +853,24 @@ def fused_specdecode_generate( |
731 | 853 | commit = candidate[:accepted] + [correction] |
732 | 854 | generated += commit |
733 | 855 | accepts.append(accepted) |
| 856 | + # #region agent log (Phase-1) |
| 857 | + _kdbg( |
| 858 | + "block", |
| 859 | + loop="torch_ftheta", |
| 860 | + blk=len(accepts) - 1, |
| 861 | + gen=len(generated), |
| 862 | + gen_since_prompt=len(generated), |
| 863 | + past_len=adapter._past_len, |
| 864 | + accepted=accepted, |
| 865 | + L=len(candidate), |
| 866 | + dt_ms=round((time.perf_counter() - _kblk_t0) * 1e3, 1), |
| 867 | + rep=_kdbg_rep(generated), |
| 868 | + lost=_kdbg_lost(adapter._cache, |
| 869 | + getattr(adapter, "_dbg_restored_positions", set()), |
| 870 | + getattr(adapter, "_dbg_prompt_len", 0)), |
| 871 | + cache=_kdbg_cache(adapter._cache), |
| 872 | + ) |
| 873 | + # #endregion |
734 | 874 | if any(t in eos for t in commit): |
735 | 875 | break |
736 | 876 | if (allow_greedy_fallback and len(accepts) >= 2 |
|
0 commit comments