Instructions to use brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Trellis
How to use brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78 with Trellis:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
Download SERVING_FUSED.md from brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78: direct link, hf CLI and curl.
- Browser
- Download file 5.66 kB
-
https://huggingface.co/brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78/resolve/main/SERVING_FUSED.md
- Command line
-
hf download hf://brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78/SERVING_FUSED.md
-
curl -L -o SERVING_FUSED.md https://huggingface.co/brandonmusic/GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78/resolve/main/SERVING_FUSED.md
Serving GLM-5.2-EXL3-TR3v4-3.5bpw-MTP78 on the fused MoE path
Reference numbers below were measured on 4x RTX PRO 6000 Blackwell (SM120a, 96 GB each, PCIe Gen5, no NVLink), TP4 + DCP4, MTP 3.
Docker image
verdictai/glm52-exl3-sparkinfer:v39-r28-r7fused-broadcast-cu132-sm120a
docker pull verdictai/glm52-exl3-sparkinfer:v39-r28-r7fused-broadcast-cu132-sm120a
Built for sm_120a (Blackwell). It will not run on other architectures without
a rebuild. It already contains every patch in patches/ -- that directory is
only needed if you are building your own image.
Measured
| value | configuration | |
|---|---|---|
| KV cache | 355,328 tokens | max_model_len 262144, util 0.955, batched 2048, seqs 4, nvfp4_ds_mla, bf16 rope, DCP4 |
| Max concurrency | 1.36x | at 262,144 tokens per request |
| KLD vs reference logits | 0.069527 | 5-run gate, nvfp4_ds_mla + outer scales, bf16 rope, fused path |
| Prefill @ 8K | 1,725 tok/s | 8,039-token prompt, unique prefix (prefix cache defeated) |
| Decode | 75.6 tok/s | 256 tokens, ignore_eos, single stream |
Non-fused reference on the identical rig: 497,408 tokens, 1,757 tok/s prefill, 73.5 tok/s decode. The fused path trades KV capacity for decode throughput.
The outer scales file is not optional
nvfp4_mla_outer_scales.json ships in this repo. It must be mounted and pointed
at by VLLM_NVFP4_MLA_SCALES_FILE. Measured on the identical build:
| nvfp4 KV | with outer scales | without |
|---|---|---|
| bf16 rope | 0.069527 | 0.099717 |
| fp8 rope | 0.075542 | 0.114359 |
Omitting it costs ~30% KLD for zero memory and zero throughput benefit. It is a per-layer outer-scale calibration (wikitext-2-raw-v1, 2048 context).
Settings that matter, and why
| setting | value | rationale |
|---|---|---|
VLLM_EXL3_R7_FUSED |
1 |
SparkInfer fused MoE. Requires the FC2-bound fix below. |
VLLM_EXL3_R7_FUSED_LAYERS |
48 |
Loader default. 6 layers carry a third trellis K value and correctly fall back to exl3_moe_r7_fused. |
VLLM_NVFP4_MLA_SCALES_FILE |
(path) | See above. Largest single quality lever. |
KV_FP8_ROPE |
0 |
fp8 rope yields ~16% more KV tokens but measured +8.7% KLD (0.075542 vs 0.069527). Not worth it for citation-sensitive work. |
VLLM_DCP_INDEXER_SHARDS |
4 |
Auto-selection picks 2 under DCP4, holding each DSA index-cache entry on two ranks. 4 is pure placement -- identical arithmetic, +8.5% KV. |
VLLM_EXL3_PREFILL_CAPACITY |
1024 |
Halves the 574 MiB fused prefill arena. 512 and 256 give more KV but cost 17% and 34% of prefill; 1024 is the knee (1,725 vs 1,739 tok/s at the full 2048). |
VLLM_EXL3_PREFILL_BLOCK_M |
64 |
Route block size. |
If you build your own image
The fused path needs patches that are not upstream. They are in patches/ in
this repo; each is idempotent, uses exact anchors and fails closed.
Required for correctness:
patch_sparkinfer_projection_tiers.py— the FC2 dispatch bound. SparkInfer bounded the FC2 (down-projection) tier-local expert index with the FC1 slot count. With per-(expert,projection) trellis bit widths, FC2 holds more experts than FC1 (tier1: 231 down vs 77 gate/up), so most down tiles were rejected and their output silently dropped — 54.14% of down-projection expert slots across the 48 fused layers. Becausezero_fc2_output=Falseandrotation_gateis aliased as the FC2 buffer, the rejected tiles left gate-rotated hidden states in place, which were then rotated, scaled and accumulated: fluent but wrong output, measured at KLD 2.36.
Required for the memory numbers above:
patch_r7_broadcast_rotations.py— requires an r28-or-later SparkInfer base. R7 stores one shared rotation row per layer (r7_shared.gate_up_suh,r7_shared.down_svh, both[6144]). Earlier bases lacked broadcast rows, so the loader expanded each to 256 identical copies. r28's ABI-v6broadcast_suh/broadcast_svhtakes the single row. ~0.5 GiB/rank.patch_r7_ballast_dealias.py— the loader handedpreparea view into the never-read ballast pool, which every frozen tier then retained (192 live aliases per rank), pinning ~576 MiB. Hands it a standalone scalar instead.patch_r7_release_ballast.py— releases the pool once, after all layers load. Must not be done per layer: that refragments the heap and OOMs the load.patch_r7_pad_before_prepare.py— the rotation tables were padded after the prepare loop, butpreparestores the[:1]/[:n]slices it is handed as views, so the unpadded originals stayed pinned alongside the padded copies. ~448 MiB.
Optional (occupancy, no measured end-to-end effect on this rig):
patch_moe_deadscale_2cta.py,patch_moe_stages3_only.py— raise the fused MoE from 8 to 15.4 warps/SM. Kernel-level improvement; end-to-end it measured at parity here.
Apply order matters: the broadcast patch must run after the R7 loader patch.
Verifying your deployment
curl -s localhost:8000/v1/chat/completions -H 'Content-Type: application/json' -d '{
"model": "GLM-5.2-EXL3-TR3v4-3.5bpw",
"messages": [{"role":"user","content":"Explain the holding of Marbury v. Madison."}],
"max_tokens": 1200, "temperature": 0}'
A broken fused path is obvious: hallucinated case law, degenerate verbatim
repetition, or empty completions. A healthy one returns an accurate ~2,000
character answer. Note this model uses a reasoning channel — with max_tokens
below ~300 the content field can come back empty simply because the budget was
consumed by reasoning. That is not a fault.