GLM-5.3-Flash¶
Z.ai's 320B-parameter mixture-of-experts model (18B active) with Kimi Delta Attention, NoPE MLA, manifold
hyper-connections and image input, served in its published block FP8 at 256K context on one 8-card Gaudi 2 node.
vllm-gaudi has no path for it, so it runs from the GLM-5.3-Flash port, built on top of
the stock v0.26.0 image.
| Model | zai-org/GLM-5.3-Flash (MIT) |
| Hardware | 8x Gaudi 2 (TP 8, expert parallel), one node |
| Image | port image built from vllm-gaudi v0.26.0 (b059f9c), vLLM 568afb3a, Gaudi SW 1.24.1 |
| Precision | block FP8 weights as published (328 GB), BF16 activations |
| Context | 262,144 served (KV pool 819,200 tokens) |
| Concurrency | --max-num-seqs 32 |
| Input | text and up to 4 images per prompt (no video) |
| Parsers | tools and reasoning glm47 |
| Recipe | recipes/zai-org/GLM-5.3-Flash/recipe.yaml |
Build the image¶
From a checkout of this repository, on top of a stock v0.26.0 image built as in
Getting started:
docker build -f ports/glm-5-3-flash/Dockerfile --build-arg BASE=<your-vllm-gaudi-v0.26.0-image> -t <your-glm-5-3-flash-image> .
install.sh checks the vllm-gaudi and vLLM commits, applies the port's four source patches, builds the mHC Sinkhorn
TPC kernel, installs the glm5_next_hpu plugin and checks all of it again. It stops on any other base. The image
carries this repository at /opt/gaudi-cookbook.
Launch¶
Inside the port image, with COOKBOOK=/opt/gaudi-cookbook:
export PT_HPU_LAZY_MODE=1
export VLLM_SKIP_WARMUP=true
export VLLM_ENGINE_ITERATION_TIMEOUT_S=7200
export VLLM_RPC_TIMEOUT=7200000
export VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=1800
export GLM5_NEXT_VISION=1
export GLM5_NEXT_IMAGE_MAX_TOKENS=2048
export GLM5_NEXT_KDA_SCAN=1
export GLM5_NEXT_DECODE_LAYER_GROUP=45
export GLM5_NEXT_MHC_SINKHORN=kernel
# Fixes: the v0.26.0 profile plus this recipe's opt-in fixes, in the pack's order.
# Stops with a non-zero exit code if the image does not match.
python3 "$COOKBOOK/fixes/apply_all.py" --profile v0.26.0 glm47-streaming-text-markers || exit 1
vllm serve zai-org/GLM-5.3-Flash \
--tensor-parallel-size 8 \
--enable-expert-parallel \
--dtype bfloat16 \
--max-model-len 262144 \
--max-num-seqs 32 \
--num-gpu-blocks-override 1280 \
--max-num-batched-tokens 8192 \
--max-cudagraph-capture-size 2048 \
--enable-chunked-prefill \
--gpu-memory-utilization 0.82 \
--no-enable-prefix-caching \
--trust-remote-code \
--reasoning-parser glm47 \
--tool-call-parser glm47 \
--enable-auto-tool-choice \
--default-chat-template-kwargs '{"reasoning_effort": "high"}' \
--chat-template "$COOKBOOK/ports/glm-5-3-flash/serving/chat_template.jinja" \
--limit-mm-per-prompt '{"image": 4}'
--enable-expert-parallelputs 36 of the 288 routed experts on each card instead of slicing every expert across TP 8; with themoe-explicit-overloadfix it is the faster layout at one stream.--num-gpu-blocks-override 1280pins the KV pool. vLLM derives a 640-token block for this hybrid model (the attention page is raised to the KDA state page), so 1,280 blocks hold 819,200 tokens; its autosizing over-commits by about one MLA layer and fails.--no-enable-prefix-cachingis required: the KDA layer implements only the non-caching state contract and refuses to start with prefix caching.--max-cudagraph-capture-size 2048andGLM5_NEXT_IMAGE_MAX_TOKENS=2048bound the number of prompt-length graphs that images create (an image costs compile time through the prompt length it adds, not through its geometry).--chat-templateis the checkpoint's template with image placeholders (one change, noted in the file's header).reasoning_effortdefaults tohighhere; requests can setlowinchat_template_kwargs, and the template treats any value other thanloworhighasmax.VLLM_EXECUTE_MODEL_TIMEOUT_SECONDS=1800: with warmup skipped, a long prefill compiles on first touch and can take longer than the 300 s default, which stops the engine.- The five
GLM5_NEXT_*switches are explained in the port README.VISIONbuilds the vision tower;KDA_SCAN,DECODE_LAYER_GROUPandMHC_SINKHORNare the prefill and decode speed-ups; with them off the port runs its reference path.
Buckets¶
No bucket file and no bucket variables: the image's default bucketing.
Required fixes¶
The v0.26.0 profile, applied at container start on top of the port's patches. The fixes this configuration depends
on are listed in the recipe: the sampling and structured-output row fixes (sampling-rows-refresh,
structured-output-rows, bad-words-index, logitsprocs-rows), the async-scheduling fixes (async-inputs,
async-discard-counter, resumed-prefill-logits), hybrid-decode-pad (decode padding on this hybrid model), the
logprobs fixes (gather-logprobs-int32, prompt-logprobs, logprobs-decode-guard), seeded-sampling,
moe-explicit-overload (the FP8 MoE op without a host sync per layer), mm-merge-idxcopy (image embedding merge),
rank-exception-fatal and xgrammar-generation-eos-stop (tool_choice: required stops at GLM's end-of-turn
tokens). The opt-in glm47-streaming-text-markers makes the streaming tool parser catch calls the model writes as
plain-text markers. The rest of the profile applies too; the golden gate below shows it does not change the model's
output (GLM-5.3-Flash routes with sigmoid scores and one group, which laguna-moe-sigmoid-routing leaves unchanged).
Verified¶
Run as written on one node of eight Gaudi 2 cards: a stock v0.26.0 image with this repository copied to
/opt/gaudi-cookbook and install.sh run as the Dockerfile runs it (commits checked, four patches applied, kernel
gates passed), then the launch block above. Every gate is ports/glm-5-3-flash/tests/hpu/run_gates.sh:
| Gate | Result |
|---|---|
| golden: 12 greedy prompts, token ids against the reference outputs | 12/12 identical |
| needles: three codes at 10, 50 and 90 % depth | 3/3 at 16K, 63K, 127K and 239K tokens |
| tools: single, parallel and no call, streamed and not; multi-turn; a call after a 32K-token document | 8/8 |
| images: three solid colours, quadrants, counting, text before and after; 5 images rejected with 400 | 8/8 |
| decode: single stream at 2K and 128K, floors 15 and 10 tokens/s | pass (numbers below) |
| prefill: warm TTFT at 16K, 64K and 128K, 128K within 120 s | pass (numbers below) |
| sinkhorn: the TPC kernel against vLLM's stock loop, 1 to 1,024 rows, one card, no server | bit-identical |
Off means stock. The same server with GLM5_NEXT_KDA_SCAN=0, GLM5_NEXT_DECODE_LAYER_GROUP=1 and
GLM5_NEXT_MHC_SINKHORN=torch (the reference path) passed the same six server gates, golden 12/12 identical to the
same reference. The CPU tests (pytest ports/glm-5-3-flash/tests, 34 tests including the patches against the
release sources and the config against the checkpoint's config.json) pass in the same image.
Performance¶
Single stream, temperature 0, unique prompts, every shape warmed once before the measured request. Decode is 512
tokens with ignore_eos, timed from the first to the last streamed token. Same node and image; the server was
relaunched for each column.
| Test | Switches on (as launched above) | Switches off (reference path) |
|---|---|---|
| Decode, 2K prompt | 43.5 tokens/s | 30.8 tokens/s |
| Decode, 128K prompt | 38.6 tokens/s | 28.7 tokens/s |
| TTFT, 16K prompt | 2.6 s | 5.4 s |
| TTFT, 64K prompt | 10.3 s | 22.3 s |
| TTFT, 128K prompt | 23.8 s | 42.7 s |
An earlier server with the switches on, same image and command, measured 38.4 and 36.2 tokens/s for the two decode rows and the same TTFT within 0.3 s: treat single-stream decode as plus or minus about 10 %. Batched decode and longer contexts are in the write-up (different builds, labelled there).
Known issues¶
- Cold start is dominated by reading 328 GB of weights: about 13 minutes from network storage (62 shards at about 12.5 s each), then a few minutes of engine start.
- New prompt lengths compile graphs on first use (warmup is skipped). The first request at a new length can take several times as long as the warm one; the gates warm every shape once before measuring.
- The 11 DSA layers run as dense MLA with the indexer skipped. Up to 2,048 prompt tokens this is the same function; beyond it is an approximation. Needles pass to the served window (above), and to 512K in the write-up.
- Video is not served, and the MTP draft head is not loaded (no speculative decoding).
What not to do¶
- Do not turn on prefix caching: the model refuses to start, because the KDA layer does not store per-block states.
- Do not drop
--num-gpu-blocks-override: KV autosizing over-commits on this hybrid model and fails. - Do not raise
GLM5_NEXT_IMAGE_MAX_TOKENSor--limit-mm-per-promptwithout re-running the image gate and a long prompt with images: each new prompt length adds graphs.