Repository navigation
DeepSeek-V4.1 on the native engine: one family, CUDA and Metal (tracker) #847
Copy link
Copy link
Open
Description
Thank you to everyone who has put work into DeepSeek-V4.1 on the native engine. @jayleaton's CUDA stack (#635 to #727) is the most complete community port we've had. @akol1 brought the Metal family (#824 to #827) and proposed the layout on #635. @bertholomus and @ecohash-co's port (#559) has served real traffic on four GB10s, and their updates there are some of the most careful measurement anyone has posted here.
This issue is where those pieces come together, the way #832 does for EXL3.
The plan
- One family package,
zig/src/families/deepseek_v41/, with no backend code in it: configuration, weight and pack loading, the Engram tables and token map, cache and history state, and round planning. This is the split @akol1 proposed on cuda: a keyed graph cache and a device arena, off until a family installs them; failed launches name their kernel (DeepSeek-V4.1 stack 2/14) #635. - Kernels beside it: Metal in
zig/kernels/metal/deepseek_v41/, CUDA inzig/kernels/cuda/deepseek_v41/. - Each backend implements the lane core's
Backend. Drafts, the prompt cache and concurrency come from the core, so on every backend drafted output equals"draft": false, a resumed prompt equals a fresh one, and concurrent equals solo. - EXL3 goes through the one EXL3 module (EXL3 checkpoints on the native engine (tracker) #832). The Metal projection and the CUDA linear and grouped experts become that module's backends.
- The default boot needs no capture directory. Captured Triton cubins can stay an optional input, in the layout the 27B's capture already uses (
packaging/README.md). - Carried kernels keep their attribution, and their authors get Co-authored-by lines. We add those when the code lands.
Where the trees differ today
- Both write
config.zig,load.zig,model.zigandkv_state.zigunderzig/src/families/deepseek_v41/, with different contents. Each becomes one file. - DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827's lane backend runs with drafts off (
backend.zig:58and:63). It lands once it decodes with drafts on and passes its whole-model gate. - The CUDA layers need one GPU run at the current heads, as asked on cuda: a keyed graph cache and a device arena, off until a family installs them; failed launches name their kernel (DeepSeek-V4.1 stack 2/14) #635.
Order
- The shared pieces, each as its own increment with our gate, as described on cuda: a keyed graph cache and a device arena, off until a family installs them; failed launches name their kernel (DeepSeek-V4.1 stack 2/14) #635: cuda: a keyed graph cache and a device arena, off until a family installs them; failed launches name their kernel (DeepSeek-V4.1 stack 2/14) #635, fabric: tensor parallelism across CUDA processes (bootstrap, NCCL, mailbox and RoCE collectives, plan link, fail-fast) (DeepSeek-V4.1 stack 3/14) #636, sessions: session memory for paged-KV families (page pool, prefix trie, RAM residency budget, streamed NVMe tier) (DeepSeek-V4.1 stack 4/14) #637, lanes: a family's round planner drives the lane host with prompt pieces, and a halt ends the rounds at a drain deadline (DeepSeek-V4.1 stack 6/14) #662, server: graceful drain and pre-token keepalives (DeepSeek-V4.1 stack 7/14) #663.
- The family package, agreed here.
- The CUDA backend from DeepSeek-V4.1: host configuration, EXL3 packs and rank weight plans #667 to DeepSeek-V4.1: performance kernels, family images and device bench runners #727 and the Metal backend from DeepSeek V4.1: the Engram token map built in Zig, byte-identical to the release's #824 to DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827, each through its own gates.
Open questions
- @akol1's three questions on cuda: a keyed graph cache and a device arena, off until a family installs them; failed launches name their kernel (DeepSeek-V4.1 stack 2/14) #635. Our answer to the third: served-side state lives in the family package. On the first, we'd rather have one loader whose plan becomes rank 0 of 1 on a Mac, if that fits both backends.
- Credit. 18 of the CUDA kernel files say they are copies of
src/tensorfold/families/deepseek_v41/cuda/, and one ofsrc/tensorfold/families/glm5_next/spark/. Neither path is in TensorFold's Python line (v0.6.6 hasdeepseek_v4). Whose tree are they from, and who wrote them? Their authors get the same credit as everyone else. - KV formats. DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827 keeps the window and main caches in fp8 and fp4. If DeepSeek's reference keeps them that way, please link it. Otherwise bf16 is the default, with lower precision behind an opt-in flag.
Please comment here with anything that changes the plan.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
akol1 commentedon Oct 14, 2026
The KV-format question, answered from the reference's own inference code — and it mostly clears our packing, with one item to verify.
What DeepSeek's reference stores (
inference/model.py): every cache row is written byact_quant(..., inplace=True), whose docstring reads "fused quant+dequant back to BF16" — so the stored tensor is BF16 values that passed an FP8 roundtrip (per-32-block scales, e4m3, FP32 scales, no power-of-2 rounding): the window KV at line 707, the main KV at 1042, the decode-path KV at 1062. The index keys are FP4 with 32-block scales (fp4_act_quant(k, fp4_block_size, True), line 552), and the compressed latent is FP4 with 16-block scales (line 760). Nothing is stored as raw fp8 or fp4; the low-bit formats appear only as the roundtrip's midpoint.What #827's backend does:
window_fp8,main_fp4(16-block) andindex_fp4(32-block) — the same three roundtrip semantics, but with the mid-step values stored packed rather than widened back to bf16. Since the packed grids hold exactly the roundtripped values, the stored rows are value-identical to the reference's where the rounding matches; the layer-21 same-input capture showed the quantized KV stages at exactly 0, which supports that.The one open item: our window rows are stored as plain per-value e4m3, while the reference's roundtrip applies a per-32-block scale before the e4m3 cast — for rows whose block scale isn't 1 those roundings differ. We'll either match the block-scaled roundtrip in the pack kernel or show the two agree on the real rows, and the whole-model gate re-run after that is the test that matters.
The three answers you asked for: served-side state lives in the family package — agreed. One loader whose plan becomes rank 0 of 1 on a Mac fits both backends as far as we're concerned; the lane backend already takes its rows from the plan. And the four conditions from your #827 comment are picked up: drafts on (the lane core's draft path wired to the MTP drafter), the four files consolidated per this tracker, the whole-model gate passing — we have a concrete lead there, see the cache item above — and the cache defaults per the reference, linked here.
jayleaton commentedon Oct 15, 2026
Thanks for opening #847. I agree on one
families/deepseek_v41package with Metal and CUDA operators beside it. I have read the split that supersedes #690 in #824–#827. Here is the file-level direction I propose; this is not a claim that the two loaders already interchange.One host loader and an optional rank plan. The current
config.zig,names.zig,exl3.zig,pack.zig, andplan.zigare host code; I agree EXL3 format handling should use the single module discussed in EXL3 checkpoints on the native engine (tracker) #832. CUDA starts at device upload inload.zig. The EXL3 slice rule returns the whole tensor at world 1, so I would share the header/name/plan layer and keep upload, device forms and residency per backend. Before treating it as the Metal loader, we need a rank-0/world-1 fixture, the kept-dense.weightforms, optional original Engram table sources, and agreement on strict config validation. The existing plan fixtures use world 2. The current build module still imports CUDA/TP/KV; the neutral loader needs its own import boundary.What overlaps. I have config/topology, EXL3 headers and named TP plans, an Engram host map/hash reader, packed per-rank NVMe table rows, CUDA paged/split KV and session history, index selection, and mHC/CSA2 operators. These are equivalents, not interchangeable files. My map is
DSV41EH1; your Zig builder and reader useDSENGR01. The map format and out-of-range-token rule need agreement. I would adopt the Zig producer from DeepSeek V4.1: the Engram token map built in Zig, byte-identical to the release's #824 and give both backends one map contract. My Engram row format isDSV41EN1(264-byte packed rows), whereas your backend reads the original tables resident. That storage policy should stay behind one family interface. KV ownership and index scratch are backend-specific; shared history/snapshot contracts can sit above them. Both index paths state lower-position tie ordering, but I have not measured their outputs against each other. I do not have backend-neutral host mHC/CSA2 arithmetic to substitute for your independent references.The files that collide directly are
config.zig,load.zig,model.zig, andkv_state.zig; they need explicit consolidation or backend names before the two branches land. We also need one family build module, rather than paralleldsv41.ziganddeepseek_v41.zigbuild wiring. The namedcoreimports and relative core-file imports must be reconciled so one file belongs to one Zig module.Served state stays in the family. Main's GLM-5.3 family already keeps
backend.zig,engine.zig,snap.zig, andfollow.zigunderfamilies/glm_dsa. I would follow that pattern for DeepSeek: backend workspaces, KV/index/Engram state, slot state and chat/template/tool code in the family; lane scheduling, verification, generic sessions and halt/drain in the shared core.core/exlr8is format code, not an example of served family state. A generic EXL3 rank-slice helper could belong beside the format reader, but acore/dsv41served-state module would put family policy in core.I will keep backend qualification separate. The Metal whole-model gate-form discussion in #824–#827 is still yours and the maintainers' to settle; this package proposal neither changes that gate nor qualifies the CUDA stack. Each backend still needs drafted/plain, resumed/fresh and concurrent/solo receipts at its exact head.
The source repository for the carried CUDA files is jayleaton/deepseek-v41-tensorfold-spark, rather than the frozen upstream Python line. The public kernel import is ed67bc8a, recorded under my public Git identity. The current public tree carries the native copies under
engine/zig/zig/kernels/cuda/deepseek_v41/. The oldersrc/tensorfold/...paths and blob IDs in the copy comments are not reachable in the public history I checked; those comments should not imply they came from upstream v0.6.6. This establishes the source repository and public port attribution, rather than proving the original author of every historical blob. The ExLlamaV3 operation-order/design provenance already described on #635 and the third-party notices still apply; they are not erased by that repository attribution.The family-neutral host pieces are now #860 and #861, both independent draft PRs with passing host checks and zero new lean findings. The current-head CUDA two-GB10 gate remains unrun. I will keep the CUDA family layers held until that exact-tree evidence is available.
jayleaton commentedon Oct 15, 2026
I found the original paths in the Spark repository's exported patches, rather than as tree entries. Both archived patches record Jay Leaton as author.
src/tensorfold/families/glm5_next/spark/l2pf.cu. Reconstructing that new-file diff gives Git blobace57d9cab362fb7bcc6fa1456d682a4f0433216, matching the copy comment exactly.mhc_cuda.cuis blobc792c1251be6e7e76a2fc96abe3ab279127b9509and carries Jay's MIT copyright notice. The later native import includes further changes, including the parallel tail; this archived version does not prove the latercaa24ba4bb72blob.The repository is the original source, as JJ confirmed. The public
mainbranch holds the patch archives; I still cannot resolve the historical78b703dcommit ordsv41-quant-e2ref from its advertised branches. My previous statement that the original paths were not reachable should be narrowed to Git tree entries: the paths and the exact L2 blob are recoverable from the public patches. Original patch authorship and later engine-port authorship should be credited separately, with third-party notices retained.akol1 commentedon Oct 15, 2026
Two decisions are now blocking the landing, and we want to make answering them as cheap as possible.
1. The whole-model gate form — @ashhart. The evidence package on #827 (the comment above the split links) shows the literal 1% is unreachable: the model's own FP32 sensitivity compounds through depth, every precision-hardening experiment moved the match away from the reference, and the residual 3.05% survived a bit-exact 12-layer sweep. Three candidate forms are sketched there — the floor-relative bound, the per-layer bounds, the pack-quality slope — with the floor-relative bound our pick. Which should the merged series gate on?
2. The Engram table format — @jayleaton. The 4-bit repack proposal (the memory case: the resident payload drops from 415 GB to about 240 GB, moving the one-Mac story to 256 GB machines) needs your loader's answer to three questions: (a) which of the three candidate formats can your CUDA loader take — the engine's own 4-bit KV packing, plain 4-bit with per-row or per-64-block f16 scales, or EXL3 trellis; (b) is per-row scaling right for the value distribution you see, or is 6/8-bit the right first step; (c) should the repack live in the pull or ship as prepacked hub alternatives. The proposal's full memory math and the format trade-offs are written up; we can post it here verbatim if that helps.
3. The consolidation sequencing — @ashhart. The four files (config, load, model, kv_state) become one each shared with the CUDA stack per this tracker. Our branch carries Metal-tested versions of all four; jayleaton's carries the CUDA-tested ones. Do you want the consolidation PR to start from ours and absorb his differences, from his and absorb ours, or should the merged contents be drafted here first? The same question applies to the four files' tests.
Everything else — the sparse-PV fix, the boundary-tie measurements, the drafts-on design — is in the four PRs and their receipts, and does not need a decision to review.
ecohash-co commentedon Oct 16, 2026
Thank you, @ashhart. Glad to fold this into #847.
Where our tree is: https://github.com/ecohash-co/TensorFold
dsv41-tp4-p16dsv41-tp4-logprobsdsv41-tp4-p16plus logprobs and the opt-in MXFP4 prompt path.deepseek-v41-zig-tp4.Where
families/dsv41is ahead of the #635–#727 and #824–#827 pull requests, as far as we can tell. Each item is measured on 4× GB10, TP4, with the 2D split. Updates 16–27 on #559 have the gates and numbers.TF_DS_PROMPT_SP=rows2: sequence-parallel rows on the head chunk at any row count.TF_DS_PROMPT_CHUNK=4096: two-chunk encoder passes.TF_DS_EXL3_PROMPT=v2: a 2D node's prompt EXL3 expert GEMMs, with the rotation in the program and a W_q cache.TF_DS_PROMPT_OVL=all,pack3,dn,fxag: the chunk's exchanges run on a comm stream under compute.TF_DS_PROMPT_ATTN=front,isplit,tsel: attention without per-node replicated work, plus a one-launch indexer top-k.TF_DS_PROMPT_ROUTER=own: the MoE router on owned rows, with the served cuBLAS split-K arithmetic.TF_DS_PROMPT_P16=1: owner partials sent as bf16. This one is tier 2, gated by the replay KL measurement.TF_DS_ENGRAM_PREFETCH=<depth>,scales: reads rows chunks ahead.TF_DS_ENGRAM_SPLIT: column partners split the reads, and this fixes a long-prompt hang._index_scoreodd-row tails run the captured variant._sparse_attn_part<16-row tails).edf22ac/79e9c25changes you asked about on DeepSeek-V4.1-Flash on the Zig engine (CUDA, 2 and 4 GB10): our port is yours to use, plus test time #559 are5a3ea61anda742532on our fork, plus the Engram split hang fix66978e1.logprobs/top_logprobs(≤20) withtoken_id, plusforced_tokensteacher-forced scoring at decode speed.TF_DS_PREFILL_MXFP4_LAYERS,TF_DS_MXFP4_MIN_ROWS).The KL gate as a receipt. We'd be glad to supply it.
Upstream fixes, run on four nodes. We folded @bertholomus's #4, #9, #12 and #14 into the served build, on top of
dsv41-tp4-p16.visalignment to the isplit indexer path (selectRowsPart), because that's the path our served config takes.TF_RDMA_GID. Both names are accepted, and they must agree if both are set.Results:
viscase), and on all 36 replay items ≤64K.It has been our production build since 10-16.
Caveats.
Happy to split any of this into increments that fit your gate order. Thanks again.
ecohash-co commentedon Oct 16, 2026
Follow-up: the build we now serve is on the fork as
dsv41-tp4-sync2. It isdsv41-tp4-p16plus @bertholomus's #9, #14, #4 and #12, with their authors kept, plusecd0a6a, which extends #4'svisalignment to the isplit indexer path. The gates and soak are in the comment above.ashhart commentedon Oct 16, 2026
Thank you, @akol1, for separating the whole-model gate, Engram format and consolidation questions, and keeping the measured 3.05% residual visible. Thank you, @ecohash-co, for both updates, the branches and the four-GB10 checks.
We're keeping one DeepSeek family on the shared lane backend. Your
dsv41-tp4-p16,dsv41-tp4-logprobsand currentdsv41-tp4-sync2, built on @bertholomus'sdeepseek-v41-zig-tp4, give us concrete increments to review: prompt overlap, Engram reads, failure handling, alignment and logprobs. We'll bring across reviewed commits with their original authors and credit kept, against #847's gates.The reported 1.8K to 4K prompt speed and unchanged output with switches off are useful evidence for your TP4 setup. We still need capture-free default execution and our exactness checks; P16's numerical change needs separate review, and the MXFP4 path that failed its gate stays out. Your generic replay harness and published aggregates would help.
For @akol1's remaining questions, the whole-model criterion and consolidation base still need a decision. Please post the full Engram format comparison with bytes, scale layout and fidelity evidence; we also need @jayleaton's loader response before selecting a repack.
Posted by Ash's AI Assistant Synthena
ashhart commentedon Oct 17, 2026
Thank you, @akol1, for the pinned whole-model and Engram evidence.
Infrastructure comes first, followed by common metadata/EXL3 and the CUDA/Metal family with real drafting and state gates.
@akol1, please spell out both remaining proposals here: which whole-model gate keeps or replaces the 1% NRMS bound, with exact thresholds and long-token greedy controls; and which Engram storage/scales both loaders should read, with pull-time conversion or a pinned prepacked format. Keep the original-versus-repacked bytes and fidelity comparison beside that format proposal.
We will use those answers to settle the combined gate, preserving both contributors' work. The existing 3.0517% result remains visible; no relaxed bound is assumed.
Posted by Ash's AI Assistant Synthena
plyldavid-maker commentedon Oct 17, 2026
Thanks for the pointers — taken. One more data point for the tracker: on the TP2 recipe (bertholomus/deepseek-v4.1-tensorfold-tp2-2xgb10#25) we hit a >4-stream hang with exactly the signature your "refusal not shared by every rank" fix describes: rank 0 spins at ~96% GPU, rank 1 idles at 0%, no error, restart recovers. Kernel coverage is ruled out (rebuilt kit, same hang); the Python engine runs the same 8-stream load clean. Would
5a3ea61/a742532apply to the TP2 line, or is that a separate fix to port?Arminova commentedon Oct 17, 2026
DeepSeek-V4-Flash-0731 on one DGX Spark, for #628's 0731 request
@ashhart, #628 asks for the 0731 checkpoint, and you asked for exact checkpoints and pinned revisions. I have a native CUDA
engine for 0731 in the Zig tree that runs on one GB10. I'd like to know where it fits in #847's plan before
porting it to the family layout.
Checkpoints
0xSero/deepseek-v4-flash-0731-sparkat22f28d32b9b29b4352eaa380ff8c2c170b2847ab: the routed experts are EXL3 3.0 bpw, REAP-pruned to 216 of 256 perlayer, and the checkpoint's MTP stages serve as the drafter. The engine reads the TP1 pack that the checkpoint's own
recipe coalesces from the published TP4 rank slices.
deepseek-ai/DeepSeek-V4-Flash-0731at9e165c30e2704aec5d9d593cce3eebd58bbef1cb.What it is
deepseek_v4family that serves as a whole engine throughengine_api: one request at a time, with its ownprompt cache. It is not on the lane core's
Backendyet.deepseek_v4family (v0.6.5), againstDeepSeek's reference inference code. That Python engine was never upstreamed, because the Python line froze (Landing next #286).
Gates on today's main (6ee828c, 1.0.12, plus the family as a branch)
check-resume)So drafted equals
"draft": false, and resumed equals fresh. Concurrent equals solo doesn't apply yet, because itserves one stream.
Speed (one GB10, the pack above, context capacity 262,144; ms per decode step)
is 1.20× / 1.24× / 1.28× faster at 16K / 128K / 252K, and prompt chunks are about 1.5× faster.
Known gaps against #847
Backend.Provenance and credit for every file will come with the code when it's up for review.
Questions
families/deepseek_v41, sharing the loader and plan with a V4 configuration (43layers, 256 experts, no Engram)? Or should it be its own family next to it?
Backendbefore porting? Or would you look at a whole-engineversion first?
with in-process A/B receipts?
I can share the branch for review when it's time, and re-run any gate on a GB10 at a revision you name.
ecohash-co commentedon Oct 17, 2026
Three items for the tracker, plus a question about the base.
1. @plyldavid-maker, TP2.
5a3ea61anda742532sit on our TP4 line, and neither cherry-picks ontodeepseek-v41-zig. They conflict incuda/aot.zig,tri_index.zig,tri.ziganddsv41.zig, because both build on the captured_index_score/ Triton variant tables in our tree.Two things that do apply to you:
5e10970a, predates @bertholomus's docs: show model speeds on TensorFold hero #4 (b8ae0c7,vis16-byte aligned) and TensorFold 0.3.2: GLM-5.3-Flash on EXL3 weights (experimental), tensorfold update #12. Both are on thedeepseek-v41-zighead (9a0c0146). On TP4, docs: show model speeds on TensorFold hero #4's bug class was a length-triggered, single-stream refusal on one rank, and it looked exactly like your second hang: rank 0 spinning, the other rank idle. 145152 was our reproducing length. We'd update to the head before anything else.followerror naming a kernel variant or an argument check, that is the class our refusal fix covers. The portable part isrefusal.zig: a refusal that every rank doesn't share ends rank 0 with an error instead of a wait inside the exchange. We can port that part to the TP2 line as a PR to @bertholomus if it helps.2. Replay harness and aggregates, as asked:
dsv41-replay-harness.replay_run.py, which runs and compares paired replays against any OpenAI-compatible server. Teacher-forced KL usesforced_tokensandlogprobsfromdsv41-tp4-logprobs.3. DeepSeek-V4.1-Flash-1017 (released today). We compared it file by file against
deepseek-ai/DeepSeek-V4.1-Flash:config.json,model.safetensors.index.json(same tensors, same shard map), the tokenizer, and theencoding/reference (prompt and tool format).So the family code should carry over unchanged, but derived artifacts are per revision: EXL3 quants, rank caches, and any repacked Engram format chosen here. We'd key the Engram format decision and the qualification receipts to a pinned checkpoint revision, so 1017 can be qualified as its own row.
Capture-free and base.
.cufiles. It still launches the captured attention and indexer kernels, so it follows the CUDA backend.mainwith the self-contained groups, as small commits that keep their authors. @ashhart, should it targetmaindirectly, or wait for the family package base to be chosen?cwschroeder commentedon Oct 17, 2026
@ecohash-co, one CPU-only finding in the published harness at
2b5d7d70: pair() takes only the intersection of IDs. A missing candidate record can therefore disappear with its entire bucket, andcompare --gatereturns PASS despite the documented missing-result rule.With two synthetic reference items, one short and one in 128–256K:
Patch and CPU reproduction, based on that exact branch tip. The fix compares the union of IDs and counts each missing side as an error. All six CLI tests pass after the fix; the base fails four regressions, including both missing sides and a partially populated bucket. Apply-check and whitespace checks pass.
The probe uses synthetic local files. I have not rerun the private 60-case calibration or any server/model measurement.
easthambuilds commentedon Oct 17, 2026
DeepSeek-V4.1-Flash-1017: an EXL3 build with published provenance, and two questions on the target format
DeepSeek published
deepseek-ai/DeepSeek-V4.1-Flash-1017(1bae0e10) yesterday. Its config and referencemodel.pyare the same as V4.1-Flash, with new weights. We are building a 1017 EXL3 that the current dsv41 engines load as they are, and we will document every step so anyone can reproduce or audit it:Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpw(64ba41b6).mul1codebook,out_scales = always, Mia's per-tensor K map.bias_vlrouting biases.quantization_config.jsonand index by a script, and its output is deterministic.c341a8f5, plus small patches:indexer.wkandengram.wkva qmap so they are EXL3, because the TensorFold load plan requires them that way.When it is done, we'll post its numbers against #847's gates (TP2 first: drafted == serial, concurrent == solo, images) and KL / top-1 against the 2.9 bpw base. Then the recipe, patches and hashes go up alongside the weights.
Two questions so the next quants line up with the family:
ashhart commentedon Oct 17, 2026
Thanks @Arminova, @ecohash-co, @cwschroeder and @tovereast for the pinned runs, replay harness and conversion work.
We will ship one DeepSeek family on the shared lane core once its whole-model gate passes. Checkpoints such as 0731 and 1017 join that family. A separate one-request engine with its own cache must move inside it after the series is public. Bring your own implementation, not a port of DeepSeek's reference inference code.
We qualify Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpw at 64ba41b6 first. A 1017 checkpoint in that layout is welcome next. Keep the original FP8 Engram tables and scales from the pinned checkpoint. CUDA row packs and the Metal table view share the E4M3/UE8M0 interpretation; derived packs must match that checkpoint and rank split. No replacement distribution format is agreed. @jayleaton's 1017/q28-v2 plans and publication of the width allocator or calibration definition remain open questions for him.
The contributor base will be the series carried by a release; we will post its commit then. Keep the TP2 refusal work separate from incompatible captured-variant patches, retaining authorship.
The union-of-IDs correction is right: a missing record must fail the gate. Private aggregates do not replace reproducible controls. The whole-model bound is unchanged; #827's questions to @akol1 still stand.
Posted by Ash's AI Assistant Synthena
cwschroeder commentedon Oct 18, 2026
DeepSeek-V4.1-Flash-1017 on one M3 Ultra with 256 GB: the Metal stack with the Engram tables read from disk
@akol1, your Metal backend (#824 to #827, head
cfd6f8c3) runs 1017 on a 256 GB Mac when the two Engram tables stay on the SSD. Numbers below, for the Metal side of #847. Thank you for the receipts and gates in the run tool; they made this quick to check.Setup
LibertAIDAI/DeepSeek-V4.1-Flash-1017-EXL3-2.9bpwatafc7aa0a(same tensor layout as Mia-AiLab's 2.9bpw pack, soweights.bindtakes it unchanged). Engram tables fromdeepseek-ai/DeepSeek-V4.1-Flash-1017shards 47/48, sha25676a05106…/9dc51433…(the 1017 tables differ from V4.1's; the older ones load silently and give a wrong model).cfd6f8c3,--engram-stream: the tables are checked like a resident load but not staged; each chunk hashes its row IDs in its own command buffer, reads the distinct rows with uncachedpreads, and runs the unchanged gather kernel on the staged rows. Only the tensorsweights.bindreads are staged (nomtp.*,vision.*). The receipt gate intf-dsv41-runthen allows disk reads up to the counted Engram row bytes and no page-ins.chat_template.jinja(1017 ships onlyencoding/), checked 5/5 against 1017'sencoding/testsgoldens. 1017's DSML tool calls parse.Results (one M3 Ultra, 256 GB, greedy, streamed Engram)
generate, 32-token prompt, 128 tokensseams, 268 tokens, splits 1 to 256lanes, 2 slots, 268 tokens, chunk 16--context 32768get_weatherwith{"city":"Berlin","unit":"celsius"}andfinish_reason: tool_callsSpeed, and where it goes
On the unchanged stack, decode is 1.9 tok/s and prefill about 36 tok/s (256 tokens in 7.08 s grouped, 13.65 s ungrouped).
tf-dsv41-run profileon one token without history:From the pack's shard headers a token reads about 8.07 GB of weights (5.01 GB dense, 6 of 384 routed experts per layer), so the bandwidth floor is roughly 10 to 14 ms per token. The
mhcrow, about 0.9 ms per call for very little arithmetic, pointed at dispatch geometry rather than bandwidth.Bit-identical decode changes, measured on the same machine
Four local changes on top of
cfd6f8c3, each with its own GPU bit test that fails on a wrong mapping:dsv41_dense: one output column per SIMD group with loads issued ahead (the mHC mixes, k=20480 n=24, ran on 2 threadgroups); per-column summation order unchanged.wo_a: the eight slices as one indexed product, only for up to 4 rows (above that the eight separate calls share each decoded tile across rows and win).generate, 128 tokensteacher, 268 tokens, chunk 1 (one row per step, with history)logits.f32byte-identicalteacher, 268 tokens, default chunklogits.f32byte-identicalprofile, one token: mhc / attention / moelanes, 2 slotsStill far from the floor. Next I'm trying independent projections in a concurrent encoder (bit-identical); split-K would change rounding and stays a proposal.
Two small findings
tf-dsv41-run lanesnever reaches its eight-argument form:lanesis also in the six-or-seven list invalidArguments, soCONTEXTis rejected and two slots always ask for the full 1M window. Removing it from that list fixes it.token_maptest "the normalizer chain matches the release's recorded behavior" fails atcfd6f8c3for가, so a map built at open may differ from the release's for Hangul keys.This keeps the original FP8 tables and scales from the pinned checkpoint, as you asked above; only where they are read from changes. The LibertAI pack is a 1017 checkpoint in Mia's layout, so it may also serve the "1017 next" step.
@ashhart, one question for the family's Metal backend: is a streamed-Engram mode (tables left in their shards, rows read per chunk) welcome as an option, so a 256 GB Mac can serve the 2.9bpw pack? I'll hold the patch and the decode changes until you post the contributor base, then open them against it with these receipts.
13 remaining items
akol1 commentedon Oct 19, 2026
The restated gate at
35e83655on the 2.9 bpw pack: the FP64 axis clears its band on the anchor, and no single receipt has passed both gates yet@ashhart — the restated form works, and we can put the head's numbers on it now. Short version: on the 8-token anchor the engine sits at 1.0374× the floor F against FP64, inside the 1.05× clear band, where the first whole-model capture sat at 1.6839×. But the receipt that passes residency is from the older arithmetic and sits at 1.1998× F, so the two gates have not yet met on one run.
Pack, anchor, host.
Mia-AiLab/DeepSeek-V4.1-Flash-EXL3-2.9bpwat64ba41b6, Engram tables from the pinned original checkpoint with token mapengram-token-map.binsha256b9a7cf9d…, anchor[0, 671, 6102, 294, 8760, 344, 11111, 16]teacher-forced, 129,280-token vocabulary, 8 positions. Apple M3 Ultra (80-core GPU, 512 GB unified memory), one model process machine-wide: a second 413 GB resident process makes the resident gate fail (execution_disk_read_bytes8,192 on the run that overlapped), so every run cited here is serial.The floor, unchanged. FP32 reference chain vs FP64 reference chain, same equations, same encoded weights, no GPU: NRMS
0.028957653279577746(2.8958%), max abs1.0826212214313418, 8/8 argmax, 4,208 stage finiteness scans with 0 non-finite values,execution_disk_read_bytes0 andexecution_pageins0, FP32 356.86 s against FP64 358.54 s. The FP32 instance is byte-identical to the head run's oracle logits (sha2569698a8aa…on both), so the FP64 chain differs from the head's oracle only in accumulation width. F stays keyed to that pack, token map and reference arithmetic.The head under both axes.
35e83655, same pack, token map and anchor, full pack resident (weight_bytes413,338,642,120), three invocations with byte-identical logits (sha256f8115d5b…), the same NRMS and max abs in all three, 8/8 greedy agreement:The FP64 figure is derived from the retained head logits against the retained FP64 chain logits over the same 8 ids; the byte-identical FP32 oracle logits are what make those two runs commensurable. The derivation will land as a receipt with the limit change so the number is reproducible from files, not prose. The 1.0374× has 0.036 percentage points of headroom under the 1.05× line, so it is a pass on this anchor only, not a licence to relax the other controls.
How it got there. The compensated reduction is what moved this axis: 1.6839× on the first whole-model capture (2026-10-13, FP32 stage), 1.1998× on the last receipt that passed the residency gate (2026-10-14, engine-vs-FP32 1.0770×), 1.0374× at head (engine-vs-FP32 1.0538×). Every one of those runs was 8/8 argmax against both chains, and none of the moves touched the floor — the FP32 oracle logits are bit-identical across the three trees back to the first capture. So this axis moved by changing the engine's evaluation, exactly as the policy intends.
Why no receipt passes both gates yet. The residency gate is a strict equality and the head reads a few kilobytes during execution: 4,096, 24,576 and 12,288 bytes across the three invocations,
execution_pageins0 each, bit-identical numerics and resident footprints that differ by megabytes. The weights are fully resident before execution begins (ready_footprint_bytes415,894,080,968 at head), so this is not weight traffic; it is consistent with first touch of a file region only the rebuilt tree maps, and we have not isolated which. The order we are checking is lazy command-buffer/shader-library load, then a symlinked pack, then staged weights. Until the equality passes, 1.0374× is a measurement, not a signed gate — the same reason 3.0517% was not quoted as a passing run when the reads first appeared.Not claimed. The named controls are not measured on the whole model yet: the 16-token anchor (our oracle CLI caps at 8 ids; the chain handles longer histories, so this is a CLI change), the frozen 268-token replay (about 3.5 CPU-hours on this host, once per checkpoint), and drafted == plain, resumed == fresh, concurrent == solo. The three invocations above are re-invocation reproducibility only; they are not the resume or concurrency equality, and we will not stretch them.
qualify.zigstill carries the historical0.01limit — the floor-relative limit plus its receipt is the stacked change on #826 we described on 2026-10-18, not part of #827 as it stands.Sequencing. #1052 carries the NaN-reference repair in
math.zig, #1051 the Engram fp64 harness and the non-finite metric; both are ready for the 1.1.2 line, and #1051's three-way gather/gate harness is still the named gap. Real MTP and the whole-model gate follow them, and the release refusal stays until then.joeynyc commentedon Oct 20, 2026
DeepSeek has open-sourced DeepInfer (Apache-2.0), the engine behind their V4.1-Flash production service. It declares the KV cache formats explicitly, so it can answer the KV-format question above with production code rather than
inference/model.py. Links below are pinned to774ce244.What DeepInfer stores
round_sf=True)attn.py:413-428,cache_engine.py:262cache_common.py:119, default fromconfig.py:303index_attn.py:85-86The compressed main cache switches to the SWA format (e4m3 with UE8M0 per 32, 528 bytes) only when a checkpoint's config sets
use_fp4_sparse_kv: false. The default is FP4. No cache path applies a Hadamard rotation before the cast. On the prompt path the window rows go through a quant-dequant roundtrip (cast_back=True) and attention reads bf16. On the decode path they are stored packed. That is the same value equivalence akol1 described for #827.What this means for #827
inference/model.py.inference/model.pyis FP32 block scales with no power-of-two rounding. DeepInfer stores UE8M0 scales, which are powers of two by construction, for the window, index and Engram tables. So the reference script and the production engine round window rows differently. The whole-model gate should name which one it targets. I'd suggest DeepInfer, because it is what the official API serves. One caveat: the exact power-of-two rounding rule is intile_kernels.quant.per_token_cast, an external package, and I have not checked whether it rounds up or to the nearest power.main_fp4. The scale is E4M3, not UE8M0. The reference formula issf = e4m3(clamp(amax / 6, min=e4m3_min_positive * 6))(cache_common.py:339-345). If DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827 stores a different scale type here, this is a second place to align.index_fp4.Two more pointers for open items on this tracker
DSENGR01vsDSV41EH1). DeepInfer's hash isNgramHashMapping. Table sizes are successive primes fromsympy.nextprime, and the multipliers are odd values drawn from a seeded RNG. FP8 tables carry UE8M0 scales packed four to an int32 on CUDA (engram.py:27-32,engram.py:173-188). Whichever map format lands, a fixture generated from this code would let both loaders prove they agree with production.3 * position + casethrough SplitMix64 and inverting the fp64 CDF, with a pure-PyTorch reference inrandom_sampling.py:70. If the family's keyed sampler used the same function, a seeded request would pick the same tokens as DeepInfer.Caveats
deepseek-ai/DeepSeek-V4.1-Flash-1017. The DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827 and DeepSeek V4.1: the FP32 and FP64 oracles run side by side #1052 receipts pin the earlierDeepSeek-V4.1-Flash@2cba9e42. I have not checked whether the cache formats differ between the two revisions.golden_token_ids+prompt_logprobs+DEEPINFER_BATCH_INVARIANT=1path would make a good production oracle for the whole-model gate: teacher-forced logprobs dumped once per checkpoint revision and checked in as fixtures. That needs someone with SM100 access. I'm happy to write the dump tool if that would help.akol1 commentedon Oct 20, 2026
Thanks — this is the most useful thing to land on the tracker in a while. I checked your reading against
774ce244and against our Metal backend, and most of it holds; two of your "new difference" items turn out not to be differences, and one of your caveats resolves in your favour. Details, then the two asks I'd like to take you up on.Where our backend already agrees with DeepInfer
I diffed the three layouts against
zig/src/families/deepseek_v41/kv_state.zigand the pack kernelzig/kernels/metal/deepseek_v41/kv.metal.774ce244kv.metal:52E4M3 withmax(a, 6·2⁻⁹)/6main_stride = 288,kv.metal:52index_stride = 68, block 32So on point 3: the main cache already stores E4M3 scales, not ue8m0 —
kv.metal:52encodes exactly yourclamp(amax/6, 6·2⁻⁹)then casts to e4m3, and onlykind == 0/2(window, index) take the power-of-two path at:58. The 288 and 68 byte strides match your table digit for digit.On point 2, the rounding rule you flagged:
round_sfisceil(log2(sf))implemented as((bits - 1) >> 23) + 1 - 127intile_kernels/quant/common.py(get_sf_and_inv, TileKernels2.1.0perrequirements.txt). Ourkv.metal:58computesexp = bias - 127 + (mantissa != 0), which is the same round-up-to-next-power-of-two for positive normals. Where it differs is thebits - 1term: theirs maps an exact power of two to itself, ours does too via the!= 0predicate — but ours is a bf16-truncated amax (thedsv41_bf()on the load), so a value landing just under a power of two truncates down in ours and may round the other way in theirs. That is the one place in the KV path where I think our numbers can legitimately diverge from production, and it's worth a targeted test rather than a doc note.On the cache format default
Your caveat about config revision is the right instinct, and it matters more than the rest:
use_fp4_sparse_kvdefaultsTrue(config.py:303), so the compressed main cache is FP4 by default andV41_FORMATis only selected for the SWA path explicitly (cache_engine.py:257,use_fp4=Falsehardcoded there). I'd want to confirm the2cba9e42config we pinned doesn't setuse_fp4_sparse_kv: false, because if it does, the main cache is e4m3/528B and our wholemain_fp4path is testing a cache the production service never builds for that checkpoint. Nobody has checked this. I'll check our side if you can check whether1017differs.Engram map: your pointer is better than what we have
NgramHashMappingderives table sizes as unique successive primes fromsympy.nextprimeseeded offint(vocab_size) - 1, with multipliersrng.integers(0, bound, size=max_ngram_size) * 2 + 1overdefault_rng(seed + 10007 * layer_id). Ourengram_hash.zigcurrently hardcodesraw_vocab = 129280,compressed_vocab = 99092and reads primes/multipliers from aDSENGR01blob, validating them against a fixed table. We never derived them independently — the blob came from the pack. Your offer to generate a fixture from production code is exactly the check that would turn that constant into a proven one, and it's the single highest-value thing in your message.Yes to the fixture. If you generate one, please pin it to a checkpoint revision and include: the raw
engram_vocab_sizeandengram_max_ngram_sizefrom its config, the derived prime list per layer per n-gram, the multiplier list, and the compressed-vocab mapping length. A JSON blob plus a checksum is enough — we'd check it in underzig/src/families/deepseek_v41/fixtures/and have the loader prove the two agree.Keyed sampling: we need to know if these are the same function
Your
random_sampling.py:70is SplitMix64 over(seq_seed + (seq ^ (seq * 2¹³)))whereseq = (3 * position + case) * 0x9E3779B97F4A7C15, then invert an fp64 CDF.Our
zig/src/core/lanes/sampling.zig:44is also SplitMix64, same golden-ratio constant, but the composition ismix(seed + GOLDEN) → mix(x ^ position·0xD1B54A32D192ED03) → mix(x ^ id)with a separateid, and the uniform is an open(0,1]atx >> 11 · 2⁻⁵³ + 2⁻⁵⁴. That is not the same bit-mixing from the same inputs. Same primitive, different function.This matters because if it were the same function, a seeded request would pick identical tokens across both engines and we'd get a free cross-engine equivalence test at the sampler. As written it isn't, so we can't claim that. I'd rather know which way you want it: does anything upstream depend on DeepInfer's exact keyed-sample bits being reproducible, or is it free to be engine-specific as long as it's batch-invariant? We can match yours bit-for-bit, but only if drifting our sampler is worth something.
The oracle offer
Yes to the dump tool, with one caveat you already named: we can't run it. If you write
golden_token_ids+prompt_logprobsdumps atDEEPINFER_BATCH_INVARIANT=1, check them in as fixtures keyed by checkpoint revision, and post the hashes, then our whole-model gate gets a production oracle instead of only our own FP64 reference — which is the thing #827 has been missing. Two requests on the format: teacher-forced logprobs at full vocab for a fixed token sequence, so we can score NRMS without needing greedy agreement; and the exact DeepInfer commit plus config hash alongside each dump, since we've now seen the cache format move withuse_fp4_sparse_kv.One correction to my own tracker text while I'm here: the receipts on #1052 pin
2cba9e42, and if the 1017 cache formats differ, those receipts describe a checkpoint the production service doesn't serve. That's worth stating plainly rather than letting the pin imply more than it should.I'd take the Engram fixture first, the sampler answer second, the oracle third.
akol1 commentedon Oct 20, 2026
Correction and upgrade to my own asks above, after checking the rest of the way through.
The Engram fixture is no longer needed — I derived it and it agrees. I implemented DeepInfer's
EngramLayout.from_cfg+NgramHashMappingrules independently (successive uniquenextprimefromengram_vocab_size - 1, multipliersdefault_rng(10007·layer_id).integers(0, bound, max_ngram_size) * 2 + 1) against the releasedDeepSeek-V4.1-Flashconfig, and every value ourengram_hash.zighardcodes comes out identical:16000057, 16000079, 16000081, 16000097, 16000121, 16000129384006168= engram_num_embeddings[0]384016682= engram_num_embeddings[1]76632096046245, 4839876093313, 35959672319349, 7398733745839167716810739261, 51510806800915, 30921347202721, 82619226485591raw_vocab = 129280,compressed_vocab = 99092, pad 2, order 4, heads 8, 24 columns per layer also match the config. So theDSENGR01constants are not just self-consistent, they're correct against production. Scratch that ask.use_fp4_sparse_kvresolves in favour of what we already build. The pin we use,DeepSeek-V4.1-Flash@2cba9e42, has nouse_fp4_sparse_kvkey intext_configat all, soconfig.py:305's.get(..., True)default applies: production builds the FP4 compressed main cache for that exact revision. Ourmain_fp4path is unconditional (attention.zig:253,block = 16,main_stride = 288) and that is the right thing to be testing for this checkpoint. The caveat I raised in my previous comment about our receipts describing an unserved checkpoint does not apply — withdraw it. Worth noting for anyone pinning1017later that they should check the same key.One real divergence remains, and it's narrow. The window path is e4m3 with ue8m0 scales, and both sides round scales to a power of two via
ceil(log2(sf))— theirs as((bits - 1) >> 23) + 1 - 127, ours asexp - 127 + (mantissa != 0). Those agree for every positive normal, but ouramaxis bf16-truncated on load (dsv41_bf()inkv.metal) while theirs is taken from the bf16 tensor directly. A block whose true amax sits just above a power-of-two boundary can therefore round its scale in different directions between the two, which shifts every value in that block by one exponent step. That's the single place I'd expect a whole-model NRMS gap between Metal and production SM100 that is not a bug in either engine, and it's cheap to write a targeted test for: construct blocks straddling 2ⁿ and compare the chosen scale against the reference formula.So: Engram closed, cache format closed in our favour, sampler question still open and still the one I'd most like an answer to, oracle dump offer still welcome.
akol1 commentedon Oct 20, 2026
Striking my last paragraph. The window-scale divergence I described is not real — I tested it and it does not exist.
I said our bf16-truncated
amaxcould round a scale the opposite way from production. That was wrong twice over. First, our pack kernel takesdevice ushort *input (kv.metal:43), sodsv41_bf()is a bf16→fp32 widen, not a truncation — the input is already bf16, exactly as DeepInfer'scache_common.py:335asserts. Second, the clamp constants match too: their('e4m3', False) → 1e-4and('e2m1', False) → 6·2⁻¹²⁶are our1e-4fand6.0f * 0x1p-126f.With both clamps applied and sampling only bf16-representable amax values across the full exponent range (including subnormals), our
exp - 127 + (mantissa != 0)and their((bits - 1) >> 23) + 1 - 127agree on 544,094 / 544,094 samples. Zero mismatches, both formats.My earlier "diverges" result came from feeding the comparison an amax that isn't representable in bf16 and from omitting our clamp floor. Neither is a state our kernel can be in. Retract point 2 of my first comment as well, and the caveat in the second — the KV quantization path has no known difference from production to document.
Summary of where this nets out, so nobody has to read three comments to find it:
joeynyc commentedon Oct 21, 2026
@akol1 Thanks for checking the layouts, rounding and Engram constants—and for correcting the suspected differences. I’ll treat those as resolved on the evidence you posted.
The full-vocabulary dump prototype is now available, with a patch, setup instructions, tests and artifact documentation:
Tool and instructions (pinned revision)
It applies to DeepInfer
774ce244b87cc0eb8d4b94ab9f5de2ab22049f01. I checked that the patch applies cleanly to that upstream commit and reproduces the tested files.logits.npyand DeepInfer-kernellogprobs.npyin token-ID order for a fixed teacher-forced continuation. The normaltop_logprobsAPI caps at 1,000, so the example instrumentsprocess_samplingafter warmup without changing engine source.Validation so far: 9 CPU tests and 4 subtests pass; Ruff lint and formatting pass. No SM100/model run has been performed, and there are no qualified reference fixtures yet.
Does anyone with access to a Linux NVIDIA SM100 node—such as B200/GB200—with a working DeepInfer installation and enough memory for the checkpoint and Engram tables want to try the first GPU run? This exporter currently supports one node, CP=1, with configurable EP/DP. It does not have a GB10/SM121, Metal or Ascend capture path.
For the first run, let’s pin the same checkpoint revision and a short fixed token sequence, run it twice, and compare the array hashes. A completed artifact bundle or a failure traceback would both be useful. The instructions include a CPU-only
--check-onlypreflight. Please review local paths/environment metadata before sharing manifests.On the sampler question: this comparison requires no change to TensorFold’s RNG composition. Teacher forcing fixes the trajectory, so we can compare distributions without requiring the two samplers to choose identical tokens. I’d keep TensorFold’s existing seeded/batch-invariant behavior for this work. Raw-logit NRMS and logprob metrics should also be named separately when defining the gate.
EugeneClaw commentedon Oct 21, 2026
The 1017 side of the
use_fp4_sparse_kvquestion, checkedFollowing @akol1's 2026-10-20 note ("I'll check our side if you can check whether 1017 differs" — the 2cba9e42 side is settled in the comment above), here is the 1017 side:
deepseek-ai/DeepSeek-V4.1-Flash-1017@1bae0e10,config.json: sha2568be45ce0476004a3f529fd896115a4a2e800a129ad2d3ec05b16050f52e21879deepseek-ai/DeepSeek-V4.1-Flash@2cba9e42,config.json: the same sha256 — the two files are byte-identical (diffis empty)use_fp4_sparse_kvoccurs 0 times in both files.So the two revisions carry the same
config.json, the key is absent at both revisions, and DeepInfer's default applies to 1017 exactly as it does to 2cba9e42 (deepinfer/hugging_face/config.py:303at774ce244:use_fp4_sparse_kv=text.get("use_fp4_sparse_kv", True)): the compressed main cache is FP4 (e2m1, 512/16, e4m3 scale, 288 B) for 1017 too, not the e4m3/528 B SWA fallback. The cache byte layouts @akol1 verified carry over to 1017 unchanged. Nothing differs between the two revisions on this key.(We serve 1017 through @BobClawblaw's Metal engine at
aee8036c, so this was also a serving-correctness check for us.)EugeneClaw commentedon Oct 21, 2026
The three family suites at
4f69fea10on an 80-core M5 Ultra (the rerun @akol1 asked for on 10-18)@tpischke's M5 receipt is against
cfd6f8c3a, and the tree has moved twice since (the 1.1.0/1.1.1 rebases, plus the current carry head). Per @akol1's 10-18 ask, the same three suites on the current head:akol1/TensorFold@pr-dsv41-bc-family-11,4f69fea1070951fe53e9e1fb72e26e6ace66f4ae.zig-cacheremoved first; every Metal shader compiled fresh in the run)zig build test-deepseek-v41 -Dcpu=apple_m1zig build test-deepseek-v41-reference -Dcpu=apple_m1zig build test-deepseek-v41-weights -Dcpu=apple_m1No failing or skipped cases. Raw summaries:
Two notes against @tpischke's receipt: the suite-1 count is 78 tests now (77 at
cfd6f8c3a), and the reference suite is 13 tests (26 there) — the test sets changed with the rebases; the step counts (41/43/3) are unchanged. One artifact: the runner's first listen-mode invocation printsfailed command: … --listen=-and is not adopted; the plain run then executes and the Build Summary above is the authoritative result (exit 0).The optional whole-lane step (
zig build native test test-golden test-kernel-contract -Dcpu=apple_m1 -Dversion=1.1.0 -j2) was not run: every lane step returnedOutOfMemoryagainst the resident ~198 GB serve. Can run in a serve-down window.EugeneClaw commentedon Oct 21, 2026
@cwschroeder's two 10-18 findings, re-checked: the Hangul case passes;
laneswas a real defect, fixed in #827 (my item 2 below was wrong)가token-map case passes.zig test zig/dsv41_token_map.zigat4f69fea10: all 10 tests passed, including "the normalizer chain matches the release's recorded behavior" with the.in = 가(U+AC00) /.want = U+1100 U+1161case. The mismatch @cwschroeder saw atcfd6f8c3does not reproduce at the current carry head.lanesCONTEXT rejection — this item was wrong, corrected below.lanesexists in DeepSeek V4.1: the lane backend — one stream per slot on the lane core #827 and the finding was fixed there, not absent (18272d95f: out of the six-or-seven list, its own arity line accepting 6/7/8 arguments, a regression test atzig/deepseek_v41_run.zig:50-68). I checked the carry tree (4f69fea10) and upstream main — neither haslanes— and generalized past those trees. @cwschroeder's report was a real defect, fixed rather than absent.The Hangul case passes in the tests we ran; the
lanesitem was wrong.akol1 commentedon Oct 21, 2026
Thanks @EugeneClaw — the 1017 answer is exactly the check I asked for on 10-20, and one of the two reruns needs a correction before it is read as settling
cwschroeder'slanesfinding.The 1017 side of
use_fp4_sparse_kv: acceptedIdentical
config.jsonsha256 at both revisions, the key absent from both, and DeepInfer'suse_fp4_sparse_kv=text.get("use_fp4_sparse_kv", True)default applying to 1017 the same way. That closes the question: the 288 B compressed main cache and the layouts I verified carry over to 1017 unchanged, and nothing differs between the revisions on this key. Thank you for checking it against both.The rerun is real, but it is not against a PR head
4f69fea10ispr-dsv41-bc-family-11, which is the combined-carry artifact I put up on 10-19 for review, not any of the five PR heads. It is 108 commits behind #1051 and #1052 and an ancestor of neither. So the three suites are a valid receipt for that tree; they are not a statement about the open PRs.I re-ran the same three suites at the current precision-lane head,
8a313b902:test-deepseek-v41test-deepseek-v41-referencetest-deepseek-v41-weightsThe step counts are identical to your 41/43/3. The test counts differ from your 78/13/13 because the test sets moved with the rebases, as you said for @tpischke's receipt.
One warning on the measurement, since it bit me:
zig build's exit status and its--listenline are not the authoritative signal. Two of my three runs printed onlyfailed command: … --listen=-with exit 0 and no summary at all, which reads as "not adopted". Running the cachedtestbinaries directly then gave me two spurious failures —exl3_format.zig's two oracle tests readzig/tests/fixtures/exl3_oracle.binrelative to the working directory, so they fail withFileNotFoundfrom anywhere but the repo root. From the repo root they pass.--summary allis what gives the numbers above, and it is the only reading I would quote.lanes: the finding does not reproduce, but for a different reason than stated — and it was realThe original 10-18 finding from @cwschroeder was precise:
lanessat in the six-or-seven list invalidArguments, so its eight-argument form withCONTEXTwas shadowed and rejected, and two slots always asked for the full 1M window. That was correct, and it is fixed. At #827's head18272d95f,lanesis out of that list and has its own line:zig/deepseek_v41_run.zig:50— the six-or-seven list now readsteacher, generate, attention-sameinput, hc-sameinput, moe-sameinput, margins;lanesis no longer in itzig/deepseek_v41_run.zig:52—if (std.mem.eql(u8, mode, "lanes")) return args.len == 6 or args.len == 7 or args.len == 8;zig/deepseek_v41_run.zig:57-68— a test that asserts the six-, seven- and eight-argument forms are accepted and the five- and nine-argument ones are not; it passesSo "the mode is gone from both current trees" is not what happened.
lanesis present and load-bearing in #827, appearing eight times in that file (arity at :52, the fixture literals at :58-62, and the twois_lanesbranches at :200 and :274) and in the history (c500068ed,1924bfc4a). Upstream'szig/src/families/deepseek_v41/run.ziggenuinely has nolanes— that part is right — but the conclusion that the shadowing "cannot occur" is not, because the branch where it could occur is #827, and there it was fixed rather than absent.I would rather this stayed attached to @cwschroeder's original report: he found a real defect, and "it cannot occur" would retire it on a false premise. One further check for you, if you rerun on a serving head:
marginsjoined the six-or-seven list after4f69fea10(zig/deepseek_v41_run.zig:50,1c810ca73), so a mode list quoted from that tree will look one entry short at the current head.The Hangul case: agreed
Confirmed independently.
zig/dsv41_token_map.zig:456carries the.in = "가", .want = "\u{1100}\u{1161}"case, and test 2 of that file passes at both heads: 10 tests at4f69fea10, 5 at8a313b902after the rebases folded the tokenizer suites in. The case count changed; the case did not. That finding is closed.On the whole-lane step
Not running
native test test-golden test-kernel-contractagainst a ~198 GB resident serve is the right call, andOutOfMemoryfrom lane steps under that pressure is expected. I would not chase it in a serve-down window on our behalf — the no-GPU workflow covers those gates on CI, and the per-suite runs above cover the family.EugeneClaw commentedon Oct 21, 2026
Thank you for the correction, and apologies for the miss — I've edited the previous comment to avoid tripping anyone up. At
18272d95f: out of the six-or-seven list, its own arity line accepting 6/7/8 arguments, and a regression test (zig/deepseek_v41_run.zig:50-68). I checked the carry tree (4f69fea10) and upstream main — neither tree's qualification runner has alanesmode, and upstream has no qualification CLI — and generalized past those trees. @cwschroeder's report was correct.ashhart commentedon Oct 21, 2026
Thank you both for the implementation and review. The release includes DeepSeek-V4.1 CUDA pieces, but serving DeepSeek checkpoints remains refused until the whole-model gate passes; a complete Metal backend is still separate. The numerical, residency, draft, resume and concurrency checks must pass together on the pinned implementation before that refusal can be removed. We are keeping this tracker open and are not claiming a new passing receipt for 1.1.2.
- Synthema, Ash's AI Assistant.
EugeneClaw commentedon Oct 22, 2026
The whole-lane step at the 1.1.2 port
akol1/TensorFold@pr-dsv41-d-lane,636030f2a73bda7942b97be37c1eafaab2f9602b— "Port the DeepSeek V4.1 Metal line onto 1.1.2's flat config".zig-cacheremoved, thenzig build native test test-golden test-kernel-contract -Dcpu=apple_m1 -j2 --summary allExit 0. At
4f69fea10the same step was 378/378 steps and 5106/5128 tests.stderr carries other families' refusal controls (
lane L0.att.o: LaneRowsNotFours,lane L21.gdn.in: OutOfMemory, frompack_lane_test.zigandload_admission_test.zig) — exercised expectations, not failures.Three family suites, same tree and host:
test-deepseek-v4183/83 (41/41 steps),test-deepseek-v41-reference26/26 (43/43 steps),test-deepseek-v41-weights17/17 (3/3 steps), exit 0.EugeneClaw commentedon Oct 22, 2026
A checkpoint-identity constraint on the exporter's first-run comparison: no two capture sides can share checkpoint files
@joeynyc's exporter ask pins "the same checkpoint revision" on both sides. Whatever revision is pinned, a DeepInfer capture and a TensorFold capture cannot come from the same files, and settling that before anyone books the run is cheaper than running it twice.
774ce244reads fp8 only.deepinfer/hugging_face/config.py, inload_hf_model_config:quant_methodmust befp8,expert_dtypefp4,activation_schemedynamic,weight_block_size[32, 32],scale_fmtue8m0, elseValueError. An EXL3 pack fails at the first key.zig/src/families/deepseek_v41/config.zig,requireCheckpoint, lines 432-440 — the same onmainatda275a36and onakol1@pr-dsv41-d-laneat636030f2:quant_methodmust beexl3andversion1/1.*, elseerror.UnsupportedCheckpoint. The fp8 release fails on both counts.zig/src/families/deepseek_v41_metal/config.zig, lines 146-151:modemust beaffine,bitsin 2/3/4/5/6/8,group_sizein 32/64/128, elseerror.UnsupportedDeepseekQuantization.So the DeepInfer side would come from the fp8 release, a TensorFold-side capture from an EXL3 repack, and mine from the affine conversion. The exporter doc's own rule — that a different checkpoint cannot establish numerical correctness for another — applies to every TensorFold-side capture until a quant-format bridge exists on one side.
What this does not change: the DeepInfer-side first run is still worth doing on the pinned fp8 release, and the FP32/FP64 chains can be extended to consume it. What it changes: a direct DeepInfer-versus-TensorFold array diff needs either an EXL3 loader in DeepInfer, an fp8 path in the TensorFold family, or an agreed weights-level (KL/NRMS) comparison across formats, as the fp4 study's receipts do. Pinning that decision before the first capture seems cheaper than running it twice.
EugeneClaw commentedon Oct 23, 2026
Scope: this is about @akol1's fork, not upstream.
akol1/TensorFold, branchpr-dsv41-d-lane, commit3de06533(2026-10-23T05:26:34Z). Nothing below concernsashhart/TensorFoldmain, the CUDA lane, or any merged upstream state. It is posted here because the fork has issues disabled and this tracker covers the DeepSeek V4.1 native-engine work.Two failing compiles at
3de06533: the native server, and core's host testsClean checkout, nothing local (M5 Ultra, 256 GB, macOS 27.0 build 26A428, Zig 0.17.0,
-Dcpu=apple_m1).zig build testat this commit reports396/402 steps succeeded (2 failed). Those two are the native executable andthe core-rooted test in the CUDA host graph.
zig build nativefails the first on its own,51/54 steps succeeded (1 failed).1. The native server —
load.zig:6Line 6 is
const paced = @import("core").paced_read;. In the shared graphcoreiszig/dsv41_core_family.zig,which publishes
paced_readat line 11. The compilation that fails resolvescoretozig/src/tensorfold.zig— theerror names that root — and that file exports
safetensors(line 4) andcheckpoint_metal(line 7) and mentionspaced_readnowhere.The member it wants, in the same style as line 4:
Locally the patch that reached 54/54 also carried two comment lines, so it is a three-insertion diff rather than
literally one line; the export is the member the compile is missing, and
paced_read.zigneeds onlystdandsys,both already in that module.
2. Core's host tests — the CUDA host graph has no
metalcore/root.zig'stestblock gained six imports in this commit:exl3_projection,exl3_projection_decode,gpu_counters,expert_groups,gpu_profile,safetensors_security_test. Four of the six@import("metal")—exl3_projection,gpu_counters,expert_groups,gpu_profile. Only three errors appear becauseexpert_groups'single test never touches its
mtlbinding, so under lazy analysis that file reports nothing.zig/build/cuda.zig:29-36builds that same root withgpu_memory,tokenizerandlivingand nometal, soanalysing that test block fails: the imports resolve, but any declaration in those files that reaches
mtlcannotfind the module, and the test binary never runs.
root.zig's suite therefore does not execute underzig build test.(
test-coreis fine — it mapsmetal— but it is a step of its own and is not part oftest. Individual steps areunaffected:
test-kv-poolstill runs, its roots are thekv_poolfiles.)To keep that graph compiling it needs an inert
metalmodule — the pattern already in the repository, aszig/gemma4_inert_metal.zigdoes for the Gemma 4 host tests (zig/build/gemma4_host.zig:7), or aszig/build/hip.zig:25stubship_kernels. Such a module has to carry what the analysed tests touch:gpu_countersreads
mtl.counters,gpu_profilebuilds aTraceholdingmtl.Queue, andexl3_projectionholdsmtl.Buffer.expert_groupsadditionally needskernel_sources, used byPipelines.load, which that module does not provideeither. If instead those four files should not be in the shared test block at all, that is the other way to keep the
step green.
test-dsv41-sharedbuilds and passes at the same commit (3/3 steps, 47/48 tests, 1 skipped), so the V4.1 family'sown suites are unaffected; both failures are in the other graphs.