I spent a day getting the RadixArk NVFP4 quant of Qwen3.8-Flash-Next serving TP=2 across two DGX Sparks, and measured it against the FP8 build on the same rig. Sharing the recipe and the numbers, since the model card only documents an SGLang path and I could not find a vLLM one.
The checkpoint does not load as published. vLLM dies during weight loading with:
ValueError: There is no module or parameter named 'ngram_embedding.weight_scale'
in Qwen3_8FlashNextNGramEmbedding. The available parameters belonging to
ngram_embedding (VocabParallelEmbedding) are: {'ngram_embedding.weight'}
It is mixed precision: NVFP4 routed experts, but the PLE embedding tables are still FP8, 128 shards of F8_E4M3 [2500012, 160] plus one bf16 scalar scale. The quant config excludes the ple modules from NVFP4, so vLLM builds a plain VocabParallelEmbedding with no weight_scale slot while the bytes on disk are still FP8. The FP8 build has the identical 128 shard plus scale layout and loads fine, which is what shows it is the config path and not the tensors.
The gate is one line in models/qwen3_8_flash_next/nvidia/ple_layer.py:
def _get_ple_embedding_quant_method(quant_config, prefix):
if not isinstance(quant_config, Fp8Config):
return None
...
return Qwen3_8FlashNextPLEFp8EmbeddingMethod()
Widening it so a ModelOpt config also returns Qwen3_8FlashNextPLEFp8EmbeddingMethod is enough, and it is safe because that class takes no constructor arguments and registers its own fp8 weight and bf16 scale. I bind mount the patched file over the image rather than baking it in. Do not work around it by deleting the stray scale, the shards really are F8_E4M3 and you would get silent corruption instead of an error. modelopt_mixed does not help either, it fails the same isinstance check.
My config:
vllm serve /model \
--served-model-name qwen3.8-flash-next-nvfp4 \
--tensor-parallel-size 2 --enable-expert-parallel \
--gpu-memory-utilization 0.85 \
--max-model-len 262144 \
--max-num-seqs 64 \
--kv-cache-dtype bfloat16 \
--enforce-eager \
--reasoning-parser qwen3 \
--enable-auto-tool-choice --tool-call-parser qwen3_xml \
--distributed-executor-backend mp \
--nnodes 2 --node-rank 0 --master-addr <fabric ip> --master-port 25400
Environment that matters:
VLLM_QWEN38FN_PLE_FP8=1
NCCL_MIN_NCHANNELS=4
NCCL_MAX_NCHANNELS=4
NCCL_SOCKET_IFNAME=<your CX7 interface>
enforce-eager is required, the full cudagraph modes wedge these boxes. bfloat16 KV is required, the QSA kernels declare supported_kv_cache_dtypes = [“auto”,“bfloat16”] and raise otherwise. The default NCCL channel count of 64 hangs channel init here. Also launch the container with --entrypoint bash, the image ENTRYPOINT is already vllm serve so a bare -c is eaten as --compilation-config.
Both ranks load at 64.06 GiB per node against 88.07 for FP8, and the engine reports:
GPU KV cache size: 2,228,932 tokens, Maximum concurrency for 262,144 tokens per request: 8.50x
FP8 on identical settings gives 607,890 tokens and 2.32x.
Concurrency ladder, 512 output tokens, both lanes at max-num-seqs 64, zero failures either side:
conc FP8 agg NVFP4 agg delta
1 23.02 29.61 +28.6%
2 42.08 55.49 +31.9%
4 61.90 92.42 +49.3%
8 94.28 143.53 +52.2%
16 145.84 212.09 +45.4%
32 152.66 231.30 +51.5%
64 176.75 257.59 +45.7%
Needle in a haystack, 5 needles:
context FP8 ttft NVFP4 ttft FP8 dec NVFP4 dec needles
4,096 3.6 s 3.1 s 37.24 50.95 5/5 both
32,768 15.5 s 12.1 s 37.20 41.51 5/5 both
131,072 63.4 s 50.6 s 43.54 42.74 5/5 both
200,000 95.7 s 77.3 s 38.15 49.64 5/5 both
245,000 118.8 s 96.9 s 29.78 49.40 5/5 both
Both stop around 248K in practice. A 258K target fails with HTTP 400 because the rendered prompt reaches 261,583 tokens and leaves no room to generate inside the 262,144 window.
On agentic quality I ran a 40 instance SWE-bench Pro subset against each, same harness and same prompts: 36 of 40 for both. NVFP4 finished in 2h30m against 3h21m, and they fail different instances, with 2 in common.
One thing that surprised me: the KV difference does not buy longer context, both cap at max-model-len. It buys concurrency at long context, 8.50 simultaneous full length requests against 2.32.
Thanks to @eugr, whose container work most of us are standing on, and to @tonyd615 and @RandomLlama whose GB10 notes saved me time on the NCCL and cudagraph side.