Skip to content

Bump sglang to v0.5.20 - #3321

Merged
Zhichenzzz merged 8 commits into
mainfrom
zhichen/bump-sglang-v0.5.20
Sep 21, 2026
Merged

Zhichenzzz merged 8 commits into
mainfrom
zhichen/bump-sglang-v0.5.20

Conversation

@Zhichenzzz

@Zhichenzzz Zhichenzzz commented Sep 19, 2026 •

Copy link
Copy Markdown
Collaborator

ci-sglang-pr: sglang-miles-v0.5.20
https://github.com/sgl-project/sglang/tree/sglang-miles-v0.5.20
Rebases the sglang-miles stack onto v0.5.20 and repoints the miles image at it.

sglang side

sglang-miles-v0.5.20 = 54 commits on v0.5.20, replayed from sglang-miles (the v0.5.19 stack: 55 numbered + 6 unnumbered commits, frozen as sglang-miles-v0.5.19-final @c9660a7e11). 727 upstream commits of drift; 21 of the 57 picks conflicted.

Dropped (already in v0.5.20 or superseded):

  • Keep NVFP4 blockscale swizzle padding on device (#39142) — byte-identical to upstream #39141.
  • state_capturer: pin the exact host-cache size via mmap + cudaHostRegister (#36562) — a strict subset of upstream #37285, which also adds the destroy() hooks.
  • dsv4: gate the padded-head path on attn_tp_size, not tp_size — v0.5.20's MQALayer.forward no longer has that block (_kernel_num_heads() / _local_attn_sink(kernel_num_heads) replaced it).
  • [38] Restore MqaAttentionBase.maybe_use_decode_attn_tp — only re-added two imports that the Nemotron commit had removed; that removal is gone (below), so the commit became empty.
  • Remove obsolete PD retract guard (#39119) squashed into [8/54] Improve PD pause handling, as its subject asked.

Notable conflict decisions. v0.5.20 split ServerArgs into arg_groups/fields/*.py (choices in arg_groups/choices.py, no NS() markers; field_order.py is a compat record, new fields sort last), removed the DSA prefill in-sequence-split CP path, moved the DSA prefill split-ops into dsa_prefill_cuda_graph.py, and made the tokenizer dispatch path async with its own dispatched / abort_sent bookkeeping. Our behaviour was re-expressed on those shapes:

  • True on-policy ([1]): true_on_policy_contract, enable_prefill_only_deterministic_inference live in fields/exec_.py, RL_ON_POLICY_TARGET_CHOICES in choices.py; the decode CUDA-graph capture keeps upstream's get_exec().graph flag inside our prefill-only deterministic patch; qwen3_moe keeps upstream's empty-batch guard around our true-on-policy routing branch.
  • MoE-LoRA ([11]): upstream dropped the unused intermediate buffers and weight_indices_long; we keep only the quant-agnostic device lookup and merge upstream's padded-replay-row clearing with our gathered-length mapping.
  • Rollout indexer replay ([15]): return_raw_indices is threaded into the real DSAIndexerMetadata in dsa_indexer_metadata.py. The stack used to carry a second DSAIndexerMetadata class in dsa_backend.py that shadowed the real one (and lacked upstream's output_size= host-sync fix); it is gone. The CP variant _get_topk_ragged_with_cp had nothing left to attach to and was dropped.
  • Stale attn_sink cache ([18]): a refresh_attn_sink_cache() that recopies the per-rank slice into upstream's cached padded sink; upstream's _kernel_num_heads structure is kept as is.
  • Abort by rid prefix ([21]): merged with upstream's abort_sent dedupe and the encoder dispatch_ready events; a pre-dispatch abort also releases the encoder wait.
  • Redesign multi-LoRA ([32]): the lease release is added to upstream's new handler-failure cleanup only on the non-dispatched branch (dispatched requests get it from the scheduler's abort echo).
  • RDT/NIXL ([36]): needs_engine_info_bootstrap() / registers_parallelism_config() on ServerArgs, the two fields in fields/model.py, handle_rdt_weight_sync through run_hook in the pipeline; RayEngine keeps upstream's Ray stat loggers inside our placement-group contextvar.
  • Streamed LoRA ([49], [54]): register_lora_adapter rebuilt with get_lora() / get_serving() accessors; _evict_lru_over_cap likewise.

Stale duplicates removed from the stack while rebasing (pre-existing warts from earlier rebases, all shadowing the maintained upstream copy): dsa_indexer.py forward_npu / do_npu_cp_balance_indexer (the DSANPUIndexerMixin has them), module-level pcg/bcg_dsa_indexer_prefill_split + scattered_to_tp_attn_full copies; loader.py _post_load_weights re-add (the stack renames it to post_load_weights, and the v0.5.20 call site upstream added now uses that name); deepseek_v2.py / glm4_moe.py determine_num_fused_shared_experts and glm4_moe.py get_input_embeddings copies; the Nemotron commit's compensating edits to deepseek_v4.py that no longer apply. glm4_moe_nextn and fused_moe read their flags through get_exec() like the rest of v0.5.20.

New upstream pre-commit hook check-registered-tests requires newly added tests to live at test/registered/<kind>/<subsystem>/ (unit = CPU-only, est_time ≤ 60). Moved: test_dense_deterministic_math, test_on_policy_wiring → unit/true_on_policy/; test_tp_invariant_ops → e2e/true_on_policy/; test_parallelism_context_integration → e2e/disaggregation/ (plus sys.exit(pytest.main(...)) for the bare-pytest hook); test_distributed_weight_update_spec_worker → unit/rl/; test_moe_lora_tail_stamp → e2e/lora/; test_weight_checker_mxfp4_marlin → e2e/rl/.

Every commit passes pre-commit individually (isort / ruff / ruff-format amendments folded in); ruff F821/F401 is zero across every file the stack touches (the three remaining F811 redefinitions are upstream's own). Authors preserved from the v0.5.19 stack.

miles side

  • docker/Dockerfile: SGLANG_IMAGE_TAG=v0.5.20, SGLANG_BRANCH=sglang-miles-v0.5.20 (to be switched back to sglang-miles at merge time, after sglang-miles is fast-forwarded to the v0.5.20 tip). torch 2.13.0, transformers 5.12.1, apache-tvm-ffi 0.1.11, nvidia-cutlass-dsl 4.6.2 and the /opt/sglang venv layout are unchanged between v0.5.19 and v0.5.20, so the wheel tags, the SGLANG_BUILD_RUST_EXTS=none editable install, the tvm-ffi reconcile step and the cu12 pyproject.toml sed patterns stay as they are. sglang-kernel moves 0.4.6.post1 → 0.4.7 (comes with the base image).
  • Open: cu12. Upstream v0.5.20 publishes only cu130 images (release-docker.yml matrix; the upstream Dockerfile dropped CUDA 12.9), so there is no v0.5.20-cu129 for docker/build.py's cu12-x86 variant. It is left at v0.5.19-cu129 in this PR, which after merge pairs a v0.5.19 base (sglang-kernel 0.4.6.post1) with the v0.5.20 python tree on the nightly dev-cu12 build. Either drop the variant or keep it pinned to the last cu12 stack; decision needed before merge.

Validation

  • CI on this PR (image built from this Dockerfile, sglang from sglang-miles-v0.5.20): pending, tracked below.

@Zhichenzzz Zhichenzzz added run-ci-fsdp run-ci-megatron run-ci-image run-ci-sglang run-ci-lora run-ci-true-on-policy Run dense true-on-policy E2E CI run-ci-model-scripts Run model script smoke tests run-ci-weight-update Run weight update tests run-ci-eval Run eval-labeled CI tests run-ci-multi-lora Run Tinker cookbook and multi-tenant gateway GPU e2e tests labels Sep 19, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Read the two failed stage-a-cpu jobs of the first run, traced both to API changes in sglang v0.5.20 and pushed fixes; please push back if any conclusion is off.

Run 35427410660 — stage-a-cpu red on 3 of 4 shards, all caused by v0.5.20 changes, all ours to fix here.

  1. stage-a-cpu (2) and stage-a-cpu (3): TypeError: must be called with a dataclass type or instance from dataclasses.fields(ServerArgs) (tests/fast/backends/sglang_utils/test_arguments.py::TestAllocatorOwnedServerArgs::test_the_skipped_launch_gate_port_names_a_real_server_args_field, and collection of test_server_args_utils.py). v0.5.20 turned ServerArgs into a msgspec.Struct. The same call sits in production code: the engine launch (sglang_engine.py), the P2P server-args reconstruction and the argv round-trip helper. Fixed in 95a11f6: all of them enumerate through msgspec.structs.fields; the shared argv helper dispatches on the record type because RouterArgs is still a dataclass.
  2. stage-a-cpu (1): tests/fast/examples/test_example_launcher_flags.py::test_examples_only_emit_registered_miles_flags — examples emit --sglang-nsa-decode-backend, --sglang-nsa-prefill-backend, --sglang-cuda-graph-max-bs, --sglang-cuda-graph-bs, which no longer exist: [Config] Retire get_global_server_args, and clear the deprecated flags that have a replacement sgl-project/sglang#38375 removed every deprecated alias that had a replacement. --sglang-mamba-scheduler-strategy is gone too (used by the Qwen3.5/3.6 MTP and Inkling launchers). Fixed in 03d7bcd667: mechanical rename to the canonical flags across scripts, examples, docs, e2e tests and the launch-script snapshots (116 files).

Next: new run on 03d7bcd667; GPU stages start once stage-a-cpu is green.

@Zhichenzzz
Zhichenzzz force-pushed the zhichen/bump-sglang-v0.5.20 branch from 03d7bcd to 5a2b2da Compare September 19, 2026 06:57
@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

@claude review always

Comment thread docker/Dockerfile
@Zhichenzzz Zhichenzzz added the run-on-blackwell Dispatch: move every selected test that supports Blackwell onto B200 label Sep 19, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional findings (outside the current diff — GitHub can't attach inline comments there):

  • 🔴 examples/infra_features/random_async/sglang_config_qwen3_5_35b_1p1d.yaml — This example's decode-engine override silently stops taking effect after the sglang v0.5.20 bump, so anyone running this config gets the default cuda-graph max batch size instead of 128 with no error. The yaml still sets cuda_graph_max_bs: 128 under overrides:, which flows straight into ServerArgs kwargs in miles/backends/sglang_utils/sglang_engine.py, but v0.5.20 renamed the field to cuda_graph_max_bs_decode (mirroring the --sglang-cuda-graph-max-bs -> --sglang-cuda-graph-max-bs-decode CLI rename this PR applies everywhere else). Because cuda_graph_max_bs is no longer in msgspec.structs.fields(ServerArgs), sglang_engine.py's unused_keys compat fallback (lines 180-191) silently pops it and only logs at info level. …

    Extended reasoning...

    …Fix: update every raw ServerArgs-field override (not just CLI-flag docs/scripts) to the new field name, i.e. rename this key to cuda_graph_max_bs_decode, and audit other yaml/json sglang override configs for the same stale-name-silently-dropped pattern.

    The PR's own rename sweep updated every --sglang-cuda-graph-max-bs CLI usage across docs/scripts/tests to --sglang-cuda-graph-max-bs-decode, and examples/infra_features/p2p_weight_transfer/run.py:967 emits the new flag name too. But examples/infra_features/random_async/sglang_config_qwen3_5_35b_1p1d.yaml:36 sets the raw ServerArgs field name cuda_graph_max_bs (not a CLI flag) under the decode server_group's overrides:. This dict is documented in sglang_config.py:30 as 'ServerArgs field overrides' and is merged into kwargs in sglang_engine.py's _compute_server_args via kwargs.update(sglang_overrides) (line ~178). The subsequent loop at lines 180-186 iterates msgspec.structs.fields(ServerArgs) and discards recognized names from unused_keys; since the real field is now cuda_graph_max_bs_decode, cuda_graph_max_bs…

    Verification: normal (regression introduced by this bump). examples/infra_features/random_async/sglang_config_qwen3_5_35b_1p1d.yaml:36 still sets cuda_graph_max_bs: 128 in the decode server_group overrides:. That dict flows into kwargs via sglang_engine.py:177 (kwargs.update(sglang_overrides)); then _compute_server_args iterates msgspec.structs.fields(ServerArgs) and any override key not matching a…

@Zhichenzzz

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Reports the CPU lanes going green, the three fixes since the last comment, and how I addressed the two review findings; please push back if any conclusion is off.

All CPU lanes green on 0b09c13f6d (run 35428928664): stage-a-cpu 0–3, stage-b-cpu, Run pre-commit. docker-build is running; GPU stages follow it.

Three more v0.5.20 API shifts surfaced after the first comment, each its own commit:

  1. 4bd4e8ef82 — _radix_eviction_policy_explicitly_set is a new Arg(no_cli=True) field, so the per-field CLI round-trip test needs it in the xfail registry beside _speculative_draft_quantization_explicitly_set.
  2. aa099d21d3 — test_p2p_remote_weight_infos.py stubs ServerArgs with a dataclass, which msgspec.structs.fields rejects.
  3. 0b09c13f6d — argv_utils is on the light worker entrypoint's import path, whose module footprint tests/fast/utils/workers/import_probe.py pins, so a module-scope import msgspec broke test_serve_call_argv_and_env. It now imports msgspec only on the Struct branch, leaving the entrypoint's footprint unchanged rather than widening the allowlist.

On the two review findings:

  • cu12-x86 version skew — real, and fixed in 0b09c13f6d. The variant pins a v0.5.19 base image but inherited the Dockerfile's new SGLANG_BRANCH default, so the nightly dev-cu12 build would have compiled the v0.5.20 stack against a v0.5.19 base. It now pins SGLANG_BRANCH=sglang-miles-v0.5.19-final. This is permanent, not a bump artifact: sglang publishes no cu12 image from v0.5.20 on, so the variant cannot follow the branch the cu13 images track. Whether to keep it at all is a separate call.
  • create_server_args_from_dict rejecting non-Struct stand-ins — fixed the other way round, in aa099d21d3: the test double became a Struct. In production ServerArgs is always a Struct, so teaching the helper to also accept dataclasses would exist purely to serve a test double. The type dispatch in argv_utils stays because that helper genuinely serves both records — ServerArgs (Struct) and RouterArgs (still a dataclass).

Two sweeps run to stop discovering these one CI round at a time, both clean:

  • Every --sglang-* / --eval-sglang-* flag in the repo (98 distinct) resolved against the registered options of both tags. Zero flags that v0.5.19 accepted and v0.5.20 does not; the remainder are miles-owned flags (--sglang-router-*, --sglang-server-concurrency) or prose fragments in docs.
  • Every from sglang… import X in miles/, tests/, examples/, scripts/ resolved against the new stack, and every sglang class that stopped being a dataclass in v0.5.20 checked against miles usage. No further breakage.

The CPU suite also runs clean against the v0.5.20 tree on an H200 devbox (v0.5.20 source, sgl-kernel 0.4.7), which is where these were triaged before pushing.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

@Zhichenzzz Zhichenzzz added the run-on-hopper Dispatch: run the selected tests on Hopper (H100/H200) only label Sep 19, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no issues

No high-confidence issues detected in this change.

@Zhichenzzz Zhichenzzz removed run-on-hopper Dispatch: run the selected tests on Hopper (H100/H200) only run-on-blackwell Dispatch: move every selected test that supports Blackwell onto B200 labels Sep 19, 2026
Point the image at lmsysorg/sglang:v0.5.20 and install the rebased
sglang-miles-v0.5.20 stack (54 commits on v0.5.20; the v0.5.19 stack is
frozen as sglang-miles-v0.5.19-final). torch 2.13.0, transformers 5.12.1,
apache-tvm-ffi 0.1.11 and nvidia-cutlass-dsl 4.6.2 are unchanged between the
two releases, so the wheel tags and the tvm-ffi reconcile step stay as they are.
sglang v0.5.20 turned ServerArgs from a dataclass into a msgspec Struct,
so dataclasses.fields(ServerArgs) raises TypeError. The engine launch,
the P2P server-args reconstruction and the argv round-trip check all
enumerated fields that way; the argv helper is shared with RouterArgs,
which is still a dataclass, so it dispatches on the record type.
sglang v0.5.20 (sgl-project/sglang#38375) dropped the deprecated aliases
that still had a replacement, so every launcher passing them fails argument
parsing. Rewrite the five that miles still emitted, everywhere they appear
(scripts, examples, docs, e2e tests and the launch-script snapshots):

  --sglang-nsa-prefill-backend     -> --sglang-dsa-prefill-backend
  --sglang-nsa-decode-backend      -> --sglang-dsa-decode-backend
  --sglang-mamba-scheduler-strategy -> --sglang-mamba-radix-cache-strategy
  --sglang-cuda-graph-max-bs       -> --sglang-cuda-graph-max-bs-decode
  --sglang-cuda-graph-bs           -> --sglang-cuda-graph-bs-decode
sglang v0.5.20 adds _radix_eviction_policy_explicitly_set, set during
resolution and declared Arg(no_cli=True) like the speculative-draft
quantization marker, so the per-field CLI round-trip test xfails it the
same way.
The production helper enumerates ServerArgs fields with
msgspec.structs.fields now that sglang v0.5.20 made the record a Struct,
so a dataclass stub raises TypeError before the test reaches its
assertions.
… image's sglang branch

The argv round-trip helper is on the light worker entrypoint's import path,
whose module footprint tests/fast/utils/workers/import_probe.py pins, so it
imports msgspec only on the Struct branch.

docker/build.py's cu12-x86 variant pins a v0.5.19 base image but inherited the
Dockerfile's new SGLANG_BRANCH default, which would build the v0.5.20 stack
against a v0.5.19 base. sglang publishes no cu12 image from v0.5.20 on, so the
variant is pinned to the frozen v0.5.19 branch instead.
The test pins prime==0.6.19, but prime's own requirement is
prime-sandboxes>=0.2.32 with no upper bound. prime-sandboxes 0.3.0
(2026-09-18) removed CommandRequest, which the pinned prime imports, so
'prime env install' dies with an ImportError. main's nightly has been red
on this test since that release.
@Zhichenzzz
Zhichenzzz force-pushed the zhichen/bump-sglang-v0.5.20 branch from e430bf2 to 524d09f Compare September 20, 2026 07:37

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of the three issues raised in my prior reviews, the two Dockerfile/build.py branch-pin and test-fixture issues are now fixed by the latest commits. The sglang_engine.py:181 / p2p_transfer_utils.py:182 finding (hardcoded msgspec.structs.fields(ServerArgs)) remains open and unaddressed: docker/build.py now explicitly freezes the cu12-x86 variant to sglang-miles-v0.5.19-final, whose ServerArgs is still a stdlib dataclass, so those two call sites will raise TypeError on every engine launch and P2P weight-update session built from that image. A human should confirm this before merge, since the dev-cu12 image is built automatically on every push to docker/Dockerfile and on the nightly schedule.

Extended reasoning...

Overview

This run's diff (d4137b9..HEAD) is the same sglang v0.5.20 bump PR I have reviewed six times before, now with four additional commits pushed since my last review at 2026-09-19T11:44:17Z: a fix pinning docker/build.py's cu12-x86 variant to SGLANG_BRANCH=sglang-miles-v0.5.19-final (addressing the earlier Dockerfile ABI-mismatch finding), a fix making the P2P test's fake ServerArgs a real msgspec.Struct (addressing the earlier test-fixture-mismatch finding), a fix generalizing argv_utils.py's field-enumeration helper to dispatch on dataclasses.is_dataclass() vs Struct, and an unrelated test-env change capping prime-sandboxes.

Security risks

None identified — this is version-pin and API-adaptation code with no auth, crypto, or data-exposure surface.

Level of scrutiny

High, because this PR touches the docker image build (affecting every downstream deployment) and a previously-flagged inline finding remains genuinely unresolved rather than fixed. I re-verified from the current code (not just the resolution metadata) that miles/backends/sglang_utils/sglang_engine.py:181 and miles/backends/training_utils/weight_update/protocols/p2p_transfer_utils.py:182 still call msgspec.structs.fields(ServerArgs) unconditionally, and confirmed via docker/Dockerfile's build recipe (the SGLANG_BRANCH checkout is pip install -e'd as the runtime sglang package) that the newly-frozen cu12-x86 variant will import a dataclass-based ServerArgs there, which is incompatible with the hardcoded msgspec call. This inline thread (posted 2026-09-19T11:44:17Z) is not marked resolved in the timeline, unlike the other two, so it is a live, currently-open finding — not something I am second-guessing or withdrawing.

Other factors

Per the review protocol, two of three prior findings being fixed while one remains open means I should focus this update only on what remains unresolved rather than restate resolved items or stay fully silent, and I should not approve while an unaddressed finding (mine or otherwise) is outstanding.

Still open from earlier reviews (1):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

The bump branch tracked sglang-miles-v0.5.20 while it was under review;
sgl-project's sglang-miles is fast-forwarded to that same tip (5a8da8cc3f)
as this merges, so the default branch name is correct again.
@Zhichenzzz
Zhichenzzz merged commit 12754e9 into main Sep 21, 2026
10 of 15 checks passed
@Zhichenzzz
Zhichenzzz deleted the zhichen/bump-sglang-v0.5.20 branch September 21, 2026 06:58

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review found no new issues

No new issues were found in this update; 1 finding from earlier reviews is still open above.

Still open from earlier reviews (1):

If you have decided not to act on one of these findings, resolve its thread (a reply alone leaves it open) and the next review stops counting it. To review this commit again now, use Re-run on its "Claude Code Review" check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

run-ci-eval Run eval-labeled CI tests run-ci-fsdp run-ci-image run-ci-lora run-ci-megatron run-ci-model-scripts Run model script smoke tests run-ci-multi-lora Run Tinker cookbook and multi-tenant gateway GPU e2e tests run-ci-sglang run-ci-true-on-policy Run dense true-on-policy E2E CI run-ci-weight-update Run weight update tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants