You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(metal): attnFold slabs did not grow with the wide tail-absorbed prefill chunk — ~52K long-context corruption
Root cause of the long-context needle-sweep failures (argmax -1 / instant EOS /
silent needle loss at ~52K on E2B): attnFold keyed its reallocation on mlpFold's
foldRowCap. mlpFold always runs FIRST and had already raised foldRowCap for a
wider chunk, so attnFold skipped its realloc and every attention slab (attn-norm,
Q, SDPA out, O out, K/V stage) stayed at the previous row count. The batched
prefill's tail absorption produces exactly ONE chunk wider than all before it
(window + tail, when promptLen mod window lands in (0, window/2]), so rows past
the stale capacity read and wrote out of bounds — undefined bytes that varied per
process: NaN cascades through attention (the argmax -1 hard failure), or silently
wrong hiddens (the needle loss at 97K, and non-identical temp-0 outputs).
attnFold now tracks its own attnRowCap/attnDModel. Forensics that found it stay
in-tree, env-gated (LTHN_DEBUG_ARGMAX): the invalid-argmax per-stage dump
(hidden -> normed -> logits -> tiles), the per-chunk boundary-hidden NaN scan with
per-layer KV fingerprinting, and the last-prefill-step seam dump. cmd/lem gains
-prompt-file (long prompts exceed argv).
Receipts: TestDenseBatchScratchAttnFoldGrowsWithRows pins the production call
order (mlpFold first, then attnFold at a grown row count) against the slab sizes;
CLI repro 3/3 clean at 51,924 tokens (was ~2/3 failing) with zero NaN diagnostics;
TestE2BQ4LongContextPrefillCorruption (real-model, E2B_Q4_DIR-gated) now produces
byte-identical temp-0 tokens across reps at the wide-chunk length (previously
diverged per rep — the silent form); full engine/metal suite 1436 green.
Co-Authored-By: Virgil <virgil@lethean.io>
prompt:=fs.String("prompt", "Write a detailed Go function that reverses a singly linked list, with inline comments on every step, then explain the pointer dance.", "user prompt")
42
+
promptFile:=fs.String("prompt-file", "", "read the user prompt from a file (long-context runs exceed argv limits); overrides -prompt")
42
43
maxTokens:=fs.Int("max-tokens", 128, "tokens to generate")
43
44
draftPath:=fs.String("draft", "auto", "MTP drafter: 'auto' detects one beside a Gemma 4 target (assistant/ pair layout, MTP/ gguf), a path forces it, '' disables")
Copy file name to clipboardExpand all lines: go/engine/metal/decode_batched_session.go
+11-3Lines changed: 11 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -71,6 +71,8 @@ type denseBatchScratch struct {
71
71
attnOutPacked metal.MTLBuffer// K × dModel O-projection outputs
72
72
kStagePacked metal.MTLBuffer// K × kvDimMax staged K rows (ring-wrap landing)
73
73
vStagePacked metal.MTLBuffer// K × kvDimMax staged V rows
74
+
attnRowCapint// attnFold's OWN row capacity — NOT foldRowCap (mlpFold updates that first, which masked row growth and left these slabs short: the ~52K wide-tail-chunk corruption)
75
+
attnDModelint
74
76
foldQDimCapint
75
77
foldKVDimCapint
76
78
// per-layer staging for the deferred-landing lane (the big-K staged sliding tail): each
normed, q, attn, attnOut, kSt, vSt:=s.attnFold(512, dModel, qDim, kvDim)
320
+
ifint(bufferLengthFast(q)) <512*qDim*bf16Size {
321
+
t.Fatalf("baseline q slab too small: %d", bufferLengthFast(q))
322
+
}
323
+
_, _, _, _, _=normed, attn, attnOut, kSt, vSt
324
+
// the wide tail-absorbed chunk: window + tail rows, mlpFold FIRST (production order)
325
+
constwide=724
326
+
s.mlpFold(wide, dModel, dFF)
327
+
normed, q, attn, attnOut, kSt, vSt=s.attnFold(wide, dModel, qDim, kvDim)
328
+
check:=func(namestring, got, wantint) {
329
+
t.Helper()
330
+
ifgot<want {
331
+
t.Fatalf("attnFold %s slab did not grow with the wide chunk: %d bytes, want >= %d — rows past the stale capacity read/write out of bounds", name, got, want)
0 commit comments