Repository navigation
[sglang-miles] Kimi K3 colocated RL: LoRA fixes and in-place MXFP4 Marlin reload - #37704
Merged
Merged
Conversation
yueming-yuan
requested review from
Fridge003,
Ying1123,
jybsuper,
lifuhuang and
yushengsu-thu
as code owners
September 3, 2026 03:30
yueming-yuan
force-pushed
the
yueming/k3-lora-on-sglang-miles
branch
from
September 4, 2026 00:53
9dc9e37 to
e34f916
Compare
yueming-yuan
requested review from
Alisehen,
AniZpZ,
BBuf,
Edwardf0t1,
FlamingoPg,
HaiShaw,
OrangeRedeng,
b8zhong,
ch-wan and
mmangkad
as code owners
September 4, 2026 01:22
yueming-yuan
force-pushed
the
yueming/k3-lora-on-sglang-miles
branch
2 times, most recently
from
September 4, 2026 06:56
22cfc9f to
bf62c92
Compare
yueming-yuan
requested review from
DarkSharpness,
HydraQYH,
celve and
yuan-luo
as code owners
September 8, 2026 00:11
Zhichenzzz
force-pushed
the
sglang-miles
branch
from
September 8, 2026 05:02
a359142 to
a8e5c63
Compare
Zhichenzzz
requested review from
CatherineSue,
Qiaolin-Yu,
hnyls2002 and
kpham-sgl
as code owners
September 8, 2026 05:02
Kimi K3's latent MoE feeds the experts a routed_expert_hidden_size (3584) activation, not hidden_size (7168); gate_up_proj_moe lora_A and down_proj_moe lora_B must follow. Restores the K3 RL serving branch behavior.
post_load_weights runs after every base weight sync; with LoRA enabled self_attn.o_proj is a BaseLayerWithLoRA, which has no params_dtype.
_merge_bfa_weights stored self.f_b_proj.weight itself on the attention module, which registers a second Parameter name for the same tensor. named_parameters() dedups by identity and keeps the parent's name, so load_weights no longer finds f_b_proj.weight and every weight update silently skipped it (fb03504). Alias the storage instead.
FusedMoEWithLoRA's marlin branch whitelisted CompressedTensors and ModelOptNvFp4 quant methods, so an MXFP4 model with --moe-runner-backend marlin and LoRA failed at LoRAManager init. The marlin quant_info for MXFP4 already exists in mxfp4_marlin_moe.build_marlin_moe_quant_info; expose it through the same method name and check for the method instead of the class list.
MarlinLoraRunnerCore only ran silu_and_mul, so Kimi K3 (gated SiTU experts) failed at CUDA graph capture with LoRA on the marlin backend. Dispatch the activation the same way fused_marlin_moe does.
Every TP rank staged a full host copy of each streamed adapter (~45 GB per rank for Kimi K3's per-expert LoRA) and kept it after installing the weights into the memory pool. Under colocated RL the adapter is re-streamed every step, so the copy is never read; four ranks per node pushed the host past the cgroup limit once the engines also paused their weights to CPU. With the flag the received CUDA-IPC views are installed into a free pool slot as soon as the stream is applied and the staged references dropped; no host or device copy is made. Eviction is refused since the adapter could not be reloaded.
With --moe-runner-backend marlin the expert intermediate is tile-padded (K3 at TP16: 192 -> 256), so the base gate/up output is [gate|up] at the padded stride and the down activation is padded too. The LoRA hooks wrote the up delta at the logical stride and fed the padded activation to a down LoRA A of the logical width, so every routed-expert LoRA delta was misplaced; the train/rollout KL grew with the adapter norm while the unpadded TP8 layout stayed flat. Bring back output_slice_stride in fused_moe_lora and the marlin_intermediate_size plumbing that the sglang-miles merge dropped.
The merged front GEMM and the shared down GEMM read the base weights straight through the LoRA wrappers, so the shared_experts gate_up/down adapters the trainer streams were never applied on the fused path.
Kimi K3 sets gemm1_alpha for SiTU but loads w1/w3 contiguously (gate_up_interleaved=False); keying the interleaved B layout on gemm1_alpha alone scrambled every routed-expert gate/up delta.
fused_experts_none_to_marlin already skips it for E8M0 scales; the LoRA runner used a different reduction than the base path under LoRA batches.
process_weights_after_loading repacked the experts into fresh Parameters, so a later load_weights found no weight_loader on them and the runner's begin_weight_update had nothing to restore. Record the checkpoint and runtime layouts per layer, view the storage back to the checkpoint shape in restore_weights_before_loading, and repack expert by expert into the same storage so the Parameter objects and their weight_loader survive.
_build_check_entries passes is_shuffled to every quantized comparable since the AITER shuffled-FP8 fix, so the MXFP4 Marlin comparable raised TypeError on every compare. Marlin never shuffles, so the flag is only asserted off.
…ooks The base gate/up output is [gate | up] at whatever width the runner produced, so the slice stride is output width / num_slices, and the down input's logical width is LoRA A's last dimension. Neither needs to travel from the layer through LoRAInfo into the kernel signature.
…RA runner The LoRA runner carried its own copy of the MXFP4 predicate and of the SiLU/SiTU beta mapping; both now come from fused_marlin_moe, so the base and LoRA runners cannot drift, and the LoRA runner picks up the GPT-OSS clamp variants the same way.
Only the LoRA wrappers define lora_active, so the attribute is the check; this drops two imports from the forward path.
K3 RL runs the marlin MoE LoRA runner, not experimental_sgl_marlin, and that backend is slated for replacement; MXFP4 support belongs in its successor. Requested by the LoRA backend owner in review.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Kimi K3 RL serving work reached
sglang-milesthroughmain, and the miles K3 PR (radixark/miles#1825) now runs againstsglang-milesinstead of the oldsglang-miles-k3branch. Running it onradixark/miles:devfound five places where the merge lost or never had what colocated K3 LoRA RL needs. Each commit is one of them; all were found by reading the traceback back to the branch diff, and the last two by--check-weight-update-equal/--check-lora-weight-equal.num_shared_experts(K3's config key) was not recognized when sizing the shared-expert LoRA buffers, soinit_statedied withKeyError('gate_up_proj')on the first dense layer.o_proj.forwardnever ran under LoRA:RowParallelLinearWithLoRAcallsquant_method.applyon the base layer, so the MLA output went out ungated and the gate GEMM on the alt stream was never joined (capturing stream has unjoined work). Restores thelora_input_transformhook from the K3 branch (3768a87).hidden_size; K3's latent MoE feeds the expertsrouted_expert_hidden_size(3584).post_load_weightsreado_proj.params_dtypethrough the LoRA wrapper._merge_bfa_weightsstoredf_b_proj.weightas a second Parameter on the attention module, which hidesf_b_proj.weightfromnamed_parameters(); every weight update silently skipped it (fb03504). Alias the storage instead.Running the full model on 16 GB300 nodes (TP16, Marlin,
--no-lora-use-virtual-experts) then showed the train/rollout KL growing with the adapter norm (0.0023 at step 0, 0.0047 by step 60) while the July stack stayed flat. A line-by-line diff of the LoRA path against that stack found four more losses, each its own commit:Mxfp4MoEMethodnever exposedget_marlin_quant_info, soFusedMoEWithLoRArefused the Marlin backend outright; the LoRA Marlin runner also only ransilu_and_mul, so K3's gated SiTU experts failed at CUDA-graph capture.output_slice_strideinfused_moe_loraand themarlin_intermediate_sizeplumbing.gemm1_alphaalone; K3 sets it for SiTU but loads w1/w3 contiguously, so every routed-expert gate/up delta was scrambled.--lora-no-cpu-backup: the streamed adapter no longer keeps a per-TP-rank host copy (~45 GB/rank for the 896-expert adapter); the IPC views are installed into the pool at apply time.Full-parameter RL re-pushes the MXFP4 base on every sync, which needs one more piece:
Mxfp4MoEMethod.process_weights_after_loadingrepacked the experts into fresh Parameters, so a laterload_weightsfound noweight_loaderon them and the runner'sbegin_weight_updatehad nothing to restore. The method now records the checkpoint and Marlin layouts per layer,restore_weights_before_loadingviews the storage back to the checkpoint shape, and the repack runs expert by expert into the same storage, so the Parameter objects (and theirweight_loader) survive. Unit test:test/registered/e2e/rl/test_mxfp4_marlin_inplace_reload.py.Mxfp4MarlinComparableaccepting the checker'sis_shuffled) is a temporary cherry-pick of a standalone fix forsglang-miles; it drops out of this PR once that lands.Validation
miles CI (radixark/miles#1825,
run-ci-model-scriptson 8xH200,radixark/miles:dev+ this branch synced at test time) onPinaster/Kimi-K3-4layer-64experts:test_kimi_k3_4layer_lora_ci.py: two rollouts and syncs;--check-lora-weight-equalmatches on every rank and the base weight checker compares equal after each sync.test_kimi_k3_4layer_ci.py(full-parameter): the MXFP4 base is re-quantized and re-pushed on every sync;--check-weight-update-equalcompares the Marlin experts equal in dequantized space ([sglang-miles] Compare MXFP4 Marlin experts in dequantized space in the weight checker #40028) after the in-place reload.The Marlin-padding, interleave and shared-expert fixes were validated on the 4-layer TP8 layout with a trained adapter re-sharded from the 16-node run (rollout-0 logprob abs diff back to the zero-adapter floor); the padded-intermediate path itself only triggers at TP16 and was validated on the 16-node GB300 run.
sglang's own CI does not run on
sglang-milesPRs (thecheck-changesmaintenance gate), so the registered tests that exercise the touched files were run by hand on a fresh 8xH200 devbox (radixark/miles:dev, this branch ate5dbafcadainstalled in place of the baked sglang):e2e/rl/test_mxfp4_marlin_inplace_reload.py,e2e/rl/test_weight_checker_mxfp4_marlin.pyunit/utils/test_weight_checker_comparator.pylora/test_fused_moe_lora_kernel.pyquant/test_marlin_moe.py,kernels/ops/moe/test_moe_wna16_marlin.pylora/test_virtual_experts_kernels.py,unit/lora/test_experimental_sgl_marlin_{multi_prefill,shared_outer_reduce}.pyunit/models/test_kimi_k3_bfa_overlap.py,kernels/ops/test_kimi_k3_prerequisite_ops.py,unit/layers/quantization/test_mxfp4_sm90_cutlass.pyrl/test_lora_load_from_tensor.py,lora/test_multi_lora_backend.py,lora/test_lora_overlap_loading.py,lora/test_lora_eviction.pylora/test_lora_qwen3_8b_logprob_diff.py,lora/test_lora_moe_tp_logprob_diff.py(TP2)triton_kernel, greedy 32 tokens x 4 promptsFive files fail identically on plain
sglang-miles(reproduced on the same devbox with the base branch installed) and are not touched by this PR:unit/utils/test_weight_checker.py(a helper returns 2 values, the test unpacks 3),rl/test_weight_checker_e2e.pyandrl/test_update_weights_from_tensor.py(they push weights withoutbegin_weight_update, which the scheduler has asserted on since the RL session protocol),lora/test_moe_lora_info.py(its fakeforward_batchlacksglobal_dp_buffer_len),lora/test_lora_spec_decoding.py(EAGLE3 draft context-length check). Not runnable on H200: the B200/B300-only registrations.Follow-ups not in scope
_symm_o_proj_forward) and SP-MoE (_sp_o_proj_forward) patches ono_proj.forwardare bypassed by the LoRA wrapper the same way the output gate was; neither is active in the smoke run (auto-probe off, no symm mem). They need an output-side hook before K3 LoRA runs with AR fusion.CI States
Latest PR Test (Base): ❌ Run #35702051199
Latest PR Test (Extra): ❌ Run #35702050805
Latest PR Test (AMD ROCm 10): ❌ Run #35702051045