Skip to content

GLM-5.2-FP8 with DSA enablement - #1760

Open
jkaniecki wants to merge 8 commits into
vllm-project:mainfrom
jkaniecki:glm_52
Open

GLM-5.2-FP8 with DSA enablement#1760
jkaniecki wants to merge 8 commits into
vllm-project:mainfrom
jkaniecki:glm_52

Conversation

@jkaniecki

@jkaniecki jkaniecki commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Enable GLM-5.2 with DSA (DeepSeek Sparse Attention) on HPU

What changed:

hpu_sparse_attn_indexer.py (new): per-request Q·K BF16 scoring + torch.topk to select top-2048 KV cache slots per decode step
hpu_attn.py: forward_mqa_sparse — gathers top-K latent KV, decompresses, runs masked MLA decode attention
oot_mla.py: dispatches to forward_mqa_sparse when use_sparse=True
deepseek_v2.py: BF16 indexer cache, HPU-safe Indexer.forward, SparseAttnIndexer dispatch
platform.py / hpu_model_runner.py: DSA routing + indexer cache layer index fix
Validated on 8× Gaudi3 (GLM-5.2-FP8, TP=8):

GSM8K 5-shot (1319 samples): 93.6% exact match
AIME 2026 - 100% - on pair with model card's target (99,2 % mean for multiple reruns)

Signed-off-by: Jan Kaniecki <jkaniecki@habana.ai>
Copilot AI lite review requested due to automatic review settings August 27, 2026 14:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Enable GLM-5.2 FP8 with DeepSeek Sparse Attention (DSA) execution on Intel Gaudi (HPU) by wiring sparse indexer execution and adding a sparse decode attention path in the HPU MLA backend.

Changes:

  • Add an HPU SparseAttnIndexer implementation and route upstream SparseAttnIndexer.forward_native to it for HPU.
  • Introduce an HPU MLA sparse decode path (forward_mqa_sparse) and dispatch to it from the OOT MLA wrapper when use_sparse=True.
  • Extend KV-cache spec discovery / platform routing to recognize attention modules that provide their own KV-cache spec and to log sparse-attention selection.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
vllm_gaudi/v1/worker/hpu_model_runner.py Adds KV-cache spec extraction for attention layers implementing AttentionLayerBase.get_kv_cache_spec.
vllm_gaudi/platform.py Updates sparse-attention backend selection logging and adds a runner KV-cache multi-layer hook.
vllm_gaudi/ops/hpu_sparse_attn_indexer.py New HPU indexer implementation used by DSA to produce top-k cache slot indices.
vllm_gaudi/models/deepseek_v2.py Monkey-patches DeepSeek indexer/cache paths to use BF16 and dispatch SparseAttnIndexer to the HPU implementation.
vllm_gaudi/attention/oot_mla.py Routes decode to forward_mqa_sparse when sparse mode is active and passes the top-k index buffer through.
vllm_gaudi/attention/backends/hpu_attn.py Adds forward_mqa_sparse implementation that gathers/decompresses top-k KV and runs sparse decode attention.
Suppressed comments (2)

vllm_gaudi/ops/hpu_sparse_attn_indexer.py:9

  • The docstring claims "BF16 matmul + torch.topk", but the implementation currently does not do Q·K scoring or torch.topk, and hidden_states/weights are unused. This is misleading for maintainers and reviewers.
def forward_hpu(self, hidden_states, q, k, weights):
    """HPU SparseAttnIndexer: BF16 matmul + torch.topk."""
    forward_context = get_forward_context()
    attn_metadata = forward_context.attn_metadata

vllm_gaudi/ops/hpu_sparse_attn_indexer.py:40

  • Decode-path slot selection uses the batch-wide attn_metadata.block_list and then expands the same all_slots to every sequence in the batch. In this codebase block_list is a flattened list across requests and block_groups indicates which request each block belongs to (see hpu_model_runner.py:2446-2451). As written, this can mix KV slots between different requests, which is both a correctness issue and a potential cross-request data exposure.
    # Decode: use first-N physical slots from the request's context blocks.
    # Full top-K logit scoring deferred to kernel optimization phase.
    batch_size = q.shape[0]
    block_list = attn_metadata.block_list
    pos_range = torch.arange(block_size, device=block_list.device)
    all_slots = (block_list.unsqueeze(1) * block_size + pos_range.unsqueeze(0)).reshape(-1)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread vllm_gaudi/ops/hpu_sparse_attn_indexer.py
Comment thread vllm_gaudi/attention/backends/hpu_attn.py
Comment thread vllm_gaudi/platform.py
Comment thread vllm_gaudi/models/deepseek_v2.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Jan Kaniecki <jkaniecki@habana.ai>
Replace first-slot decode selection with per-request BF16 QK scoring and top-K selection. Mask repeated top-K entries for short contexts in sparse MLA decode.
Issue vllm-project#1 (Copilot review): _fill_sequential() was only initializing topk
columns based on batch size, leaving remaining columns stale/uninitialized.
When gather uses the full buffer width (topk_tokens), stale indices can
cause out-of-range KV cache access. Fix: fill entire buffer width.

