cyijun2k's picture
Add architecture-faithful GLM-5.3 Flash NVFP4 tiny-random fixture
dc386a5 verified
Raw
History Blame Contribute Delete
6.32 kB
"""Runtime compatibility patch for GLM-5.3 NoPE sparse MLA on SM120/SM121.
The fp8_ds_mla physical cache layout reserves 64 BF16 dimensions for RoPE.
GLM-5.3 uses native NoPE (qk_rope_head_dim=0), so vLLM otherwise forwards an
empty k_pe tensor to concat_and_cache_mla, whose packed-layout kernel requires
pe_dim=64. Pad only the physical cache write; the model and attention kernel
continue to use the architecture-correct logical RoPE dimension of zero.
"""
from __future__ import annotations
import os
import torch
import flashinfer.mla._sparse_mla_sm120 as _sparse_mla_sm120
from vllm.v1.attention.backend import MLAAttentionImpl
from vllm.v1.attention.backends.mla.flashinfer_mla_sparse import (
_get_workspace_buffer,
)
from vllm.v1.attention.backends.mla.flashinfer_mla_sparse_sm120 import (
FlashInferMLASparseSM120Impl,
)
from vllm.v1.attention.backends.mla.sparse_utils import (
triton_convert_req_index_to_global_index,
)
_original_decode_dsv3_2_dispatchable = (
_sparse_mla_sm120._decode_dsv3_2_dispatchable
)
_debug = os.getenv("GLM53_GB10_PATCH_DEBUG") == "1"
_debugged_forward = False
if (64, 2176) not in _sparse_mla_sm120._DECODE_DSV3_2_DISPATCH:
raise RuntimeError(
"This mock requires the GLM-5.3 H=64/top-k=2176 FlashInfer decode "
"specialization; use the companion GB10 adapter image."
)
def _glm53_decode_dsv3_2_dispatchable(
num_tokens: int,
num_heads: int,
topk: int,
d_qk: int,
page_block_size: int,
) -> bool:
# The CLI block size is 256 for kpool storage alignment; vLLM exposes the
# compressed sparse-MLA cache to FlashInfer as physical 64-token pages.
result = _original_decode_dsv3_2_dispatchable(
num_tokens,
num_heads,
topk,
d_qk,
page_block_size,
)
if _debug:
print(
"GLM53_PATCH dispatch "
f"tokens={num_tokens} heads={num_heads} topk={topk} "
f"d_qk={d_qk} physical_page={page_block_size} -> {result}",
flush=True,
)
return result
_sparse_mla_sm120._decode_dsv3_2_dispatchable = (
_glm53_decode_dsv3_2_dispatchable
)
_original_do_kv_cache_update = MLAAttentionImpl.do_kv_cache_update
def _glm53_nope_do_kv_cache_update(
self,
kv_c_normed: torch.Tensor,
k_pe: torch.Tensor,
kv_cache: torch.Tensor,
slot_mapping: torch.Tensor,
kv_cache_dtype: str,
k_scale: torch.Tensor,
) -> None:
if kv_cache_dtype == "fp8_ds_mla" and k_pe.shape[-1] == 0:
k_pe = k_pe.new_zeros((*k_pe.shape[:-1], 64))
return _original_do_kv_cache_update(
self,
kv_c_normed,
k_pe,
kv_cache,
slot_mapping,
kv_cache_dtype,
k_scale,
)
MLAAttentionImpl.do_kv_cache_update = _glm53_nope_do_kv_cache_update
def _glm53_nope_forward_mqa(
self,
q: torch.Tensor | tuple[torch.Tensor, torch.Tensor],
kv_c_and_k_pe_cache: torch.Tensor,
attn_metadata,
layer,
) -> tuple[torch.Tensor, None]:
"""Run logical NoPE through the physical 576-wide GLM_NSA kernel.
SM120's GLM_NSA sparse kernel supports the architecture-required top-k
2048, but its packed cache ABI is 512 latent FP8 values plus 64 BF16 RoPE
values. Appending zero RoPE values preserves the exact NoPE dot product.
"""
global _debugged_forward
if isinstance(q, tuple):
q = torch.cat(q, dim=-1)
if self.qk_rope_head_dim != 0:
raise RuntimeError("GLM-5.3 NoPE patch received a non-NoPE MLA layer")
q = torch.nn.functional.pad(q, (0, 64))
num_actual_toks = q.shape[0]
assert self.topk_indices_buffer is not None
topk_indices = self.topk_indices_buffer[:num_actual_toks]
if attn_metadata.topk_tokens != 2048:
raise RuntimeError(
f"expected architecture index_topk=2048, got {attn_metadata.topk_tokens}"
)
if topk_indices.shape[1] != 2176:
raise RuntimeError(
"expected the GLM kpool/tail/alignment buffer width 2176, got "
f"{topk_indices.shape[1]}"
)
converted = triton_convert_req_index_to_global_index(
attn_metadata.req_id_per_token[:num_actual_toks],
attn_metadata.block_table,
topk_indices,
BLOCK_SIZE=attn_metadata.block_size,
NUM_TOPK_TOKENS=topk_indices.shape[1],
return_valid_counts=True,
)
topk_indices_physical, valid_counts = converted
empty_rows = valid_counts == 0
topk_indices_physical[:, 0] = topk_indices_physical[:, 0].masked_fill(
empty_rows, 0
)
active_lengths = valid_counts.clamp(min=1)
sparse_topk_capacity = topk_indices_physical.shape[1]
if _debug and not _debugged_forward:
print(
"GLM53_PATCH forward "
f"q={tuple(q.shape)} topk={tuple(topk_indices.shape)} "
f"physical_topk={tuple(topk_indices_physical.shape)} "
f"valid_min={int(valid_counts.min())} "
f"valid_max={int(valid_counts.max())} "
f"block_size={attn_metadata.block_size} "
f"cache={tuple(kv_c_and_k_pe_cache.shape)}",
flush=True,
)
_debugged_forward = True
output = q.new_empty(
(num_actual_toks, self.num_heads, self.kv_lora_rank),
dtype=q.dtype,
)
if self._workspace_buffer is None:
self._workspace_buffer = _get_workspace_buffer(q.device)
from vllm.utils.flashinfer import (
flashinfer_trtllm_batch_decode_with_kv_cache_mla,
)
out = flashinfer_trtllm_batch_decode_with_kv_cache_mla(
query=q.unsqueeze(1),
kv_cache=kv_c_and_k_pe_cache.view(torch.uint8).unsqueeze(1),
workspace_buffer=self._workspace_buffer,
qk_nope_head_dim=self.qk_nope_head_dim,
kv_lora_rank=self.kv_lora_rank,
qk_rope_head_dim=64,
block_tables=topk_indices_physical.unsqueeze(1),
seq_lens=active_lengths,
max_seq_len=sparse_topk_capacity,
out=output.unsqueeze(1),
bmm1_scale=self.scale,
bmm2_scale=1.0,
sparse_mla_top_k=sparse_topk_capacity,
kv_scale_format=self.kv_scale_format,
).squeeze(1)
out = out.masked_fill(empty_rows[:, None, None], 0)
return out, None
FlashInferMLASparseSM120Impl.forward_mqa = _glm53_nope_forward_mqa