Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

379 lines
14 KiB
Python

import logging
import math
import torch
from sglang.srt.layers.attention.hybrid_linear_attn_backend import MambaAttnBackendBase
from sglang.srt.layers.attention.linear.lightning_attn import (
BailingLinearKernel,
linear_decode_forward_triton,
)
from sglang.srt.layers.attention.linear.linear_metadata import (
BailingLinearMetadata,
)
from sglang.srt.layers.attention.linear.seg_la import SegLaMeta, seg_la_fwd
from sglang.srt.layers.radix_attention import RadixAttention
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
from sglang.srt.model_executor.model_runner import ModelRunner
from sglang.srt.runtime_context import get_parallel
logger = logging.getLogger(__name__)
class LightningAttentionBackend(MambaAttnBackendBase):
"""
Note about the init:
- If no spec decoding
- FlashAttentionBackend will be init once when the server starts.
- If spec decoding
- FlashAttentionBackend will be init once for the target worker
- FlashAttentionMultiStepBackend will be once for the draft worker
- It will spawn num_steps FlashAttentionBackend for the draft worker
Note about CUDA Graph:
- We only support CUDA Graph for Decode (Normal Decode and Draft Decode) and Target Verify.
- We don't support CUDA Graph for Extend and Draft Extend.
- When server init, init_cuda_graph_state will be called first and then init_cuda_graph_capture will be called.
- For each forward batch, init_replay_cuda_graph will be called first and then replay the graph.
"""
def __init__(self, model_runner: ModelRunner):
super().__init__(model_runner)
# seg_la processes draft tokens as a chain -- it has no parent-indices
# plumbing for tree-shaped drafts, so spec v2 tree verify (topk > 1) would
# commit wrong mamba states silently. Fail fast instead of mis-decoding.
if self.topk > 1:
raise NotImplementedError(
"Lightning (seg_la) linear-attention backend does not support "
f"speculative decoding with topk > 1 (got topk={self.topk}); "
"seg_la verifies a draft tree as a chain. Use "
"--speculative-eagle-topk 1."
)
# lightning attn does not need conv cache, but to keep the interface for mamba cache
self.conv_states_shape = (
model_runner.req_to_token_pool.mamba_pool.mamba_cache.conv[0].shape
)
assert not (
model_runner.sliding_window_size is not None
and model_runner.model_config.is_encoder_decoder
), "Sliding window and cross attention are not supported together"
# extra metadata for handling speculative decoding topk > 1, extended draft decode and verify
self.max_context_len = model_runner.model_config.context_len
self.device = model_runner.device
self.decode_cuda_graph_metadata = {}
self.kv_cache_dtype = model_runner.kv_cache_dtype
self.kv_cache_dtype_str = model_runner.server_args.kv_cache_dtype
self.BLOCK = (
model_runner.model_config.block
if hasattr(model_runner.model_config, "block")
else 256
)
total_num_heads = model_runner.model_config.hf_config.num_attention_heads
num_hidden_layers = model_runner.model_config.hf_config.num_hidden_layers
self.tp_slope = LightningAttentionBackend._build_slope_tensor(
total_num_heads, num_hidden_layers, self.device
)
self.linear_backend = getattr(
model_runner.model_config.hf_config, "linear_backend", "seg_la"
)
logger.info(
f"linear_backend for linear attention in hybrid_linear_backend: {self.linear_backend}"
)
def init_forward_metadata_out_graph(
self,
forward_batch: ForwardBatch,
in_capture: bool = False,
):
# seq_lens_cpu is unused by the underlying _replay_metadata for
# non-target-verify modes; pass it through for compatibility.
bs = forward_batch.batch_size
metadata = self._replay_metadata(
bs,
forward_batch.req_pool_indices,
forward_batch.forward_mode,
forward_batch.spec_info,
forward_batch.seq_lens_cpu if not in_capture else None,
)
self.forward_metadata = BailingLinearMetadata.prepare_decode(
metadata.query_start_loc,
metadata.mamba_cache_indices,
bs,
forward_batch.seq_lens,
)
def init_forward_metadata(self, forward_batch: ForwardBatch):
metadata = self._forward_metadata(forward_batch)
self.forward_metadata = BailingLinearMetadata.prepare_mixed(
metadata.query_start_loc,
metadata.mamba_cache_indices,
forward_batch,
)
@staticmethod
def _build_slope_tensor(
n_attention_heads: int, num_hidden_layers: int, device="cuda"
):
def get_slopes(n):
def get_slopes_power_of_2(n):
start = 2 ** (-(2 ** -(math.log2(n) - 3)))
ratio = start
return [start * ratio**i for i in range(n)]
if math.log2(n).is_integer():
return get_slopes_power_of_2(n)
else:
closest_power_of_2 = 2 ** math.floor(math.log2(n))
return (
get_slopes_power_of_2(closest_power_of_2)
+ get_slopes(2 * closest_power_of_2)[0::2][: n - closest_power_of_2]
)
slopes = torch.tensor(
get_slopes(n_attention_heads), dtype=torch.float32
).reshape(n_attention_heads, 1, 1)
tp_heads = n_attention_heads // get_parallel().attn_tp_size
tp_rank = get_parallel().attn_tp_rank
if num_hidden_layers <= 1:
slope_rate_list = [slopes * (1 + 1e-5)]
else:
slope_rate_list = [
slopes * (1 - layer_id / (num_hidden_layers - 1) + 1e-5)
for layer_id in range(num_hidden_layers)
]
tp_slope = [
slope_rate_list[layer_id][tp_rank * tp_heads : (tp_rank + 1) * tp_heads]
.contiguous()
.to(device)
for layer_id in range(num_hidden_layers)
]
return tp_slope
def _prefill_and_mix_infer(
self,
q,
k,
v,
kv_cache,
state_indices_tensor,
forward_batch,
layer,
metadata,
):
hidden = []
for _prefill_idx in range(metadata.num_prefills):
if _prefill_idx >= forward_batch.extend_start_loc.shape[0]:
break
if _prefill_idx >= state_indices_tensor.shape[0]:
break
_start = forward_batch.extend_start_loc[_prefill_idx]
if _prefill_idx + 1 < forward_batch.extend_start_loc.shape[0]:
_end = forward_batch.extend_start_loc[_prefill_idx + 1]
else:
if (
forward_batch.extend_seq_lens is not None
and _prefill_idx < forward_batch.extend_seq_lens.shape[0]
and metadata.num_decodes > 0
):
seq_len = forward_batch.extend_seq_lens[_prefill_idx]
_end = _start + seq_len
else:
_end = q.shape[0]
slot_id = state_indices_tensor[_prefill_idx]
qs = q[_start:_end].transpose(0, 1).contiguous()
ks = k[_start:_end].transpose(0, 1).contiguous()
vs = v[_start:_end].transpose(0, 1).contiguous()
slice_layer_cache = kv_cache[slot_id, ...]
out_slice = BailingLinearKernel.jit_linear_forward_prefix(
qs,
ks,
vs,
slice_layer_cache,
self.tp_slope[layer.layer_id],
self.BLOCK,
layer_idx=layer.layer_id,
)
hidden.append(out_slice.contiguous())
if metadata.num_decodes > 0:
hidden.append(
self._decode_infer(
q, k, v, kv_cache, state_indices_tensor, metadata, layer
)
)
if not hidden:
return torch.empty((0, q.size(-1)), device=q.device, dtype=q.dtype)
hidden = torch.concat(hidden, dim=0).contiguous()
return hidden
def _decode_infer(self, q, k, v, kv_cache, state_indices_tensor, metadata, layer):
num_prefill_tokens = metadata.num_prefill_tokens
num_prefills = metadata.num_prefills
q = q[num_prefill_tokens:].unsqueeze(2).contiguous()
k = k[num_prefill_tokens:].unsqueeze(2).contiguous()
v = v[num_prefill_tokens:].unsqueeze(2).contiguous()
slot_id = state_indices_tensor[num_prefills:]
assert slot_id.shape[0] == q.shape[0], (
f"slot_id length {slot_id.shape[0]} does not match decode batch size {q.shape[0]}. "
"This indicates a bug in the upstream logic that should be investigated."
)
hidden = linear_decode_forward_triton(
q, k, v, kv_cache, self.tp_slope[layer.layer_id], slot_id, 32
)
return hidden
def _linear_attention_entry(
self,
q,
k,
v,
kv_cache,
state_indices_tensor,
metadata,
layer,
mask=None,
temp_cache=None,
intermediate_state_indices=None,
):
q_offsets = metadata.query_start_loc
seg_meta = SegLaMeta(
batch_size=metadata.batch_size,
q_offsets=metadata.query_start_loc,
s_offsets=state_indices_tensor,
q_lengths=q_offsets.diff(),
s_scales=metadata.has_initial_states,
max_q_length=None,
mask=mask,
)
hidden = seg_la_fwd(
q=q,
k=k,
v=v,
s=kv_cache,
decay_scales=self.tp_slope[layer.layer_id],
meta=seg_meta,
caches=temp_cache,
cache_indices=intermediate_state_indices,
decouple=True,
)
return hidden
def forward_extend(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
layer: RadixAttention,
forward_batch: ForwardBatch,
save_kv_cache=True,
**kwargs,
):
layer_id = layer.layer_id if layer else kwargs["layer_id"]
metadata = self.forward_metadata
if self.kv_cache_dtype_str != "auto" and layer.k_scale is not None:
q = q.to(self.kv_cache_dtype)
cache_indices = self.forward_metadata.mamba_cache_indices
mamba_cache_params = self.req_to_token_pool.mamba2_layer_cache(layer_id)
ssm_states = mamba_cache_params.temporal
if self.linear_backend == "minimax":
o = self._prefill_and_mix_infer(
q.contiguous().view(-1, layer.tp_q_head_num, layer.head_dim),
k,
v,
ssm_states,
cache_indices,
forward_batch,
layer,
metadata,
)
elif self.linear_backend == "seg_la":
intermediate_state_indices = (
torch.arange(
cache_indices.shape[0],
dtype=torch.int32,
device=cache_indices.device,
)
if forward_batch.forward_mode.is_target_verify()
else None
)
o = self._linear_attention_entry(
q,
k,
v,
ssm_states,
cache_indices,
metadata,
layer,
temp_cache=(
mamba_cache_params.intermediate_ssm
if forward_batch.forward_mode.is_target_verify()
else None
),
intermediate_state_indices=intermediate_state_indices,
)
else:
raise ValueError(
f"linear backend: {self.linear_backend} is not support for now"
)
if (
not forward_batch.forward_mode.is_target_verify()
and forward_batch.mamba_track_mask is not None
):
# save mamba cache for extra buffer
mamba_track_mask = forward_batch.mamba_track_mask
mamba_track_indices = forward_batch.mamba_track_indices
dst_masked = mamba_track_indices[mamba_track_mask]
src_masked = metadata.mamba_cache_indices[mamba_track_mask]
ssm_states[dst_masked] = ssm_states[src_masked]
return o.view(-1, layer.tp_q_head_num * layer.v_head_dim)
def forward_decode(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
layer: RadixAttention,
forward_batch: ForwardBatch,
save_kv_cache=True,
**kwargs,
) -> torch.Tensor:
layer_id = layer.layer_id if layer else kwargs["layer_id"]
# Use precomputed metadata across all layers
metadata = self.forward_metadata
if self.kv_cache_dtype_str != "auto":
q = q.to(self.kv_cache_dtype)
# Do linear attention
cache_indices = self.forward_metadata.mamba_cache_indices
mamba_cache_params = self.req_to_token_pool.mamba2_layer_cache(layer_id)
ssm_states = mamba_cache_params.temporal
if self.linear_backend == "minimax":
o = self._decode_infer(q, k, v, ssm_states, cache_indices, metadata, layer)
elif self.linear_backend == "seg_la":
o = self._linear_attention_entry(
q, k, v, ssm_states, cache_indices, metadata, layer
)
else:
raise ValueError(
f"linear backend: {self.linear_backend} is not support for now"
)
return o.view(-1, layer.tp_q_head_num * layer.v_head_dim)