Issue vllm-project#2 (Copilot review): masked_fill(-inf) followed by softmax can
produce NaN when valid_topk == 0 (entire row masked). Fix: use
torch.finfo(dtype).min for finite masking and explicitly zero rows
where entire sequence is masked.
jkaniecki added a commit to jkaniecki/vllm-gaudi that referenced this pull request Sep 7, 2026
Enable GLM-5.2 with DSA (DeepSeek Sparse Attention) on HPU.

What changed:
- hpu_sparse_attn_indexer.py (new): per-request Q.K BF16 scoring +
  torch.topk to select top-2048 KV cache slots per decode step. Rewritten
  to derive per-request valid slots via block_groups/block_usage masking
  instead of a fragile block_offset walk that assumed block_list was an
  unpadded per-request concatenation (breaks under contiguous PA, which
  scatters/reorders blocks by physical block id). Padding now uses the
  upstream -1 sentinel convention instead of repeating the last valid
  slot or leaving stale buffer contents.
- hpu_attn.py: forward_mqa_sparse - gathers top-K latent KV, decompresses,
  runs masked MLA decode attention. Masking is now driven directly by the
  -1 sentinel in topk_indices instead of seq_lens_tensor/context_lens_tensor,
  which are always None on HPU decode for models without mamba-like layers
  (the previous fallback silently handed every request in the batch the
  same first topk_tokens slots). Uses torch.finfo(dtype).min instead of
  -inf for masking to avoid NaN on fully-padded rows, and zeroes fully
  padded rows' output with a dtype- and shape-correct multiply (previously
  promoted bf16 attn to fp32 before a bf16 matmul, and used a 3D mask that
  only broadcast correctly by coincidence when num_heads == batch_size).
  HPUMLAImpl.__init__ now stores topk_indices_buffer (previously swallowed
  by **kwargs and never read) and sets is_sparse per-instance, which is
  what actually makes forward_mqa_sparse reachable.
- oot_mla.py: dispatches to forward_mqa_sparse when use_sparse=True,
  reading topk_indices_buffer from self.impl (where HPUMLAImpl now stores
  it) instead of the wrapper module (where it was never set).
- deepseek_v2.py: BF16 indexer cache, HPU-safe Indexer.forward,
  SparseAttnIndexer dispatch.
- platform.py / hpu_model_runner.py: DSA routing + indexer cache layer
  index fix. Ports the get_kv_cache_spec AttentionLayerBase branch from
  the main-branch sibling (vllm-project#1760), which this backport was missing -
  without it DeepseekV32IndexerCache never gets a KV cache allocation, so
  kv_cache.numel() is always 0 and the indexer never runs.

Addresses review feedback from Pawel Olejniczak and Copilot on PR vllm-project#1777:
- Sparse decode path is now actually reachable (three independent gates
  previously routed every DSA decode back to dense MLA).
- Fixed NaN-producing masked_fill(-inf) on fully-masked rows.
- Fixed uninitialized/stale topk_indices_buffer columns.
- Fixed bf16->fp32 dtype promotion before a bf16 matmul.
- Removed unused _orig_forward_native and an orphaned comment.
- Added missing -> None annotation on check_runner_kv_caches_multi_layer.
- Applied yapf 0.43.0 (column_limit=120) formatting; ruff 0.11.7 clean.

Still open (not addressed here, needs checkpoint inspection on the pod):
whether the validated GLM-5.2-FP8 checkpoint still ships indexers_proj
weights now that the load_weights filter for them has been dropped.

Validated on 8x Gaudi3 (GLM-5.2-FP8, TP=8):
GSM8K 5-shot (1319 samples): 93.6% exact match

Signed-off-by: Jan Kaniecki <jkaniecki@habana.ai>
…ejniczak)

- get_kv_cache_spec (already present): DSA indexer cache gets a spec so
  kv_cache.numel() != 0 and the sparse path is actually reachable
- hpu_sparse_attn_indexer.py: derive per-request valid cache slots from
  block_groups/block_usage masking instead of an always-None
  seq_lens_tensor/context_lens_tensor fallback and a block_offset walk
  that assumed an unpadded per-request block_list layout; adopt
  upstream's -1 sentinel for padding instead of repeating a valid slot
- forward_mqa_sparse: mask via the -1 sentinel directly instead of the
  unreliable seq_lens derivation; use finfo(dtype).min instead of -inf
  to avoid NaN on fully-masked rows; fix a bf16->fp32 dtype promotion
  bug in the empty-row zero-mask multiply
- HPUMLAImpl: store topk_indices_buffer (previously swallowed by
  **kwargs) and set is_sparse per-instance so forward_mqa_sparse is
  actually reachable
- oot_mla.py: read topk_indices_buffer from self.impl (where it is
  actually stored) instead of the wrapper; drop an orphaned comment
- deepseek_v2.py: drop an unused _orig_forward_native assignment
- platform.py: add -> None annotation to check_runner_kv_caches_multi_layer

Signed-off-by: Jan Kaniecki <jkaniecki@habana.ai>
…g for GLM DSA

Signed-off-by: Jan Kaniecki <jkaniecki@habana.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants