ENG-EXPERT-STREAM: the CUDA arm for IQ1_S and IQ1_XXXS, verified on GB10 - #967
Merged
Conversation
Without these two the Qwen3.8-2.4T routed experts, which are 96.92 % of either checkpoint, fall through `IsCudaKeepQuantSupported` to the CPU arm. That path emits CORRECT tokens at CPU speed, so no token gate can see it and the model simply looks slow. Naming it as owed was right; leaving it owed while claiming a device benchmark would not be. Both device dots are transcriptions of the CPU kernels they must agree with bit for bit, including the `bsums` split that neither of the other IQ dots here needs: IQ1_S and IQ1_XXXS reconstruct dl*(grid + delta), so the dot separates into an integer grid term plus delta times the activation's per-16 sums, which Q8_K already caches. The device grids deliberately keep the SAME u64 layout as the CPU tables rather than upstream's packed u32 `*_grid_gpu` variants. One encoding to validate instead of two, the device dot reads as a literal transcription of the CPU one, and the provenance digests the CPU tests re-derive therefore cover these bytes as well. Parity is gated by the existing CUDA-vs-CPU case, which is the assertion that matters here: `nmse_cpu <= 1e-6` against the CPU oracle, plus `nmse_ref` against an independent f64 dequantize. The synthetic scale narrowing mirrors the CPU test for the same measured reason, and the delta sign bit stays random so a kernel that dropped it is still caught. STATUS, stated plainly: this COMPILES and LINKS on GB10 (nvcc 13.0.1, sm_121a) and is NOT yet verified on device. dgx's GPU is held by another session, so the parity test has not run. Do not read this as a working device path until that test reports. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ef bound measured The device kernels now have a hardware result rather than a compile. On dgx.casa (GB10, sm_121a, nvcc 13.0.1) the parity case reports nmse_cpu = 5.2e-14 against the CPU oracle, on a 1e-6 bound. That is the assertion this port lives or dies by, and it passes by eight orders of magnitude. One case needed a ceiling, and the reason is measured rather than assumed. The f64-dequant comparison holds a Q8_K-quantized ACTIVATION against an f32 reference, so it bounds activation error, not the kernel. iq1_s reported 5.23981e-4 against the 5e-4 default at m=4, k=2048, n=1, where n=1 and m=4 is four dot products. The CPU test measures 5.23975e-4 at the SAME shape: the two agree to six significant figures, which is what identifies the activation term rather than anything device-specific. The ceiling is per-CASE and applies only to that comparison; the CUDA-vs-CPU bound is untouched at 1e-6 and shared by every encoding. Set to 2e-3: about 4x the residual, and an order of magnitude below the weakest decode defect the CPU mutation run measured (2.7e-2 for a dropped delta term). Also recorded before this ran: the two device dots were diffed line by line against the CPU kernels they transcribe. Every arithmetic line matches except `GRID + i` versus `&GRID[i]` and `y[i].bsums` versus `yb->bsums`, which are the same expressions written for a loop over blocks and for a single block pointer. A desk check is not a hardware result, which is why it did not stand in for one. The GPU lock was 3h38m stale with zero compute processes when this ran, past the 2h TTL, and was reclaimed for the duration and released after. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Collaborator
Author
Gated locally, CI saturated
The device run was on dgx.casa (GB10, sm_121a, nvcc 13.0.1) inside Not run here: |
localai-bot
pushed a commit
that referenced
this pull request
Aug 16, 2026
…the first, and it came from main (#968, #672) With #965's `C4456 'loaded' shadow` removed, `windows-msvc-cpu` and `windows-msvc-vulkan` failed again on this row's pull request — and on a different cause: include\vector(1461,29): error C2220: the following warning is treated as an error include\vector(1461,29): warning C4244: '=': conversion from 'const double' to 'float', possible loss of data raised from `src/vllm/multimodal/ltx2_video.cpp:203,214`, the two narrowing `positions.assign` calls that `c7cb59fbb` (#964, LTX25-TOKEN-APPEND) landed on `main` while this row was in flight. `StreamState::positions` and `Ltx2LatentState::positions` differ in element type; GCC and Clang narrow silently, MSVC diagnoses and the build treats it as an error. NOT FIXED HERE, deliberately. #964's own comment at `ltx2_video.cpp:129-132` reasons that "double -> float -> double reproduces the bits", so the narrowing is intentional and a silencing `static_cast` would be a claim about that reasoning rather than a formatting repair. It belongs to the lane that owns the round trip. Filed as #968 with the evidence rather than papered over. THE MATCHED ARM SPLITS EXACTLY ON THE MERGE BASE, which is what makes it inherited rather than mine. Grepping each `windows-msvc-cpu` job log for the warning: #966 and #951, both on `c7cb59fbb`, hit it twice each; #967, #956, #950, #939 and #938, all based before it, do not hit it at all. This row's diff touches zero LTX2 files. THE FINDING WORTH CARRYING is not either warning. It is that TWO INDEPENDENT CAUSES WERE STACKED BEHIND ONE HABITUALLY-RED JOB NAME, and the first hid the second — and that neither was #645, the `M_PI` regression both jobs are usually attributed to. A known-red list tells you a job is often red. It never tells you that today's red is the same one. Only reading the log does. Issue: #968 FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 16, 2026
FOLLOWING_AGENTS_PROTOCOL Keeps the branch current before review. The incoming commit is the ENG-EXPERT-STREAM CUDA arm for IQ1_S/IQ1_XXXS (#967); it touches no MiniMax-Music3 surface. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot
pushed a commit
that referenced
this pull request
Aug 16, 2026
…gating it (#931) FOLLOWING_AGENTS_PROTOCOL `origin/main` moved from `c7cb59fbb` to `10813dfeb` (#967) WHILE the post-merge preflight was running. The remote-tracking ref is shared by every worktree of this checkout, so a sibling worktree's fetch advances it under a run in progress. The visible symptom was a preflight that printed "All gates green." without printing its "Commit trailers vs origin/main" block at all: the block is guarded by `git merge-base --is-ancestor origin/main HEAD`, which had been true when the run started and was false by the time the guard was evaluated. A green banner is therefore not evidence that the guarded blocks ran. Read the block headings, not the banner. This merge restores the ancestry so the trailer block is armed again. Automatic merge, no conflict, and the incoming change touches only `src/vt/cuda/cuda_quant_dot.cu`, `src/vt/cuda/cuda_quant_iq_tables.cuh` and `tests/vt/test_cuda_quant_dot.cpp`. Nothing it carries overlaps this row's files, and no keyed record is involved. Nothing is rebased, amended or forced. Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code]
localai-bot
added a commit
that referenced
this pull request
Aug 16, 2026
… heard, and the five keys upstream refuses that we dropped (#672, #953, #965) (#966) feat(MODEL-MUSIC-MUSIC3): a music-only server, an example that can be heard, and the five keys upstream refuses that we dropped (#672, #953) Developer directive: parity on what upstream supports — "we want to be a good reference" — usage docs for MiniMax-Music3, and in those docs the weights. Then, mid-flight: "we should allow to load only the music model" and "we need to have an e2e test working". FOLLOWING_AGENTS_PROTOCOL ## The upstream surface, enumerated rather than summarized SGLang-Omni `748a0b43` `models/minimax_music3/` and diffusers `c6da9936` `modular_pipelines/minimax_music3/`, read field by field and recorded with `file:line` in spec §10.1 so the next reader re-derives nothing. **Closed here:** the music-only server, the missing example, and five refusals. **Owed and named:** the non-`wav` response formats, request batching and `/v1/audio/speech/batch`, the 32 kHz delivery resample. **Permanently refused rather than owed:** streaming — neither upstream arm has it (`supports_streaming_vocoder=False`). **One place we are ahead of both arms:** `guidance_scale` is a real per-request control here, where diffusers freezes it at 1.7 into the guider component (`denoise.py:180`) and SGLang exposes it only as a serve-time knob. ## `--model` is optional when `--speech-model` is given Serving a 28.5 GB music model also forced loading an unrelated text model, and on this box the smallest text checkpoint is 35B — so the recipe this project documented was effectively unrunnable. Upstream's own is `sgl-omni serve --model MiniMaxAI/MiniMax-Music3`, no text tower anywhere. vllm-server --speech-model /path/to/minimax-music3 Third instance of a shape already in `server_main.cpp`: a pooling checkpoint serves `/v1/embeddings` alone, a Parakeet checkpoint serves `/v1/audio/transcriptions` alone. It mirrors vLLM's task-conditional registration (`api_server.py:255-265`). **Additive, and proved rather than argued.** The only case whose verdict changes is BOTH flags absent, which was an error and remains one with a message naming both options. The route table is gated in both directions over a real socket, because a handler-dispatch test cannot see route registration at all. ## The example the music family did not have `examples/minimax_music3_gen` — a thin client of `include/vllm.h` and nothing else, like `parakeet-transcribe` and `vllm-cli`. Hearing this model previously needed a running server plus a `curl`, or a C ABI caller nobody had written. ## Five keys upstream refuses by name were SILENT here (#953) `temperature`, `top_p`, `top_k`, `repetition_penalty` — refused upstream at `request_builders.py:14-19,109-114`, because this model's autoregressive stage has ONE sampler, a fixed top-50 draw (`encoders.py:48,94-103`). And `max_new_tokens`, upstream's LENGTH spelling in 25 Hz frames rather than seconds (`request_builders.py:56-68`), so a 250-frame request silently became the family's 60 s default. That is the #925 class exactly, in the same file that already carries #925's refusal one paragraph above. Fixed in flow. ## The e2e gate no longer reports a skip wearing a pass It read 5 cases / 5 passed with **`assertions: 0`** whenever the checkpoint was absent — the same shape that fooled this project on `test_qwen3_paged_engine`. Split into a checkpoint-free half that runs unconditionally in CI (request contract, both ceilings, the speech-only route table over a real socket with a stub synthesizer) and the env-gated half, whose HTTP case now drives the real engine over a real socket against the music-only server shape. A coverage-report case prints which arms ran, every run. The full arm was run: `POST /v1/audio/speech -> 200 audio/wav, 12332 bytes in 518 s wall`, 2 AR frames -> 6 latent frames -> 3072 samples/channel, 6144 int16 samples all non-zero, 0 clipped, 2818 of 3072 positions differing between L and R, and `/v1/completions` + `/v1/chat/completions` both 404 from the route table. `checkpoint_arms_run=5`. | arm | cases | assertions | |---|---|---| | `test_minimax_music3_e2e_real`, no env vars | 9 | 37 (was 5 / **0**) | | `test_minimax_music3_e2e_real`, checkpoint only | 9 | 86 | | `test_minimax_music3_e2e_real`, checkpoint + `VLLM_CPP_MUSIC3_DIT=1` | 9 | **582** | | `test_speech_api` | 6 | 67 | | `test_openai_api_server` | 62 | 733 | | `test_openai_conformance` | 23 | 252 | | `test_minimax_h3` (unchanged) | 79 | 57395 | | server flag ctest cases | 7 passed | | ## The weights are documented (porting-a-model.md §2.1) `docs/USAGE.md` gains component-by-component tables: the diffusers arm at `MiniMaxAI/MiniMax-Music3`@`fbdf52fbaaca799592917417eb05f1899f1255ec`, **28.5 GB resident** (28 517 617 303 B, measured) out of a 57.4 GB repository and why they differ; the native `.pth` arm we refuse and SGLang-Omni serves; the one implemented GGUF Q4_K artifact with its sha256; and the fourteen third-party quantized repositories in five formats, each marked refused. The revision is verified rather than copied — `condition_encoder/diffusion_pytorch_model.safetensors` on disk hashes to that revision's own LFS record. ## A sample a human can hear 2.0 s of 44100 Hz stereo from this engine in 3286 s of wall clock: RMS 0.03169, peak 0.97437 with 0 clipped samples, 84 073 of 88 064 positions differing between left and right. **Its samples are compared to nothing** — §5 withdrew the token gate — so it shows the pipeline runs, not that the music is right. It is not committed: `check-pr-size.py` classifies every path and none takes a `.wav` outside `tests/`, where a file compared to nothing would sit beside the goldens and imply it was one. ## The four asks, answered directly **1. Music-only server.** `vllm-server --speech-model <dir>` with NO `--model` starts and serves, observed live rather than inferred: server: speech/music-only model (family=minimax-music3, 44100 Hz, text-only synthesis, family DETECTED); serving /v1/audio/speech server: listening on http://0.0.0.0:18923 (model 'minimax-music3') `--model` alone and `--model` + `--speech-model` are **byte-identical in behaviour**. The whole change is one new early branch, `if (args.model_dir.empty())`, which loads the speech engine and `return 0`s before reaching a single line of the existing path; nothing downstream of it was touched. The only case whose verdict changes is BOTH flags absent, which was an error and remains one. Server suites: **7 of 7** ctest cases (4 pre-existing + 3 new — neither flag is still an error and now names both options; `--speech-model` alone reaches the speech LOAD; `--speech-family` alone still demands a checkpoint), `test_openai_api_server` **62 cases / 733 assertions** (+1 case / +24 assertions, the speech-only route table over a real socket), `test_openai_conformance` **23 / 252** unchanged. **2. e2e, three arms.** What a bare CI run executes unconditionally: the request contract on the exact body the real case posts, the near-miss and sampling refusals, the duration arithmetic including both ceilings, and the speech-only route table over a real socket with a stub synthesizer. What stays env-gated: everything needing the 28.5 GB checkpoint, plus the two 2.4B-DiT arms behind `VLLM_CPP_MUSIC3_DIT`. | arm | cases | assertions | checkpoint arms run | |---|---|---|---| | no env vars (what CI runs) | 9 | **37** | 0 — was 5 cases / **0 assertions** | | `VLLM_CPP_MUSIC3_CHECKPOINT` | 9 | **86** | 3 | | + `VLLM_CPP_MUSIC3_DIT=1` | 9 | **582** | 5 | **3. The five keys.** All five were **accepted and silently dropped**; all five are **now refused by name**. | key | upstream anchor | why it cannot be honoured | |---|---|---| | `temperature` | `request_builders.py:14-19,109-114` | the AR stage's only sampler is a fixed top-50 draw, `encoders.py:48,94-103` | | `top_p` | same | no nucleus branch exists | | `top_k` | same | `_AR_SAMPLING_TOP_K` is a module constant of 50 | | `repetition_penalty` | same | no penalty is applied anywhere in the loop | | `max_new_tokens` | `request_builders.py:56-68`, `constants.py:4-5` | upstream's LENGTH, in 25 Hz frames not seconds; the refusal names `audio_duration` and the /25 conversion | **4. The weights table** (`docs/USAGE.md`, "MiniMax-Music3: the exact weights"). It carries: repo **and** revision — `MiniMaxAI/MiniMax-Music3`@`fbdf52fbaaca799592917417eb05f1899f1255ec`, verified rather than copied, since `condition_encoder/diffusion_pytorch_model.safetensors` on disk hashes to `83179c5e…a202c2a4d`, that revision's own LFS record; the Q4_K artifact's sha256 `4c5d41b2…c70cbdd0` at revision `c36aaeed…` with its exact byte count; **28.5 GB resident (28 517 617 303 B, measured) versus 57.4 GB repository**, with the reason they differ; the refused native `.pth` arm (`qwen_7B/`, `flowmatching_vae.pth`, `dav.pth`) and that SGLang-Omni serves it; and all fourteen community quant repositories across five formats, each marked refused and each marked **third-party** rather than first-party. This is the first application of `.agents/porting-a-model.md` §2.1 (landing as #951). ## Two reds stacked behind one habitually-red job name `windows-msvc-cpu`/`windows-msvc-vulkan` are usually attributed to #645 (`M_PI` in three LTX2 sources). **Neither of the two causes here was #645**, and the first hid the second. **#965, fixed in flow.** `C4456: declaration of 'loaded' hides previous local declaration` at `server_main.cpp:1315` — W6's own speech-attach block declaring `loaded` inside the text engine's `loaded`. The only warning in the job, on `main` since W6 landed. Matched arm: #956, #950 and #939, none touching the speech surface, fail identically. Renamed; nothing suppressed. **#968, filed and NOT fixed here.** With the shadow gone the same jobs failed again on `C4244: conversion from 'const double' to 'float'`, raised inside MSVC's `<vector>` from `ltx2_video.cpp:203,214` — two narrowing `positions.assign` calls that `c7cb59fbb` (#964) landed on `main` while this row was in flight. **This branch touches zero LTX2 files.** The matched arm splits exactly on the merge base: #966 and #951 (on `c7cb59fbb`) hit it, #967/#956/ #950/#939/#938 (before it) do not. It is deliberately left to the LTX-2.5 lane — #964's own comment reasons that "double -> float -> double reproduces the bits", so a silencing cast is a claim about that reasoning rather than a formatting fix. **The finding, which outlives both:** a known-red list tells you a job is often red. It never tells you that today's red is the same one. Only reading the log does — and here it took two readings, because removing the first cause is what made the second visible. <!-- kept for the record --> ### The first of the two, in detail (#965) `windows-msvc-cpu`/`windows-msvc-vulkan` failed here, and they are **not** #645 (`M_PI` in three LTX2 sources). They were W6's own `C4456: declaration of 'loaded' hides previous local declaration` at `server_main.cpp:1315` — the only warning in the job, on `main` since W6 landed. The matched-arm check is what separated it from my diff: #956, #950 and #939, all touching no speech surface, fail identically. `main` has no baseline because `windows-msvc-*` are PR-only (#584), so it presents to each author in turn as their own red. Filed and fixed in flow by renaming the inner declaration; nothing suppressed. ## Mutations Four run, four fire: sampling refusal neutered (5 assertions red), `max_new_tokens` refusal neutered (2 red), `--model` made mandatory again (2 ctest cases red), generate routes registered unconditionally (3 cases / 6 assertions red in the api-server suite, 1 / 2 in the e2e suite). Sources restored and verified sha256-identical. Supersedes #954 (untrailered merge commits) and #963 (a `server_main.cpp` commit that owed `docs/USAGE.md` under the per-commit documentation checkpoint). Same tree, linear history, every commit green on `check-doc-checkpoint`, `check-commit-trailers` and `check-commit-style` locally before pushing. Every source file is byte-identical to the one built and gated. Issue: #672 Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [Claude Code] --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
This was referenced Aug 16, 2026
localai-bot
pushed a commit
that referenced
this pull request
Aug 16, 2026
…d called it success #967 taught `IsCudaKeepQuantSupported` to return true for `kIQ1_S` and `kIQ1_XXXS`. Three dispatch switches consume that predicate, and #967 extended one of them. `MatmulBTQuantGroupedKernelCuda` used the same predicate to skip its CPU fallback, then dispatched through a `switch (w)` with no case for either dtype and no `default:`. It quantized the activation, launched nothing, and returned. `CheckCuda(cudaGetLastError())` reported success, because a launch that never happened cannot fail, so the output tensor kept whatever it already held. An independent review measured that on GB10 through a poisoned output buffer: both IQ1 encodings left -12345 in place, at NMSE 4.58e6 and 9.96e6 against the CPU oracle, while the `iq2_s` control passed. That path is the default routed-expert path of Qwen3.8-2.4T, and the two encodings are 96.92 % of it, so #967 turned correct-but-slow into silently wrong. The fused MoE SwiGLU seam had the same hole, where a NAMED refusal became silence. Both grouped switches gain the two arms. No kernel code was needed: the grouped templates are generic in `W` and depend only on `DotSuperblock<W>` and `FinalFactor<W>`, which the dense path already specializes. The `default:` that now throws on all three is the more useful half. Past that predicate there is no fallback left, so any future missing case is a silent no-op unless something refuses out loud. It landed green because the grouped dispatch had no test at all: two files in `tests/` mention `MatmulBTQuantGrouped` and neither mentions `kCUDA`. Both grouped seams are now driven over the dense gate's case table, against the CPU grouped golden, through a poisoned output buffer. The poison is not redundant with the value comparison. Mutating `MatmulBTQuantKernel` to write nothing leaves the golden AND the independent reconstruction both at the poison value, so `memcmp` passes with zero failures and only the poison assertion fires: 55 failed assertions, all `poisoned == 0`. Every value gate in that file would have read an unwritten buffer as a merely inaccurate result. A CPU arm of the same probe runs on every host, because both CUDA cases return early without a device and doctest prints SUCCESS for a gate that asserted nothing. It takes the host from 0 assertions to 112. `cuda_quant_iq_tables.cuh` claimed from the day it landed that a runtime test memcmps the device tables against the CPU ones. No such test existed, and nothing read `vt::cuda::d_iq1s_grid` at all: the CPU tests digest the host symbols, which are different objects in a different address space. Replaying the CUDA gate's own mt19937(0x5EED) stream, 266 of 2048 `d_iq1s_grid` entries (13.0 %) are never addressed, so drifting entry 0 is caught and drifting entry 3 is green at 150032/150032. The seal now exists and covers all eight device codebooks byte for byte. Both false comments are corrected. Not done: `-Wswitch` for CUDA. It would not have caught this. `-Wswitch` is silent whenever a `default:` label exists, and a `default:` is what this change adds. The flag that fires is `-Wswitch-enum`, which warns on every enum switch in the tree that omits an enumerator even with a default, and that is a tree-wide change with no measurement behind it. Owed: the CUDA arms of the new gate have not run on a device. `dgx.casa` was unreachable throughout (`No route to host`, ping 100 % loss), this box has no CUDA toolkit and no NVIDIA device, and there is no second CUDA host. Issue #1029 stays open for the GB10 run. Closes nothing. Refs #1029, #912. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
localai-bot
added a commit
that referenced
this pull request
Aug 16, 2026
…d called it success (#1029) (#1035) #967 taught `IsCudaKeepQuantSupported` to return true for `kIQ1_S` and `kIQ1_XXXS`. Three dispatch switches consume that predicate, and #967 extended one of them. `MatmulBTQuantGroupedKernelCuda` used the same predicate to skip its CPU fallback, then dispatched through a `switch (w)` with no case for either dtype and no `default:`. It quantized the activation, launched nothing, and returned. `CheckCuda(cudaGetLastError())` reported success, because a launch that never happened cannot fail, so the output tensor kept whatever it already held. An independent review measured that on GB10 through a poisoned output buffer: both IQ1 encodings left -12345 in place, at NMSE 4.58e6 and 9.96e6 against the CPU oracle, while the `iq2_s` control passed. That path is the default routed-expert path of Qwen3.8-2.4T, and the two encodings are 96.92 % of it, so #967 turned correct-but-slow into silently wrong. The fused MoE SwiGLU seam had the same hole, where a NAMED refusal became silence. Both grouped switches gain the two arms. No kernel code was needed: the grouped templates are generic in `W` and depend only on `DotSuperblock<W>` and `FinalFactor<W>`, which the dense path already specializes. The `default:` that now throws on all three is the more useful half. Past that predicate there is no fallback left, so any future missing case is a silent no-op unless something refuses out loud. It landed green because the grouped dispatch had no test at all: two files in `tests/` mention `MatmulBTQuantGrouped` and neither mentions `kCUDA`. Both grouped seams are now driven over the dense gate's case table, against the CPU grouped golden, through a poisoned output buffer. `cuda_quant_iq_tables.cuh` claimed from the day it landed that a runtime test memcmps the device tables against the CPU ones. No such test existed, and nothing read `vt::cuda::d_iq1s_grid` at all: the CPU tests digest the host symbols, which are different objects in a different address space. Replaying the CUDA gate's own mt19937(0x5EED) stream, 266 of 2048 `d_iq1s_grid` entries (13.0 %) are never addressed, so drifting entry 0 is caught and drifting entry 3 is green at 150032/150032. That figure came from the review and was re-derived here rather than quoted: the dense gate's widest weight is 128 blocks and 4096 grid draws, giving 1782 distinct entries and 266 untouched. The seal now exists and covers all eight device codebooks byte for byte. Both false comments are corrected. The same replay measured one thing in the other direction: the new grouped gate builds a 64-row weight, 512 blocks and 16384 draws, and that reaches all 2048 entries. Recorded as a fact rather than as a closure. It is coverage by accident of shape, one edit away from shrinking again, and it says nothing about the seven other tables. ## The poison earns its place It is not redundant with the value comparison. Mutating `MatmulBTQuantKernel` to write nothing leaves the golden AND the independent reconstruction both at the poison value, so `memcmp` passes with ZERO failures and only the poison assertion fires. | mutation | applied | compiled | result | |---|---|---|---| | baseline (unmutated) | n/a | yes | 1 case, 112 assertions, 0 failed, SUCCESS | | M1 grouped golden writes nothing | 2 lines | yes | FAILURE, 110 failed assertions | | M2 grouped golden ignores `eids[p]` | 2 lines | yes | FAILURE, 11 failed assertions | | M3 grouped golden ignores the broadcast row | 2 lines | yes | FAILURE, 11 failed assertions | | M4 `MatmulBTQuantKernel` writes nothing (golden AND reference) | 1 line | yes | FAILURE, exit 1, **memcmp failures 0**, poison failures 55 | Each mutation was restored byte-exact (`cmp -s` against a pre-mutation snapshot), and the tree is clean against `HEAD` afterwards. `applied` is measured against that snapshot rather than against `HEAD`, because the tree already carries the fix. A CPU arm of the same probe runs on every host, because both CUDA cases return early without a device and doctest prints SUCCESS for a gate that asserted nothing. It takes this box from 0 assertions to 112. ## What did NOT run, and why **The CUDA arms of the new gate have not run on a device.** `dgx.casa` was unreachable for the whole of this work (`ssh: connect to host dgx.casa port 22: No route to host`; `ping` 100 % packet loss, retried), this box has neither a CUDA toolkit nor an NVIDIA device (`nvcc not found`, `nvidia-smi` absent, no `/dev/nvidia*`), and there is no second CUDA host. So: - The two new CUDA test cases and the codebook seal are UNVERIFIED at runtime. - The confirmation that removing either new `case` turns the grouped gate red is UNVERIFIED at runtime. It follows statically from the `default:` that throws, and from the poison assertion for the pre-fix shape, but it has not been run. - The `cuda-fat-build` CI job compiles `cuda_quant_dot.cu` with nvcc over ten SM targets, so the production edit gets a compile check here. It configures with `-DVLLM_CPP_BUILD_TESTS=OFF`, so the CUDA-only test block does not. Issue #1029 stays OPEN for the GB10 run rather than being closed by this PR. ## Not done: `-Wswitch` for CUDA The issue proposed it. It would not have caught this defect. `-Wswitch` is silent whenever a `default:` label exists, and a `default:` is exactly what this change adds. The flag that fires is `-Wswitch-enum`, which warns on every enum switch in the tree that omits an enumerator even with a default. That is a tree-wide change with no measurement behind it, on a compile lane this box cannot run. Recorded here and in the spec rather than done. ## Gate - Full CPU build, CI configuration (`-DVLLM_CPP_BUILD_TESTS=ON`, no build type, so asserts are live): exit 0, zero `error:` lines. - Full `ctest`: **100 % tests passed, 0 failed out of 492**, exit 0, 710.36 s. Two did not run and both are pre-existing environment skips (`test_modelopt_mixed_precision_checkpoint`, `test_voxtral_e2e`). - Focused: `test_cuda_quant_dot` 10 cases, 112 assertions, 0 failed. The CUDA cases return early on this host, which is why the CPU arm exists: without it the same binary reports SUCCESS on 0 assertions. - This branch replaces #1034, whose history carried a `git merge` commit with git's default message and therefore no trailers. Rewriting it would have meant a force-push, so the work was cherry-picked onto `origin/main` instead. The resulting tree is byte-identical: `git rev-parse HEAD^{tree}` on both heads is `dbc2bb620d5a1b6da356a25218f1c850ea6fbbb1`, so the `ctest` run above is a run of exactly this tree. - `scripts/agent-preflight.sh`: **All gates green**, including `check-agent-record`, `commit-trailers` and `commit-style`. - New: `src/vt/cuda/cuda_iq_table_seal.h` declares the seal's copy. It is a gate instrument, called only from `tests/vt/test_cuda_quant_dot.cpp`, and it exists because a host translation unit cannot take the address of a `__device__` array. Named here rather than left to be discovered. Refs #1029, #912. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code] --------- Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
W10 and W13 of
.agents/specs/expert-streaming.md, the owed half of #946. Tracked by #912.Why this is not optional polish
cuda_quant_dot.cumaps an unrecognised weight dtype toreturn false, which is a silent CPU fallback. Without these kernels the Qwen3.8-2.4T routed experts, 96.92 % of either checkpoint, decode on the CPU while the model emits CORRECT tokens and looks healthy. No token gate can see that, so it would surface only as "the GPU is mysteriously slow".Hardware result
Run on dgx.casa (GB10, sm_121a, nvcc 13.0.1),
test_cuda_quant_dot: 9 cases, 150032 assertions, 0 failed.nmse_cpu <= 1e-6nmse_ref <= ceilingThe first is the assertion this port lives by, and it passes by eight orders of magnitude.
The one bound that moved, and why
nmse_refforiq1_scame in at 5.23981e-4 against a 5e-4 default, at m=4, k=2048, n=1, where n=1 and m=4 is four dot products. That comparison holds a Q8_K-quantized ACTIVATION against an f32 reference, so it measures activation error, not the kernel: Q8_K carries one scale per 256 elements while these encodings carry a per-32 scale.What identifies it is the CPU test measuring 5.23975e-4 at the same shape. The two agree to six significant figures, so this is the activation term reproducing itself, not anything device-specific. The ceiling is per-CASE, applies only to that comparison, and is set to 2e-3: about 4x the residual and an order of magnitude below the weakest decode defect measured by mutation on the CPU arm (2.7e-2 for a dropped delta term). The CUDA-vs-CPU bound is untouched at 1e-6 and shared by every encoding.
Design note
The device grids deliberately keep the SAME u64 layout as the CPU tables rather than upstream's packed u32
*_grid_gpuvariants: one encoding to validate instead of two, the device dot reads as a literal transcription of the CPU one, and the provenance digests the CPU tests re-derive cover these bytes too.Both dots read
bsums, which no other IQ dot here needs, because IQ1_S and IQ1_XXXS reconstructdl*(grid + delta): the dot separates into an integer grid term plus delta times the activation's per-16 sums, which Q8_K already caches.Evidence
GRID + ivs&GRID[i]andy[i].bsumsvsyb->bsums, the same expressions written for a block loop and for a single block pointer. A desk check is not a hardware result and did not stand in for one.The GPU lock was 3h38m stale with zero compute processes when the device run happened, past the 2h TTL. It was reclaimed for the duration and released after.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]