Repository navigation
feat(gguf): serve qwen3vl and qwen3vlmoe gguf checkpoints text only - #807
Conversation
57417ec to
66bc73d
Compare
|
@jason-fxz: llama.cpp has the same gap (its converter never writes |
|
Align with HF, so keep the NFC change in this PR. The model is trained on NFC-normalized text, and tokenizer.json is the reference. llama.cpp skips NFC in its BPE path, but not on purpose: since ggml-org/llama.cpp#30540, gguf-py writes LGTM on the rest. Checked on real files: unsloth Qwen3-VL-8B BF16 and 30B-A3B BF16 are bit-exact against the HF safetensors (8B: all 291 tensors; 30B: 339 dense tensors plus experts in layers 0/24/47), and the parsed config matches HF. Served end to end next to the HF checkpoints (8B/30B, BF16 and Qwen's Q4_K_M): prompt token counts are identical on every request, including an NFC-sensitive prompt and an 8k-token recall, and the answers match. |
Upstream up to 507a8bf (FlashML-org#641, FlashML-org#811 cache flush, FlashML-org#801, FlashML-org#807, FlashML-org#804, FlashML-org#665, FlashML-org#803, FlashML-org#774). Conflicts resolved: - gguf/config.py: keep our rope --hf-overrides next to upstream's mmproj hf_fields - scheduler/cache.py: take upstream's _evict() helper, keep freeing the host bank slots - scheduler/scheduler.py: block/idle checks wait for both the kv ladder and a pending flush - glm_moe_dsa, minimax_m2 moe.py: keep our fp32-bias comments - api_server.py: import Query and Headers - tests/moe/test_fused_moe.py: keep our moe_align tests and upstream's sigmoid top-k test - tests/scheduler/test_cache_rebuild.py: the flush test's busy marker is a 1-token batch, next's overlap loop reads last_data[1] for the DFlash drain Assisted-by: Claude Opus 5.5
Serves the text trunks of Qwen3-VL (
qwen3vl, dense) and Qwen3-VL-MoE (qwen3vlmoe)GGUFs: a
qwen3_vl/gguf.pyadapter through the family's ownparse_config, registryrows, arch-map and AOT alias entries. Text only, TP 1; vision/mmproj stays out.
Text rope is plain NeoX: llama.cpp's IMROPE keeps full-spectrum theta (ggml.h), so
equal (t, h, w) text positions reduce to standard rope and q/k carry no permute.
Experts stack as
ffn_*_expsbanks likeqwen3moe. Part of #671.Also fixes a latent GGUF-tokenizer bug on main for the whole Qwen lineage
(
preqwen2/qwen35, the families of #707 and #701): every Qwen HFtokenizer.json applies NFC (Unicode Normalization Form C) before the byte-level
BPE, but the GGUF carries no normalizer, so decomposed text encoded differently
from HF.
tok_compare (vs the model's own tokenizer.json)
Before the NFC fix these were 1/248/114 (Qwen3-VL-4B) and 1/0/0 (Qwen3.6); the
NFC-sensitive strings (combining accents, CJK compat ideographs) mismatch HF without
the normalizer on both lineages.
gguf_check sweep
fetch_headers
--search Qwen3-VL(19,823 shards), check.py over the stubs:519/565 files of served architectures pass.
token_embd, no norm/headNot covered
qwen2vlGGUFs (4 files in the sweep)qwen3vlbut need the audio towertoken_embd, TQ1_0/TQ2_0, MTP-only draftsReal-file serving
RTX 5090 32 GB (driver 580.95.05), Ryzen 9 7950X3D, 187 GB RAM, torch 2.14.1+cu130,
triton 3.8.0, transformers 5.18.0, python 3.10.19.
Both answer coherently; thinking parses into reasoning_content with the auto
qwen3reasoning parser. The log's instantaneous "input throughput" swings with its logging
window, so prefill above is from request wall time and per-chunk log lines.
Tests
No per-family GGUF test file, per the #721 review direction; the live-registry
tests/models/test_models_registry.pycovers the new rows and passes.