Follow-up to the earlier MiMo-V2.5-DFlash thread. I got DFlash speculative decoding running together with a 4-bit NVFP4 KV cache in a single vLLM instance on a 2× DGX Spark (GB10) pair — and ported the whole thing to the v0.24.0 release. Full build + mods + recipe:
👉 GitHub - DoctorMasterNewb/vLLM-MiMo-V2.5-DFlash-NVFP4Kv · GitHub
Why this is awkward in stock vLLM
DFlash is a block-diffusion drafter — it fills a block of 8 masked tokens in one forward using non-causal attention. Two consequences:
- The draft’s KV registers into vLLM’s global paged allocator, which insists on one physical page size across layers. A bf16 draft page won’t unify with a 4-bit NVFP4 target page — the wall behind upstream #41559 (“DFlash fundamentally incompatible with all KV-cache quantization”).
- So
--kv-cache-dtype nvfp4+ DFlash blows up.
The way around it: run the DFlash drafter as a vLLM custom_class proposer — a standalone module whose KV never enters the global allocator. The NVFP4 target keeps its full pool; the drafter runs decoupled beside it (attention forward reimplemented to match qwen3_dflash: partial rotary dim-64 @ θ=5e6, attention sinks, value-scale, mask-embedding sidecar, dense per-request context buffer). Decoupled → works with any target KV dtype.
What v0.24.0 changed (the useful part for everyone)
v0.24.0 upstreams most of the old MiMo mod stack: MiMoV2Omni model + config/arch registry, DiffKV backend (#41797), MXFP8 kernels, native DFlash / custom_class. What still needs a startup mod for the lukealonso/MiMo-V2.5-NVFP4 export:
| mod | why |
|---|---|
fix-mimo-config |
transformers still doesn’t know model_type=mimo_v2 |
fix-mimo-qkv-mxfp8 |
fused qkv is `[Q_all |
fix-mimo-…-merger |
vision merger mlp bias=True (checkpoint ships the bias → else KeyError) |
fix-mimo-…-packed |
packed_modules_mapping on the Omni wrapper (else MXFP8 read as bf16 → garbage) |
nvfp4-kv-diffkv |
NVFP4 KV for DiffKV; backend drops in, the triton kernel needed one line (v0.24.0’s compute_kv_seq_mask gained a seq_len arg) |
dflash-custom-proposer |
the decoupled proposer — applies unchanged from the old build |
Maintainer takeaway: on a version bump, most MiMo mods upstream or apply unchanged. The churn is checkpoint-specific quant loaders + triton arg drift.
Numbers (2× GB10, TP=2, 0.24.1.dev0)
Structured (temp 0): ~2.37 acc/block — JSON 3.33 (45 tok/s), code 2.81 (38), math 2.41 (32).
Deep context (llama-benchy, prose):
| depth | tg tok/s (peak) | acc/block |
|---|---|---|
| short | 21.7 (31) | 1.13 |
| 100k | 7.85 (12) | 1.02 |
| 200k | 5.03 (10.5) | 1.17 |
- NVFP4 KV pool ≈ 1.35M tokens (~2.8× fp8). Needle recall at 26k; coherent throughout.
- Prose acceptance is lower — DFlash collapses on unpredictable prose, measure your own workload.
- Honest tradeoff vs fp8: fp8-KV DiffKV builds decode deep context faster (~100k@12 / 200k@10) since fp8 attention is cheaper than 4-bit; NVFP4 buys the bigger pool at some decode cost. Both now coexist with DFlash via the decoupled proposer, so pick per use case.
Repo
GitHub - DoctorMasterNewb/vLLM-MiMo-V2.5-DFlash-NVFP4Kv · GitHub — Dockerfile (v0.24.0 GB10, sm_121a/CUDA 13.2), all seven mods, serve recipe, README with the full breakdown. Lineage based on eugr/spark-vllm-docker; fp8-KV DiffKV approach cross-referenced from this forum — thanks all. Experimental; validate before leaning on it.