Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
49e709e
(cherry-pick) feat: add native gRPC sidecar module launcher (#31076) …
ishandhanani Jul 22, 2026
6227ae8
[Cherry-pick to release/v0.5.16] Fix nvfp4 online scale with pcg (#32…
Kangyan-Zhou Jul 23, 2026
6870be7
[Cherry-pick to release/v0.5.16] [spec decoding] fix inkling multi la…
Kangyan-Zhou Jul 23, 2026
7505dc6
[Cherry-pick to release/v0.5.16] Fix dynamo recompile limit in allred…
Qiaolin-Yu Jul 24, 2026
fdebc93
[Cherry-pick to release/v0.5.16] Fix stale flashinfer-MLA fallback po…
Kangyan-Zhou Jul 24, 2026
5338b11
[1/27] [sglang-miles] True on-policy training support (FSDP2 + qwen_d…
yueming-yuan Jul 25, 2026
726efd8
[2/27] [sglang-miles] R3 (Rollout Routing Replay) DeepEP and MTP supp…
yueming-yuan Jul 25, 2026
23b0989
[3/27] [sglang-miles] PD disaggregation for RL (#18646)
yueming-yuan Jul 25, 2026
3a01329
[4/27] [sglang-miles] MTP related fix (#18647)
yueming-yuan Jul 25, 2026
502c4d5
[5/27] [sglang-miles] VLM training multimodal fallback fixes (#18781)
yueming-yuan Jul 25, 2026
9c3ea38
[6/27] [sglang-miles] Fix pause-aware weight update deadlocks (#22754…
maocheng23 Jul 25, 2026
6005dcc
[7/27] [sglang-miles] R3 support on PD disaggregation mini_lb (#22916)
zyzshishui Jul 25, 2026
4014c32
[8/27] [sglang-miles] Improve PD pause handling (#23672, #23887)
ByronHsu Jul 25, 2026
e08222b
[9/27] [sglang-miles] Add KimiK2 raw tool call id parser (#25196)
guapisolo Jul 25, 2026
496d820
[10/27] Fix GLM4 MoE Lite shared expert TP flag
yueming-yuan Jul 25, 2026
448997a
[11/27] [sglang-miles] MoE-LoRA: kimi 2.5/2.6, GLM-5.2 FP8, cuda-grap…
nanjiangwill Jul 25, 2026
ed1b342
[12/27] [sglang-miles] DeepSeek V4 RL fixes (#27131, #27603, #27604, …
yueming-yuan Jul 25, 2026
976db7a
[13/27] [sglang-miles] load_lora_adapter_from_distributed API, with u…
gongyisheng Jul 25, 2026
9e3f38a
[14/27] [sglang-miles] P2P weight update support and fixes (#21278, #…
JD-ETH Jul 25, 2026
37f5a22
[15/27] [sglang-miles] rollout indexer replay: raw seq-relative topk …
yueming-yuan Jul 25, 2026
bb17e99
[16/27] [sglang-miles] RL weight-update sessions + distributed update…
guapisolo Jul 25, 2026
94442dc
[17/27] [sglang-miles] exclude shared skip-topk layer indexer weights…
yueming-yuan Jul 25, 2026
30bce16
[18/27] [sglang-miles] Fix stale _attn_sink_local cache after RL weig…
yueming-yuan Jul 25, 2026
6276616
[19/27] [lora] Support GDN in_proj_ba adapters for Qwen3.5 (#30499)
yushengsu-thu Jul 25, 2026
e0bd0e6
[20/27] [RL] Add /pull_weights: engine-side pull of published weights…
nanjiangwill Jul 25, 2026
4b64264
[21/27] feat(sglang-miles): Support aborting requests by rid prefix -…
mathewjhan Jul 25, 2026
d0c484a
[22/27] [sglang-miles] Fix flush_cache() no-op after pause_generation…
Zhichenzzz Jul 25, 2026
dd2c725
[23/27] [sglang-miles] check_weights: make the wire ChecksumInfo matc…
yueming-yuan Jul 25, 2026
7687585
[24/27] [sglang-miles] Port RankParallelismConfig / ParallelismContex…
JD-ETH Jul 25, 2026
47325f4
[25/27] [sglang-miles] Repoint three symbols v0.5.16 renamed or moved
yueming-yuan Jul 25, 2026
119689c
[26/27] [sglang-miles] Repoint two more attributes v0.5.16 relocated
yueming-yuan Jul 25, 2026
a3839f7
[27/27] [sglang-miles] Fix iter_runners reaching the draft runner on …
guapisolo Jul 25, 2026
5138eb2
fix: honor weight-check skips for quantized entries (#32809)
guapisolo Jul 29, 2026
49a5ef8
[Fix] Make RowParallelLinear k-size tuple-aware for FP8 (#30742)
XinyuJiangCMU Jul 28, 2026
3003d70
[sglang-miles] Warn instead of silently dropping one-sided MoE expert…
yushengsu-thu Jul 29, 2026
71db101
[sglang-miles] Back up the CUDA graph pool across TMS pause/resume (#…
zyzshishui Jul 30, 2026
3fe50ed
[Cherry-pick of #32861] Fix Inkling tool-call parsing recovery, conte…
Zhichenzzz Jul 31, 2026
0b45fbc
[sglang-miles] Release the LoRA reference on every request-cleanup path
Shi-Dong Aug 3, 2026
ffc5748
fix(lora): make request accounting lifecycle-safe
yushengsu-thu Aug 3, 2026
e22f56c
Redesign multi-LoRA (#33299)
yushengsu-thu Aug 4, 2026
31ad05a
Fix NextN weight loading by sharing the unified-loader mappings (#33478)
yueming-yuan Aug 4, 2026
c447264
Put the DSA cuda-graph page table in the pausable memory region (#33479)
yueming-yuan Aug 4, 2026
cb05a44
[sglang-miles] Nemotron support on sglang-miles (#27110)
Zhichenzzz Aug 5, 2026
18c0019
Merge sglang-miles and preserve transactional LoRA leases
yushengsu-thu Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ dependencies = [
"uvloop",
"watchfiles",
"xgrammar==0.2.1",
"zstandard",
"xxhash", # /pull_weights delta checksum
"zstandard", # /pull_weights delta codec
]

[[tool.uv.index]]
Expand Down
154 changes: 104 additions & 50 deletions python/sglang/kernels/ops/moe/virtual_experts.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ def _merged_experts_fused_moe_lora_add_impl(
output: torch.Tensor,
hidden_states: torch.Tensor,
lora_a: torch.Tensor,
lora_b: torch.Tensor,
lora_b: torch.Tensor | list[torch.Tensor] | tuple[torch.Tensor, ...],
topk_ids: torch.Tensor,
topk_weights: torch.Tensor,
token_lora_mapping: torch.Tensor,
Expand All @@ -524,13 +524,30 @@ def _merged_experts_fused_moe_lora_add_impl(
experts_shared_outer_loras_b: bool,
routing_cache: dict | None = None,
) -> None:
"""Fused virtual-experts LoRA delta add.

``lora_b`` accepts either a single tensor or a sequence of tensors stacked
along the output dim. Length-2 is the gate_up case where A has rank ``2*r``
(gate's A and up's A concatenated along rank) and each B has rank ``r``.
The shrink runs once over the full ``2*r`` rank; the expand runs once per
B, each reading its half of the intermediate and writing to its slice of
``output``.
"""
1. Prepare virtual expert routing metadata from topk_ids + token_lora_mapping * num_experts.
2. Flatten LoRA weights from [max_loras, num_experts, ...] to [max_loras * num_experts, ...].
3. Run regular SGLang fused-MoE kernels for LoRA A and LoRA B.
4. Mask out tokens with token_lora_mapping == -1 on the add path.
"""
lora_b_list: list[torch.Tensor] = (
list(lora_b) if isinstance(lora_b, (list, tuple)) else [lora_b]
)
n_b = len(lora_b_list)
assert n_b in (1, 2), f"lora_b must be length 1 or 2, got {n_b}"
b_rank = lora_b_list[0].shape[3]
for b in lora_b_list[1:]:
assert (
b.shape == lora_b_list[0].shape
), f"all lora_b tensors must share shape; got {[tuple(t.shape) for t in lora_b_list]}"

max_loras, _, max_lora_rank, _ = lora_a.shape
assert (
max_lora_rank == n_b * b_rank
), f"lora_a rank {max_lora_rank} != n_b ({n_b}) * lora_b rank {b_rank}"
input_top_k = 1 if hidden_states.shape[0] == topk_ids.numel() else topk_ids.shape[1]

def _merge_lora_expert_weight(t: torch.Tensor) -> torch.Tensor:
Expand Down Expand Up @@ -614,16 +631,18 @@ def _get_routing(
block_size=block_size,
num_experts=virtual_num_experts,
)
# _align_block_size uses a worst-case padded allocation. Trim the routing buffers
# to a tighter upper bound so we keep the real routed work but drop unused padding
num_tokens = topk_ids.numel()
max_nonempty = min(num_tokens, virtual_num_experts)
tight_padded = (
triton.cdiv(num_tokens + max_nonempty * (block_size - 1), block_size)
* block_size
)
sorted_token_ids = sorted_token_ids[:tight_padded]
expert_ids = expert_ids[: tight_padded // block_size]
# NOTE: do NOT trim sorted_token_ids / expert_ids to a tighter upper bound here.
# The downstream kernels (_moe_lora_shrink_splitk_kernel, fused_moe_kernel) read
# sorted_token_ids[pid_m*BLOCK : +BLOCK] and expert_ids[pid_m] WITHOUT a bounds mask
# for every block up to num_tokens_post_padded (a GPU-side count loaded at run time).
# num_tokens_post_padded comes from _align_block_size with `virtual_num_experts` buckets
# and can exceed a tighter `numel + min(numel,virtual_num_experts)*(block-1)` bound
# (most so for shared-outer, where virtual_num_experts = max_loras is small), so trimming
# made those unmasked reads land PAST the view. In eager mode the slack still lives inside
# the same _align_block_size allocation (garbage, masked out downstream) so it worked; under
# CUDA-graph capture/replay the graph mempool packs tensors tightly and that slack may belong
# to another pooled tensor / lie past a page -> cudaErrorIllegalInstruction during capture.
# Keep the full worst-case-allocated buffers so every unmasked read stays in-allocation.
expert_ids = fused_sanitize_expert_ids(expert_ids, virtual_num_experts)
result = (
sorted_token_ids,
Expand All @@ -642,12 +661,22 @@ def _get_routing(
)

lora_a_virtual = _merge_lora_expert_weight(lora_a)
lora_b_virtual = _merge_lora_expert_weight(lora_b)
lora_b_virtuals = [_merge_lora_expert_weight(b) for b in lora_b_list]
num_experts_a = lora_a.shape[1]
num_experts_b = lora_b.shape[1]

num_experts_b = lora_b_list[0].shape[1]
half_out = lora_b_list[0].shape[2]

# The kernels index token_lora_mapping / intermediate by token ids up to
# topk_ids.shape[0] (the DP-gathered token count under --enable-dp-attention). An
# under-sized mapping means unmasked OOB reads/writes that surface as a sticky,
# hard-to-attribute CUDA IMA — fail loudly on the host instead.
assert token_lora_mapping.shape[0] >= topk_ids.shape[0], (
f"token_lora_mapping covers {token_lora_mapping.shape[0]} tokens but the MoE runs on "
f"{topk_ids.shape[0]} (DP-gathered?) tokens; mapping was sized before the dp gather "
f"length was known (see get_gathered_moe_num_tokens)"
)
intermediate = torch.zeros(
[token_lora_mapping.shape[0], topk_ids.shape[1], max_lora_rank],
[topk_ids.shape[0], topk_ids.shape[1], max_lora_rank],
dtype=hidden_states.dtype,
device=hidden_states.device,
)
Expand Down Expand Up @@ -678,7 +707,7 @@ def _get_routing(
a_stage_config,
)

b_stage_config = _get_stage_config(lora_b_virtual, 1)
b_stage_config = _get_stage_config(lora_b_virtuals[0], 1)
(
sorted_token_ids,
expert_ids,
Expand All @@ -692,33 +721,53 @@ def _get_routing(
b_stage_config["BLOCK_SIZE_M"],
)

invoke_fused_moe_kernel(
intermediate.view(-1, max_lora_rank),
lora_b_virtual,
None,
output,
None,
None,
None,
topk_weights,
topk_ids,
sorted_token_ids,
expert_ids,
num_tokens_post_padded,
mul_routed_weight,
1,
b_stage_config,
tl.bfloat16 if hidden_states.dtype == torch.bfloat16 else tl.float16,
False,
False,
False,
False,
False,
None,
fuse_add_to_output=True,
add_output_mask=token_lora_mask,
router_topk=topk_ids.shape[1],
)
# n_b expands. For len 1: K=b_rank covers full intermediate, write full output.
# For len 2 (gate_up): split intermediate along rank into [gate, up] halves
# (each contiguous, K=b_rank=r) and output along last dim into [gate, up]
# halves (each of width half_out). Each B in lora_b_virtuals is its own
# half's weight tensor, naturally K=b_rank.
for b_idx, b_virtual in enumerate(lora_b_virtuals):
if n_b == 1:
inter_arg = intermediate.view(-1, b_rank)
out_arg = output
else:
inter_arg = (
intermediate[..., b_idx * b_rank : (b_idx + 1) * b_rank]
.contiguous()
.view(-1, b_rank)
)
out_arg = output[
..., b_idx * half_out : (b_idx + 1) * half_out
].contiguous()
invoke_fused_moe_kernel(
inter_arg,
b_virtual,
None,
out_arg,
None,
None,
None,
topk_weights,
topk_ids,
sorted_token_ids,
expert_ids,
num_tokens_post_padded,
mul_routed_weight,
1,
b_stage_config,
tl.bfloat16 if hidden_states.dtype == torch.bfloat16 else tl.float16,
False,
False,
False,
False,
False,
None,
fuse_add_to_output=True,
add_output_mask=token_lora_mask,
router_topk=topk_ids.shape[1],
)
if n_b != 1:
output[..., b_idx * half_out : (b_idx + 1) * half_out].copy_(out_arg)


def _merged_experts_fused_moe_lora_add_op(
Expand Down Expand Up @@ -761,7 +810,7 @@ def merged_experts_fused_moe_lora_add(
output: torch.Tensor,
hidden_states: torch.Tensor,
lora_a: torch.Tensor,
lora_b: torch.Tensor,
lora_b: torch.Tensor | list[torch.Tensor] | tuple[torch.Tensor, ...],
topk_ids: torch.Tensor,
topk_weights: torch.Tensor,
token_lora_mapping: torch.Tensor,
Expand All @@ -770,7 +819,12 @@ def merged_experts_fused_moe_lora_add(
experts_shared_outer_loras_b: bool,
routing_cache: dict | None = None,
) -> None:
"""Public API: wraps the registered op with routing_cache support."""
"""Public API: wraps the registered op with routing_cache support.

``lora_b`` accepts a sequence of length 2 for the gate_up case (each B
holds one half of the stacked output, rank ``r``, with A's rank ``2*r``);
a single tensor is used for the down case.
"""
_merged_experts_fused_moe_lora_add_impl(
output,
hidden_states,
Expand Down
14 changes: 10 additions & 4 deletions python/sglang/srt/arg_groups/overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -1939,7 +1939,12 @@ def _page_size_default(view: Any) -> dict:
@register_post_process
def _data_parallelism_defaults(view: Any) -> dict:
if view.dp_size == 1 and view.ep_join_mode != "scale":
return {"enable_dp_attention": False, "enable_dp_lm_head": False}
overrides = {"enable_dp_attention": False}
# Keep the dp LM head when attention context parallelism is enabled,
# even without data parallelism (context-parallel LM head).
if not (view.enable_dp_lm_head and view.attn_cp_size > 1):
overrides["enable_dp_lm_head"] = False
return overrides
return {}


Expand All @@ -1948,9 +1953,10 @@ def _dp_lm_head_validation(view: Any) -> dict:
"""Read-only validation pass: dp-attention is a prerequisite for the
dp LM head. Reads the mid-resolution values through the view."""
if view.enable_dp_lm_head:
assert (
view.enable_dp_attention
), "Please enable dp attention when setting enable_dp_lm_head. "
assert view.enable_dp_attention or view.attn_cp_size > 1, (
"Please enable dp attention when setting enable_dp_lm_head, "
"unless attention context parallelism is enabled."
)
return {}


Expand Down
10 changes: 9 additions & 1 deletion python/sglang/srt/configs/cohere2_moe.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: Apache-2.0
"""Cohere2Moe text config used by the Cohere Command-A Plus checkpoints."""

from dataclasses import dataclass

from transformers.configuration_utils import PreTrainedConfig
from transformers.models.auto.configuration_auto import CONFIG_MAPPING

Expand All @@ -13,6 +15,7 @@ def strict(cls): # type: ignore[misc]


@strict
@dataclass
class Cohere2MoeConfig(PreTrainedConfig):
model_type = "cohere2_moe"
keys_to_ignore_at_inference = ["past_key_values"]
Expand Down Expand Up @@ -52,6 +55,9 @@ class Cohere2MoeConfig(PreTrainedConfig):
rms_norm_eps: float | None = None
sliding_window_pattern: int = 4

def validate_rope(self):
return super().validate_rope()

def __post_init__(self, **kwargs):
if self.num_key_value_heads is None:
self.num_key_value_heads = self.num_attention_heads
Expand Down Expand Up @@ -82,7 +88,9 @@ def __post_init__(self, **kwargs):
]
self.layer_types = prefix_layers + rest_layers

super().__post_init__(**kwargs)
post_init = getattr(super(), "__post_init__", None)
if post_init is not None:
post_init(**kwargs)


try:
Expand Down
5 changes: 4 additions & 1 deletion python/sglang/srt/configs/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,10 @@ def dsa_layer_skips_topk(config: PretrainedConfig, layer_id: int) -> bool:


def get_dsa_index_n_heads(config: PretrainedConfig) -> int:
assert is_deepseek_dsa(config)
# Permit both DSA (V3.2-family) and V4: both carry the indexer (index_n_heads) and this must
# match get_dsa_index_head_dim's contract, else LoRA buffer init for indexer.wq_b /
# indexer.weights_proj on a V4 model asserts here while indexer.wk (which uses head_dim) succeeds.
assert is_deepseek_dsa(config) or is_deepseek_v4(config)
return config.index_n_heads


Expand Down
2 changes: 2 additions & 0 deletions python/sglang/srt/disaggregation/prefill.py
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,8 @@ def process_disagg_prefill_inflight_queue(
# todo: set Transferring correctly in backend
undone_reqs.append(req)
elif poll == KVPoll.Success: # transfer done
if req.return_routed_experts:
self.batch_result_processor._maybe_collect_routed_experts(req)
release_kv_cache(req, self.tree_cache) # unlock the tree
if not isinstance(req.finished_reason, FINISH_ABORT):
req.finished_reason = FINISH_LENGTH(length=0)
Expand Down
9 changes: 9 additions & 0 deletions python/sglang/srt/distributed/communication_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
import torch
import torch.distributed

from sglang.srt.tp_invariant_ops import tree_all_reduce_sum
from sglang.srt.true_on_policy import should_use_tp_invariant_tree_all_reduce

from .parallel_state import (
get_attn_tp_group,
get_moe_ep_group,
Expand All @@ -17,6 +20,8 @@

def tensor_model_parallel_all_reduce(input_: torch.Tensor) -> torch.Tensor:
"""All-reduce the input tensor across model parallel group."""
if should_use_tp_invariant_tree_all_reduce():
return tree_all_reduce_sum(input_, device_group=get_tp_group().device_group)
return get_tp_group().all_reduce(input_)


Expand Down Expand Up @@ -64,6 +69,10 @@ def broadcast_tensor_dict(

def attention_tensor_model_parallel_all_reduce(input_: torch.Tensor) -> torch.Tensor:
"""All-reduce the input tensor across attention parallel group."""
if should_use_tp_invariant_tree_all_reduce():
return tree_all_reduce_sum(
input_, device_group=get_attn_tp_group().device_group
)
return get_attn_tp_group().all_reduce(input_)


Expand Down
Loading
Loading