fix: support FA4 backend for GLM4.7-flash (#33436)

This commit is contained in:
Yuzhen Zhou
2026-08-09 20:05:38 +08:00
committed by GitHub
parent 71043b9dbb
commit 7120f3ee13
2 changed files with 260 additions and 18 deletions
@@ -4,6 +4,7 @@ import os
from typing import Callable, Optional, Tuple, Union
import torch
import torch.nn.functional as F
from sglang.kernel_api_logging import debug_kernel_api
@@ -31,6 +32,60 @@ def _maybe_contiguous(x: Optional[torch.Tensor]) -> Optional[torch.Tensor]:
return x.contiguous() if x is not None and x.stride(-1) != 1 else x
def _pad_mla_q_heads(q, qv, v, pack_gqa):
if qv is None or pack_gqa is False:
return q, qv, None
num_heads = qv.shape[-2]
num_kv_heads = v.shape[-2]
qhead_per_kvhead = num_heads // num_kv_heads
if 128 % qhead_per_kvhead == 0 or qhead_per_kvhead % 128 == 0:
return q, qv, None
qhead_per_kvhead_padded = 1 << (qhead_per_kvhead - 1).bit_length()
def pad(x):
if x is None:
return None
prefix = x.shape[:-2]
x = x.reshape(*prefix, num_kv_heads, qhead_per_kvhead, x.shape[-1])
x = F.pad(x, (0, 0, 0, qhead_per_kvhead_padded - qhead_per_kvhead))
return x.reshape(*prefix, num_kv_heads * qhead_per_kvhead_padded, x.shape[-1])
# Pad each KV group to a valid ratio so MLA stays on the packed kernel.
return (
pad(q),
pad(qv),
(
num_kv_heads,
qhead_per_kvhead,
qhead_per_kvhead_padded,
),
)
def _unpad_mla_result(result, head_padding):
if head_padding is None:
return result
num_kv_heads, qhead_per_kvhead, qhead_per_kvhead_padded = head_padding
out, lse = result
prefix = out.shape[:-2]
out = out.reshape(*prefix, num_kv_heads, qhead_per_kvhead_padded, out.shape[-1])[
..., :qhead_per_kvhead, :
]
out = out.reshape(
*prefix, num_kv_heads * qhead_per_kvhead, out.shape[-1]
).contiguous()
if lse is not None:
prefix = lse.shape[:-1]
lse = lse.reshape(*prefix, num_kv_heads, qhead_per_kvhead_padded)[
..., :qhead_per_kvhead
]
lse = lse.reshape(*prefix, num_kv_heads * qhead_per_kvhead).contiguous()
return out, lse
@debug_kernel_api
def flash_attn_varlen_func(
q: torch.Tensor,
@@ -81,6 +136,15 @@ def flash_attn_varlen_func(
) from _flash_attn_import_error
q, k, v, qv = [_maybe_contiguous(t) for t in (q, k, v, qv)]
if qv is None and q.shape[-1] == 256 and k.shape[-1] == 256 and v.shape[-1] == 256:
# The vendored hd256 kernel assumes dense Q/K/V strides.
# TODO: Remove this workaround after the FA4 in current environment includes
# https://github.com/Dao-AILab/flash-attention/pull/2670 (flash-attn-4 >= 4.0.0b20).
q, k, v = [t.contiguous() for t in (q, k, v)]
q, qv, mla_head_padding = _pad_mla_q_heads(q, qv, v, pack_gqa)
if qv is not None and num_splits < 1:
# FA4 MLA does not implement split-KV; auto mode must use one split.
num_splits = 1
cu_seqlens_q, cu_seqlens_k = [
_maybe_contiguous(t) for t in (cu_seqlens_q, cu_seqlens_k)
]
@@ -145,6 +209,7 @@ def flash_attn_varlen_func(
**descale_kwargs,
**rel_bias_kwargs,
)
result = _unpad_mla_result(result, mla_head_padding)
if return_softmax_lse:
return result