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

466 lines
17 KiB
Python

from __future__ import annotations
import functools
import inspect
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING, Any, Optional, Union
import torch
from sglang.srt.layers.moe.moe_runner.base import (
MoeQuantInfo,
MoeRunnerConfig,
MoeRunnerCore,
RunnerInput,
RunnerOutput,
register_post_permute,
register_pre_permute,
)
from sglang.srt.layers.moe.utils import MoeRunnerBackend
from sglang.srt.utils import get_bool_env_var, get_int_env_var
if TYPE_CHECKING:
from sglang.srt.layers.moe.token_dispatcher.base import CombineInput
from sglang.srt.layers.moe.token_dispatcher.deepep import (
DeepEPLLDispatchOutput,
DeepEPNormalDispatchOutput,
)
from sglang.srt.layers.moe.token_dispatcher.moriep import (
MoriEPLLDispatchOutput,
MoriEPNormalDispatchOutput,
)
from sglang.srt.layers.moe.token_dispatcher.standard import (
StandardCombineInput,
StandardDispatchOutput,
)
class AiterQuantType(str, Enum):
NONE = "No"
PER_TOKEN = "per_Token"
PER_128X128 = "per_128x128"
PER_1X32 = "per_1x32"
@dataclass
class AiterMoeQuantInfo(MoeQuantInfo):
w13_weight: torch.Tensor
w2_weight: torch.Tensor
quant_type: AiterQuantType = AiterQuantType.NONE
w13_scale: Optional[torch.Tensor] = None
w2_scale: Optional[torch.Tensor] = None
a13_scale: Optional[torch.Tensor] = None
a2_scale: Optional[torch.Tensor] = None
b13: Optional[torch.Tensor] = None
b2: Optional[torch.Tensor] = None
expert_mask: Optional[torch.Tensor] = None
doweight_stage1: bool = False
hidden_pad: int = 0
intermediate_pad: int = 0
swiglu_limit: float = 0.0
fused_moe_kwargs: Optional[dict[str, Any]] = None
@dataclass
class AiterRunnerInput(RunnerInput):
hidden_states: torch.Tensor
topk_ids: torch.Tensor # int32
topk_weights: torch.Tensor # float32
# Effective activation quant_type (may differ from quant_info.quant_type
# after the dispatch-aware decision in mori pre_permute).
quant_type: AiterQuantType
# Per-token activation scale produced by an EP dispatcher (mori). Falls
# back to quant_info.a13_scale when None.
a1_scale: Optional[torch.Tensor] = None
# Mori-only fused_moe kwargs.
num_local_tokens: Optional[torch.Tensor] = None
output_dtype: Optional[torch.dtype] = None
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
@dataclass
class AiterRunnerOutput(RunnerOutput):
hidden_states: torch.Tensor
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
_AITER_ACTIVATIONS = {"silu": "Silu", "swiglu": "Swiglu"}
def _aiter_activation(activation: str):
from aiter import ActivationType
return getattr(ActivationType, _AITER_ACTIVATIONS.get(activation, "Gelu"))
def _aiter_quant_type(quant_type: AiterQuantType):
from aiter import QuantType
return getattr(QuantType, quant_type.value)
@functools.cache
def _aiter_fused_moe_supports_no_combine() -> bool:
"""Probe whether the installed aiter.fused_moe accepts a `no_combine` kwarg.
Older wheels don't expose it, so feature-detect once and forward
conditionally, matching the existing `**extra` conditional-kwarg pattern
used for `num_local_tokens` / `dtype`.
"""
from aiter.fused_moe import fused_moe
return "no_combine" in inspect.signature(fused_moe).parameters
class AiterRunnerCore(MoeRunnerCore):
def run(
self,
runner_input: AiterRunnerInput,
quant_info: AiterMoeQuantInfo,
running_state: dict,
hooks: Optional[Any] = None,
) -> AiterRunnerOutput:
if self.config.no_combine and not _aiter_fused_moe_supports_no_combine():
raise NotImplementedError(
"no_combine=True requested but the installed aiter.fused_moe does "
"not accept a `no_combine` kwarg. Install an aiter build that "
"supports fused_moe no_combine output."
)
if runner_input.hidden_states.shape[0] == 0:
if self.config.no_combine:
topk = runner_input.topk_ids.shape[-1]
hidden_size = runner_input.hidden_states.shape[-1]
return AiterRunnerOutput(
hidden_states=runner_input.hidden_states.new_empty(
(0, topk, hidden_size)
)
)
return AiterRunnerOutput(hidden_states=runner_input.hidden_states)
from aiter.fused_moe import fused_moe
from sglang.srt.environ import envs
a1_scale = (
runner_input.a1_scale
if runner_input.a1_scale is not None
else quant_info.a13_scale
)
extra: dict = {}
if quant_info.fused_moe_kwargs:
extra.update(quant_info.fused_moe_kwargs)
if runner_input.num_local_tokens is not None:
extra["num_local_tokens"] = runner_input.num_local_tokens
if runner_input.output_dtype is not None:
extra["dtype"] = runner_input.output_dtype
if quant_info.swiglu_limit > 0:
# GateMode is only needed for the gpt-oss MXFP4 swiglu_limit path.
# Import lazily so models that don't use it (e.g. DeepSeek-V3 fp8,
# swiglu_limit==0) still run on aiter builds where this module
# lives elsewhere / is absent.
from aiter.ops.flydsl.moe_common import GateMode
# Default (INTERLEAVE) preserves the pre-fix behavior for paths
# that prepare weights in the gate/up-interleaved layout. Set
# `SGLANG_USE_AITER_MOE_GU_ITLV=0` to switch to SEPARATED, which
# matches the layout produced by `Mxfp4MoEMethod` (gpt-oss
# MXFP4) and the gptoss_fp4 tuned FlyDSL kernels.
extra["gate_mode"] = (
GateMode.INTERLEAVE.value
if envs.SGLANG_USE_AITER_MOE_GU_ITLV.get()
else GateMode.SEPARATED.value
)
extra["swiglu_limit"] = quant_info.swiglu_limit
if self.config.no_combine:
extra["no_combine"] = True
output = fused_moe(
hidden_states=runner_input.hidden_states,
w1=quant_info.w13_weight,
w2=quant_info.w2_weight,
topk_weight=runner_input.topk_weights,
topk_ids=runner_input.topk_ids,
quant_type=_aiter_quant_type(runner_input.quant_type),
activation=_aiter_activation(self.config.activation),
w1_scale=quant_info.w13_scale,
w2_scale=quant_info.w2_scale,
a1_scale=a1_scale,
a2_scale=quant_info.a2_scale,
bias1=quant_info.b13,
bias2=quant_info.b2,
expert_mask=quant_info.expert_mask,
doweight_stage1=quant_info.doweight_stage1,
hidden_pad=quant_info.hidden_pad,
intermediate_pad=quant_info.intermediate_pad,
**extra,
)
return AiterRunnerOutput(hidden_states=output)
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
# ---------------------------------------------------------------------------
# Pre-permute: dispatch_output -> AiterRunnerInput
# ---------------------------------------------------------------------------
@register_pre_permute("standard", "aiter")
def pre_permute_standard_to_aiter(
dispatch_output: StandardDispatchOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> AiterRunnerInput:
hidden_states = dispatch_output.hidden_states
topk_weights, topk_ids, _ = dispatch_output.topk_output
topk_weights = topk_weights.to(torch.float32)
if runner_config.apply_router_weight_on_input and not quant_info.doweight_stage1:
# Pre-scale at the Python level for kernels that don't honor doweight_stage1.
assert (
topk_weights.dim() == 2 and topk_weights.shape[-1] == 1
), "apply_router_weight_on_input requires topk=1"
hidden_states = hidden_states * topk_weights.to(hidden_states.dtype)
topk_weights = torch.ones_like(topk_weights)
return AiterRunnerInput(
hidden_states=hidden_states,
topk_ids=topk_ids.to(torch.int32),
topk_weights=topk_weights,
quant_type=quant_info.quant_type,
)
def _is_mori_dispatch_output(dispatch_output: Any) -> bool:
# MoriEP{Normal,LL}DispatchOutput carry the post-mori-permute origin_topk_*
# tensors that the standard DeepEP outputs lack.
return hasattr(dispatch_output, "origin_topk_ids")
def _resolve_mori_quant_type(
dispatch_a1_dtype: torch.dtype,
dispatch_scale: Optional[torch.Tensor],
weight_quant: AiterQuantType,
) -> AiterQuantType:
"""Pick the activation quant_type for AITER when the dispatch path may have
pre-quantized hidden_states. Mirrors the original MoriEPMoE.run_moe_core
decision tree."""
is_fp8_quant = weight_quant in (
AiterQuantType.PER_128X128,
AiterQuantType.PER_TOKEN,
)
is_w4a4 = weight_quant == AiterQuantType.PER_1X32
is_fp4_dispatch = dispatch_a1_dtype == torch.float4_e2m1fn_x2
has_dispatch_scale = dispatch_scale is not None
if is_w4a4:
# W4A4 weights always run as per_1x32; FP8 dispatch is upscaled to BF16
# before this point so dispatch_scale won't conflict.
return AiterQuantType.PER_1X32
if is_fp8_quant:
return weight_quant
# BF16 weights: lift to the dispatch-side quant type when scales are provided.
if has_dispatch_scale and is_fp4_dispatch:
return AiterQuantType.PER_1X32
if has_dispatch_scale and not is_fp4_dispatch:
return AiterQuantType.PER_128X128
return AiterQuantType.NONE
def _pre_permute_deepep_to_aiter(
dispatch_output: Union[
DeepEPNormalDispatchOutput,
DeepEPLLDispatchOutput,
MoriEPNormalDispatchOutput,
MoriEPLLDispatchOutput,
],
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> AiterRunnerInput:
is_mori = _is_mori_dispatch_output(dispatch_output)
hidden_states = dispatch_output.hidden_states
topk_ids = dispatch_output.topk_ids.to(torch.int32)
topk_weights = dispatch_output.topk_weights.to(torch.float32)
a1_scale: Optional[torch.Tensor] = None
num_local_tokens: Optional[torch.Tensor] = None
output_dtype: Optional[torch.dtype] = None
quant_type = quant_info.quant_type
if is_mori:
from sglang.srt.layers.moe.rocm_moe_utils import upscale, upscale_mxfp4
a1_scale = dispatch_output.hidden_states_scale
num_local_tokens = dispatch_output.num_recv_tokens_per_expert
output_dtype = dispatch_output.out_dtype
# Truncate dispatch tensors to the configured cap; mori combine only
# reads [0, totalRecvTokenNum), so the truncated result needs no
# padding back.
mori_max = get_int_env_var("SGLANG_MORI_MOE_MAX_INPUT_TOKENS", 0)
if mori_max > 0:
hidden_states = hidden_states[:mori_max]
if a1_scale is not None:
a1_scale = a1_scale[:mori_max]
topk_ids = topk_ids[:mori_max]
topk_weights = topk_weights[:mori_max]
# Upscale dispatched activations when there is no AITER kernel for the
# weight/activation dtype pair.
weight_quant = quant_info.quant_type
is_fp8_quant = weight_quant in (
AiterQuantType.PER_128X128,
AiterQuantType.PER_TOKEN,
)
is_w4a4 = weight_quant == AiterQuantType.PER_1X32
is_fp4_dispatch = hidden_states.dtype == torch.float4_e2m1fn_x2
# AITER fused_moe Clamped-SwiGLU is dispatched with
# gate_mode=INTERLEAVE, for which AITER picks a bf16/fp8 `q_dtype_a`
# Refer to https://github.com/ROCm/aiter/blob/a2617c366dc7271a1662ecda2023d19f6ccefcec/aiter/fused_moe.py#L406-L412
swiglu_interleave = quant_info.swiglu_limit > 0 and get_bool_env_var(
"SGLANG_USE_AITER_MOE_GU_ITLV", "true"
)
if is_w4a4 and a1_scale is not None and not is_fp4_dispatch:
# W4A4 weights with FP8 dispatch: dequant FP8->BF16 first; the
# FP4 per_1x32 path needs BF16 input.
hidden_states = upscale(
hidden_states, a1_scale, num_local_tokens, output_dtype
)
a1_scale = None
elif is_w4a4 and is_fp4_dispatch and a1_scale is not None and swiglu_interleave:
# W4A4 weights + FP4 dispatch on the clamped-SwiGLU/INTERLEAVE
# path: AITER expects a bf16/fp8 activation here, not fp4x2.
# Dequant FP4->BF16 and let fused_moe re-quantize internally.
hidden_states = upscale_mxfp4(
hidden_states, a1_scale, num_local_tokens, output_dtype
)
a1_scale = None
elif is_fp8_quant and is_fp4_dispatch and a1_scale is not None:
# FP8 weights + FP4 dispatch: no kernel for the fp4x2/fp8 pair;
# dequant FP4->BF16 and let fused_moe re-quantize to FP8.
hidden_states = upscale_mxfp4(
hidden_states, a1_scale, num_local_tokens, output_dtype
)
a1_scale = None
quant_type = _resolve_mori_quant_type(
hidden_states.dtype, a1_scale, weight_quant
)
running_state["aiter_combine_topk_ids"] = dispatch_output.origin_topk_ids
running_state["aiter_combine_topk_weights"] = (
dispatch_output.origin_topk_weights
)
else:
# DeepEP marks invalid topk slots with idx == -1; AITER cannot accept
# negative ids, so reroute them to the sink slot at index
# num_local_experts (masked off by quant_info.expert_mask which has
# shape (num_local_experts + 1,)).
topk_ids = torch.where(
topk_ids == -1,
torch.full_like(topk_ids, runner_config.num_local_experts),
topk_ids,
)
running_state["aiter_combine_topk_ids"] = dispatch_output.topk_ids
running_state["aiter_combine_topk_weights"] = dispatch_output.topk_weights
running_state["aiter_combine_is_mori"] = is_mori
return AiterRunnerInput(
hidden_states=hidden_states,
topk_ids=topk_ids,
topk_weights=topk_weights,
quant_type=quant_type,
a1_scale=a1_scale,
num_local_tokens=num_local_tokens,
output_dtype=output_dtype,
)
register_pre_permute("deepep_normal", "aiter")(_pre_permute_deepep_to_aiter)
register_pre_permute("deepep_ll", "aiter")(_pre_permute_deepep_to_aiter)
# ---------------------------------------------------------------------------
# Post-permute: AiterRunnerOutput -> CombineInput
# ---------------------------------------------------------------------------
@register_post_permute("aiter", "standard")
def post_permute_aiter_to_standard(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> StandardCombineInput:
from sglang.srt.layers.moe.token_dispatcher.standard import StandardCombineInput
return StandardCombineInput(hidden_states=runner_output.hidden_states)
def _post_permute_aiter_to_deepep(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
is_normal: bool,
) -> CombineInput:
if running_state.get("aiter_combine_is_mori"):
from sglang.srt.layers.moe.token_dispatcher.moriep import (
MoriEPLLCombineInput,
MoriEPNormalCombineInput,
)
cls = MoriEPNormalCombineInput if is_normal else MoriEPLLCombineInput
else:
from sglang.srt.layers.moe.token_dispatcher.deepep import (
DeepEPLLCombineInput,
DeepEPNormalCombineInput,
)
cls = DeepEPNormalCombineInput if is_normal else DeepEPLLCombineInput
return cls(
hidden_states=runner_output.hidden_states,
topk_ids=running_state["aiter_combine_topk_ids"],
topk_weights=running_state["aiter_combine_topk_weights"],
)
@register_post_permute("aiter", "deepep_normal")
def post_permute_aiter_to_deepep_normal(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> CombineInput:
return _post_permute_aiter_to_deepep(
runner_output, quant_info, runner_config, running_state, is_normal=True
)
@register_post_permute("aiter", "deepep_ll")
def post_permute_aiter_to_deepep_ll(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> CombineInput:
return _post_permute_aiter_to_deepep(
runner_output, quant_info, runner_config, running_state, is_normal=False
)