Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1512db7
[Chore] release: Cherry-pick [Disagg] Return prompt routed experts fr…
Kangyan-Zhou Oct 14, 2026
baa9bef
[Chore] release: Cherry-pick [CI] glm5.2: Temporarily disable PD HiSp…
Kangyan-Zhou Oct 14, 2026
15da98c
[1/42] [sglang-miles] True on-policy training and tuple-aware row-par…
yueming-yuan Feb 10, 2026
d9f62b9
[2/42] [sglang-miles] R3 draft isolation and graph capture layout (#1…
yueming-yuan Feb 11, 2026
4e54c8d
[3/42] [sglang-miles] PD disaggregation for RL (#18646)
yueming-yuan Feb 11, 2026
9d57fb8
[4/42] [sglang-miles] MTP related fix (#18647)
yueming-yuan Feb 12, 2026
08b9c16
[5/42] [sglang-miles] VLM training multimodal fallback fixes (#18781)
yueming-yuan Feb 13, 2026
dd5fdd7
[6/42] [sglang-miles] Allow a ragged final merged FP8 matrix (#22623)
maocheng23 Apr 12, 2026
62ff83f
[7/42] [sglang-miles] R3 support on PD disaggregation mini_lb (#22916)
zyzshishui Apr 16, 2026
0b66303
[8/42] [sglang-miles] Add KimiK2 raw tool call id parser (#25196)
guapisolo May 13, 2026
890c471
[9/42] [sglang-miles] Fix GLM4 MoE Lite shared expert TP flag
yueming-yuan Aug 22, 2026
0eabd9a
[10/42] [sglang-miles] MoE LoRA support and expert-target validation …
nanjiangwill May 23, 2026
f19a8ea
[11/42] [sglang-miles] DeepSeek V4 RL fixes (#27131, #27603, #27604, …
yueming-yuan May 29, 2026
c40cd81
[12/42] [sglang-miles] Streamed LoRA updates, upsert and deferred pub…
gongyisheng Jun 4, 2026
ce3edb9
[13/42] [sglang-miles] P2P weight mapping and scoped replica parallel…
JD-ETH Mar 24, 2026
64b3f11
[14/42] [sglang-miles] rollout indexer replay: raw seq-relative topk …
yueming-yuan Aug 22, 2026
ebb1007
[15/42] [sglang-miles] Exclude shared skip-topk indexer weights from …
yueming-yuan Jun 25, 2026
0ca3311
[16/42] [sglang-miles] Fix stale _attn_sink_local cache after RL weig…
yueming-yuan Jul 2, 2026
450c010
[17/42] [sglang-miles] Support Qwen3.5 GDN LoRA targets (#30499, #374…
yushengsu-thu Jul 8, 2026
f0604fa
[18/42] [sglang-miles] Add /pull_weights: engine-side pull of publish…
nanjiangwill Jul 7, 2026
8199ffd
[19/42] [sglang-miles] Nemotron hybrid metadata and routing replay (#…
yueming-yuan Aug 4, 2026
61bfead
[20/42] [sglang-miles] RDT/NIXL weight sync for Ray scheduler actors …
xyuzh Jun 9, 2026
9698daf
[21/42] [sglang-miles] args: drop DeepseekV4 from the FlashInfer allr…
yueming-yuan Aug 27, 2026
946ca4f
[22/42] [sglang-miles] Add SGLANG_DISABLE_MULTIMEM_AG to force the NC…
yueming-yuan Aug 23, 2026
99ebed6
[23/42] [sglang-miles] Extract Anthropic conversion into standalone u…
guapisolo Aug 11, 2026
6a957cb
[24/42] [sglang-miles] Exclude derived sincos and MRoPE buffers from …
yueming-yuan Sep 2, 2026
480b1de
[25/42] [sglang-miles] Compare MXFP4 Marlin experts in dequantized sp…
yueming-yuan Sep 17, 2026
40991a9
[26/42] [sglang-miles] Kimi K3 LoRA, fused shared experts and in-plac…
yueming-yuan Sep 3, 2026
04452ec
[27/42] [sglang-miles] Resolve HF LoRA targets through model-aware no…
yueming-yuan Sep 18, 2026
d3c23d2
[28/42] [sglang-miles] Flush and synchronize hybrid caches before off…
Zhichenzzz Sep 23, 2026
42e34b7
[29/42] [sglang-miles] H3 rollout trajectories, response timing and I…
Rockdu Aug 20, 2026
bc1ad00
[30/42] [sglang-miles] MiMo colocated RL loaders, pausable caches and…
guapisolo Sep 21, 2026
45d0421
[31/42] [sglang-miles] fix: initialize Inkling safely on ROCm (#37294)
guapisolo Aug 30, 2026
88167e2
[32/42] [sglang-miles] Allocate packed weight receive buffers directl…
zianglih Oct 2, 2026
f7c05cb
[33/42] [sglang-miles] [cherrypick from #42332] [diffusion] fix: repo…
Rockdu Oct 4, 2026
c3647f7
[34/42] [sglang-miles] [diffusion] fix: give the scheduler its own ho…
Rockdu Oct 4, 2026
cd33a69
[35/42] [sglang-miles] Fix the bf16 triton MoE path and mhc_pre split…
Zhichenzzz Oct 7, 2026
bb538a2
[36/42] [sglang-miles] fix(dsv4): support R3 capture and MXFP4 online…
bobboli Oct 10, 2026
8aff7b8
[37/42] [sglang-miles] [Diffusion] Support distributed weight updates…
zhihengy Oct 10, 2026
56bef33
[38/42] [sglang-miles] Support P2P replica loading and target/draft w…
yueming-yuan Oct 12, 2026
6ac15f4
[39/42] [sglang-miles] Quantized weight-update fixes: Qwen3.5 draft K…
yueming-yuan Oct 12, 2026
092386f
[40/42] [sglang-miles] Register the batch-invariant matmul fallback f…
fzyzcjy Oct 14, 2026
36b1d3b
[41/42] [sglang-miles] Report prompt KV weight versions in generation…
fzyzcjy Oct 14, 2026
993bf90
[42/42] [sglang-miles] [Fix] lora: Load fused-module members into the…
yueming-yuan Oct 15, 2026
4abd6da
[Fix] DeepSeek-V4.1: keep the mHC next-layer link out of the module tree
guapisolo Oct 16, 2026
266912b
[Fix] LoRA MoE: expose the owner partitions on FusedMoEWithLoRA
guapisolo Oct 16, 2026
9d86e91
[Fix] Custom all-reduce v1: return empty inputs without launching the…
guapisolo Oct 16, 2026
b3b1b2e
Keep message images on pretokenized chat requests
celve Oct 17, 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
1 change: 1 addition & 0 deletions docs/docs/advanced_features/sglang_for_rl.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ A request can outlive a version change: the RL flow retracts it with `pause_gene
```

- Ranges are half-open `[start, end)` over output-token indices, prompt excluded. The usual case is a single span.
- `--enable-prefill-weight-versions` adds `prefill_weight_versions`, the same span list over the prompt tokens, so KV reused from the prefix cache under older weights is visible.

## Easy To Postpone Generation

Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ diffusion-qvg = [
]

ray = [
"ray[default]>=2.55.1",
"ray[default]>=2.56.0",
]

tracing = [
Expand Down
15 changes: 9 additions & 6 deletions python/sglang/kernels/ops/layernorm/mhc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1178,14 +1178,17 @@ def mhc_pre(
else:
if num_tokens <= 2048:
assert n_splits == 1
if hc_hidden_size == 16384:
hidden_block = 256
elif hc_hidden_size == 28672:
hidden_block = 128
split_size = hc_hidden_size // n_splits_pre
for hidden_block in (256, 128):
if (
hc_hidden_size % hidden_block == 0
and split_size % hidden_block == 0
):
break
else:
raise NotImplementedError(
f"mhc_pre splitk kernel only supports hc_hidden_size in {{16384, 28672}}, "
f"got {hc_hidden_size}"
f"mhc_pre splitk kernel needs a hidden_block in {{256, 128}} that divides "
f"hc_hidden_size // split_k = {split_size}, got hc_hidden_size={hc_hidden_size}"
)
kernel_0, _ = mhc_pre_gemm_sqrsum_splitk_kernel(
hc_mult3,
Expand Down
5 changes: 4 additions & 1 deletion python/sglang/kernels/ops/lora/moe/fused_moe_lora_kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,8 +403,11 @@ def _fused_moe_lora_expand(
IS_PRIMARY=False,
**expand_config,
)
# the base output may be padded (Marlin), so its slices sit at output width / num_slices
slice_stride = output.shape[-1] // num_slices
for i in range(num_slices):
output[:, :, i * N + offset : (i + 1) * N + offset] += b_intermediate_cache1[i]
start = i * slice_stride + offset
output[:, :, start : start + N] += b_intermediate_cache1[i]


@torch.inference_mode()
Expand Down
162 changes: 110 additions & 52 deletions python/sglang/kernels/ops/lora/moe/virtual_experts.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ def _fused_virtual_topk_ids_kernel(
# a real virtual-expert slot belonging to another adapter and trigger OOB
# loads in downstream LoRA kernels.
shifted = base + safe_lora * num_experts_for_weight
result = tl.where(base < 0, base, shifted)
# Reject an out-of-range base ID before it can alias the next adapter.
result = tl.where(base >= num_experts_for_weight, -1, shifted)
result = tl.where(base < 0, base, result)
tl.store(virtual_topk_ids_ptr + offs, result, mask=valid)

# Write mask once per row (at first k position)
Expand Down Expand Up @@ -517,7 +519,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 @@ -526,13 +528,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 @@ -616,16 +635,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 @@ -644,13 +665,24 @@ 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)"
)
# Split-K atomic additions must accumulate before rounding to the activation dtype.
intermediate = torch.zeros(
[token_lora_mapping.shape[0], topk_ids.shape[1], max_lora_rank],
dtype=hidden_states.dtype,
[topk_ids.shape[0], topk_ids.shape[1], max_lora_rank],
dtype=torch.float32,
device=hidden_states.device,
)

Expand Down Expand Up @@ -680,7 +712,8 @@ def _get_routing(
a_stage_config,
)

b_stage_config = _get_stage_config(lora_b_virtual, 1)
intermediate = intermediate.to(hidden_states.dtype)
b_stage_config = _get_stage_config(lora_b_virtuals[0], 1)
(
sorted_token_ids,
expert_ids,
Expand All @@ -694,33 +727,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 @@ -763,7 +816,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 @@ -772,7 +825,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
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ def _check_remote_scheduler(self):
raise ConnectionError(
f"Could not connect to remote scheduler at "
f"{self.server_args.scheduler_endpoint} with `local mode` as False. "
"Please ensure the server is running."
"Please ensure the server is running, binds a reachable "
"--scheduler-host, and uses the same --scheduler-port as this client."
)
logger.info(
f"Successfully connected to remote scheduler at "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class RolloutRequest(BaseModel):
fps: Optional[int] = None

rollout: bool = True
# "uint8": quantise the video engine-side; None: ship unchanged
rollout_video_dtype: Optional[str] = None
rollout_sde_type: str = "sde"
rollout_noise_level: float = 0.7
rollout_log_prob_no_const: bool = False
Expand Down Expand Up @@ -113,3 +115,30 @@ class RolloutResponse(BaseModel):

inference_time_s: Optional[float] = None
peak_memory_mb: Optional[float] = None


@dataclass
class InitWeightsUpdateGroupReqInput:
master_address: str
master_port: int
rank_offset: int
world_size: int
group_name: str
backend: str = "nccl"


@dataclass
class UpdateWeightsFromDistributedReqInput:
names: list[str]
dtypes: list[str]
shapes: list[list[int]]
group_name: str
target_modules: list[str] | None = None
weight_update_mode: str | None = None
lora_alpha: int | None = None
lora_rank: int | None = None


@dataclass
class DestroyWeightsUpdateGroupReqInput:
group_name: str
Loading