Skip to content

feat(sglang): megatron backend weight refit for sglang rollouts - #3612

Merged
yuki-97 merged 27 commits into
NVIDIA-NeMo:mainfrom
Kh4L:recut/sglang-megatron-refit
Aug 26, 2026
Merged

yuki-97 merged 27 commits into
NVIDIA-NeMo:mainfrom
Kh4L:recut/sglang-megatron-refit

Conversation

@Kh4L

@Kh4L Kh4L commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Original work: #3190 by @xiuhu17. This is a re-cut opened at his request so the change can complete review. His commits retain their original authorship and Signed-off-by trailers.

Summary

  • Routes SGLang weight refit through the weight-synchronizer interface.
  • Uses Ray CUDA IPC for colocated Megatron/FSDP2 policies.
  • Uses a trainer-rank-0 NCCL broadcast for non-colocated Megatron policies.
  • Rejects unsupported non-colocated FSDP2/SGLang during setup; support is tracked in feat: support non-colocated (broadcast) SGLang refit for the FSDP2/DTensor policy #3745.
  • Wraps bucket transfer in SGLang begin_weight_update / end_weight_update sessions so derived quantized layouts are finalized once after the last bucket.
  • Removes the legacy HTTP transport. It was live on main; this PR supersedes it with the IPC/NCCL transports.

Runtime dependencies

  • SGLang: 0.5.17.dev30+g3003d70f6 from pinned commit 3003d70f680d41c59d1b7acbf65cb47795dfd19e
  • SGLang kernel: 0.4.5
  • FlashInfer Python/cubin/CUDA-13 JIT cache: 0.6.14
  • Transformers in the SGLang environment: 5.12.1

The pinned SGLang revision contains sglang#27948, replacing the local TMS CUDA-graph patch removed here. The matching FlashInfer cubin and JIT-cache wheels are explicit so engine startup does not fall back to compiling kernels.

Dependency versions are isolated per extra: only SGLang uses Transformers 5.12.1 and TileLang 0.1.11; Megatron/vLLM stay on Transformers 5.8.1, and base/non-SGLang extras stay on TileLang 0.1.9 where the package's x86_64 marker applies. This PR no longer depends on #3498.

Review fixes included

  • Lazily initializes backend-neutral refit transport state and reuses the shared Megatron HF iterator/bucketer.
  • Guarantees generation/policy phase restoration when pause, onload, or transfer fails.
  • Declares the four SGLang policy methods on ColocatablePolicyInterface.
  • Preserves SGLang's engine-side message error text.
  • Cleans up stale Gloo subgroups and the Ray lock actor, bounds lock acquisition, and routes pause/continue through the common HTTP helper.
  • Centralizes NCCL_CUMEM_ENABLE selection before the first NCCL process group.
  • Removes stale test deselections and moves the ordering AST check to the CPU lane.

Validation

Current head: 9336ee7af808556dc884f65462a4d025f50a8f87

Local/current-head checks:

  • uv lock --check: pass.
  • Per-extra exports confirm SGLang-only Transformers/TileLang isolation.
  • Changed recipe inheritance configs pass minimize-check.
  • Relevant launchers pass Bash syntax and TEST_DRYRUN=1.
  • pytest --collect-only succeeds with all 57 weight-synchronizer tests collected.
  • Ruff/format hooks pass; Pyrefly reports no errors in the touched files.
  • Exact-head CI requested with /ok to test 9336ee7af.

HSG hardware validation used immutable image digest sha256:3ef02100ebceb15790ad0e1e32728feb3d2d144f8e6da97daf954758acc74891, built by GitLab job 411470249 from 469bd63927650b00fca5ed4399926b11f46baa21.

  • Qwen job 6518683: COMPLETED (0:0), 450/450 training steps and 450/450 successful refits, four SGLang engines, validator passed, median token_mult_prob_error=1.003821.
  • Nemotron-H/Nano job 6518682: COMPLETED (0:0), 4/4 training steps and 4/4 successful refits, positive gradients on steps 1–2, minimum 4043 valid tokens, validator passed, median token_mult_prob_error=1.015443.
  • Decode CUDA graphs remained enabled; only prefill capture was disabled. Full --disable-cuda-graph was not used.
  • Runtime preflight payload in job 6526299 printed PR3612_RUNTIME_VERSIONS_OK for the immutable image on GB200/ARM64: the SGLang environment has SGLang 0.5.17.dev30+g3003d70f6, kernel 0.4.5, FlashInfer 0.6.14, Transformers 5.12.1, TileLang 0.1.11, and Torch 2.11.0+cu130; the Megatron environment has Transformers 5.8.1, Transformer Engine 2.15.0+42b84005, and Torch 2.11.0+cu130. Megatron's x86_64-only TileLang dependency is correctly absent on ARM64. The wrapper was cancelled after the success marker when container teardown remained stuck, releasing the node.

Qwen 50-step window means from the 450-step run:

Steps Reward Loss token_mult_prob_error
1–50 0.2950 0.003241 1.003745
51–100 0.3200 0.004743 1.003776
101–150 0.3700 0.006685 1.004218
151–200 0.3225 0.009041 1.004265
201–250 0.3300 0.001671 1.004324
251–300 0.3825 0.004617 1.003998
301–350 0.3950 0.011951 1.004246
351–400 0.3700 0.003949 1.004182
401–450 0.4075 0.001219 1.003809

The only change after the hardware-tested commit is 9336ee7af, which replaces the invalid breakable prefill backend in shipped/test configs with disabled. That is the same prefill setting used by both successful HSG runs; runtime code and dependencies are unchanged.

Stack

This is the base of the linear follow-up stack:

KV-cache preservation parity is tracked separately in #3747.

@Kh4L
Kh4L requested review from a team as code owners August 12, 2026 22:47
@copy-pr-bot

copy-pr-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Kh4L Kh4L left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Self-review (/review-pr-team), per the contributor self-review policy

Ran the four reviewer roles the skill specifies — rl-expert, bug-finder, design-reviewer, test-agent — plus the mandatory devil's-advocate challenge, and the repo's own pre-commit suite.

39 raw findings → 21 CONFIRMED, 8 DOWNGRADED, 4 DISPUTED. The adversarial pass earned its place: it killed a flashinfer-cubin finding by checking PyPI (no 0.6.14 exists, so the removal from the sglang extra was forced, not a divergence), corrected two sets of wrong GPU-hour arithmetic, and upgraded one dependency finding after reading the lockfile.

Already fixed in b5356f0

  1. grpo.py raised NameError on every SGLang setup. It still referenced worker_init_timing_metrics, which main replaced with the SetupTimingMetrics dataclass. It was the only surviving reference in the file, inside the elif backend == "sglang" branch. It survived the rebase precisely because that hunk merged cleanly while its two neighbours conflicted and got fixed. Routed to setup_timing_metrics.extras, the documented overflow bucket the vLLM sparse transports already use.
  2. A failed refit left every engine paused forever. begin_weight_update() and invalidate_kv_cache() sat outside the try whose finally calls continue_generation(). Each acquired state now has its own guard, and end_weight_update() only runs for a session that actually opened.
  3. The refit driver-import guard was blind to from ...workers.megatron_policy_worker import X — ast.ImportFrom puts the module on node.module and only the symbol on alias.name. Checked against all four import spellings: the old comprehension caught 2 of 4.
  4. sglang_weight_synchronizer.py was missing from pyrefly.toml, so the largest new module in the PR escaped type-checking.
  5. pre-commit hygiene the PR shipped red: taplo formatting and three unminimized recipe YAMLs.

Worth stating plainly: the only guaranteed crash was found by pre-commit, not by any of the four agents. That is an argument for running the real linter as part of the review rather than trusting the agent pass alone.

Environment caveats

  • No GPU on the review machine, so nothing runtime-dependent was executed; every such finding is marked unverified rather than asserted. A GPU re-run would strengthen the test findings.
  • CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1 was not set, so the roles ran without inter-agent messaging. Devil's-advocate verified findings itself instead of interrogating the reporting agent across its usual two challenge rounds.

The five comments below are what survived and is not fixed — left for the reviewer's judgement rather than changed unilaterally.

Generated by Claude Code

Comment thread nemo_rl/models/policy/utils.py Resolved
Comment thread nemo_rl/models/policy/utils.py Resolved
Comment thread pyproject.toml Resolved
@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from b5356f0 to 6cc4448 Compare August 14, 2026 19:14
@Kh4L

Kh4L commented Aug 14, 2026

Copy link
Copy Markdown
Contributor Author

Addendum to the self-review above — a rationale in this PR's own commit message is wrong, and it hides how big the change is.

refactor(sglang): route every SGLang refit through the weight synchronizer (now 7c78def after today's rebase) deletes nemo_rl/weight_sync/http_weight_synchronizer.py, justifying it as:

Also drop http_weight_synchronizer. It was dead (never constructed) and broken: policy.stream_weights_via_http was removed in 7b0f0bd along with stream_weights_via_http_impl.

Both halves are false against main:

  • It is constructed — for every SGLang run. nemo_rl/weight_sync/factory.py on main reads if generation_backend == SGLANG_BACKEND: ... return HTTPWeightSynchronizer(...). That is the current shipped SGLang weight-sync path, not dead code.
  • stream_weights_via_http is alive on main. It is called at nemo_rl/algorithms/grpo.py:2362, declared on PolicyInterface, implemented on lm_policy and dtensor_policy_worker_v2, and backed by stream_weights_via_http_impl in nemo_rl/models/policy/utils.py. That impl was added in feat: Add SGLang rollout backend and tests #1674 (2026-01-22) and the only commit that has ever touched the symbol is the one that added it.
  • 7b0f0bd2 is not on main. It is a commit on this PR's own branch. So the reasoning is circular: this stack removed the HTTP path, and then removed the synchronizer because "it was removed in 7b0f0bd".

To be clear about what I am not saying: the deletion itself is correct and is the point of the PR. Replacing the HTTP transport with the IPC/broadcast synchronizers is exactly what this change is for, it is done coherently (no dangling imports — factory.py has no HTTP branch left), and I am not asking for it to be reverted.

What I am asking for is that the PR stop describing it as removing dead code. A maintainer who reads "it was dead (never constructed)" will not scrutinize what is in fact a swap of the live SGLang weight-transfer transport, and the PR body doesn't mention the swap at all. Anyone currently on SGLang gets a different transport after this merges.

Action: reword that paragraph of the commit body to say it replaces the transport rather than removing dead code, and add a line to the PR description noting the behavioural change for existing SGLang users.

Found while rebasing the stack onto current main — this is also the reason all four PRs went CONFLICTING today (upstream #3618 added tests to tests/unit/weight_sync/test_weight_synchronizer.py, the same file this change edits). All four are rebased and MERGEABLE again as of 6cc4448.

Generated by Claude Code

@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from 1d5b657 to b30bb85 Compare August 17, 2026 17:32

@yuki-97 yuki-97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-cut of #3190, so I re-checked all 34 of my review threads there against this tree. This review is the #3190 carry-over only — comments on the new code will follow separately.

Reuse of the vLLM / TRT-LLM transfer functions — settled offline, no action needed. SGLang's refit differs enough that it can't easily reuse them as-is: it runs its own Ray CUDA-IPC NCCL group, needs an HF-weight conversion step and SGL-specific begin/apply/commit hooks, and has different pause / KV-cache / recovery semantics after a partial update. So the three threads asking for reuse (r3689520162, r3689584423, r3689651312) are not carried over here; please treat them as closed.

Carried over from #3190, details inline: 4 unresolved threads that still apply to this tree, and 2 asks that had no natural line to sit on, anchored to the nearest relevant file.

Everything else is either the reuse family above or genuinely addressed here.

Comment thread nemo_rl/models/megatron/setup.py Resolved
Comment thread nemo_rl/weight_sync/factory.py Resolved
Comment thread nemo_rl/weight_sync/factory.py Resolved
Comment thread nemo_rl/models/megatron/setup.py Resolved
Comment thread nemo_rl/models/automodel/setup.py Resolved
Comment thread nemo_rl/weight_sync/factory.py Resolved
@yuki-97 yuki-97 added the CI:L1 Run doctests, unit tests, and functional tests label Aug 18, 2026
@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from b30bb85 to 8cd3a35 Compare August 18, 2026 19:24
@Kh4L
Kh4L requested a review from a team as a code owner August 18, 2026 19:24
@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from 8cd3a35 to b86aa8e Compare August 18, 2026 19:29
@github-actions github-actions Bot added the Documentation Improvements or additions to documentation label Aug 18, 2026

@yuki-97 yuki-97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Follow-up round on the re-cut. The fix commits stacked on top of the original seven (dffdc88b9 through b86aa8e4b) address 16 of the 20 open threads, which are now resolved.

Still open:

  • quantization schema — declaring the TypedDict is good, but see my reply: let's keep .get("scheme", "bf16") rather than adding get_sglang_quantization_scheme.
  • NCCL_CUMEM_ENABLE placement — see my reply on megatron/setup.py.
  • Convergence evidence (#3612 (comment)) — the 2n8g non-colocated nightly is back at 450 steps / 150 min, but the description still says this tree has not been run on hardware. A reward + loss + token_mult_prob_error curve from a longer run would close it.

The rest is inline.

Two things this review does not cover: no CI has run on this tree, and every shipped SGLang recipe is PP=1, so the multi-rank bucket path is not exercised by anything in the suite.

Comment thread nemo_rl/models/megatron/setup.py Resolved
Comment thread nemo_rl/models/automodel/setup.py Resolved
Comment thread nemo_rl/algorithms/grpo.py Resolved
@yuki-97

yuki-97 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

/ok to test b86aa8e

@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from b86aa8e to f4d36ef Compare August 21, 2026 00:51
@Kh4L

Kh4L commented Aug 21, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f4d36ef

@terrykong terrykong left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Team review of the SGLang Megatron refit path, run across six agents with an adversarial verification pass (12 candidate findings were dropped as unreachable, pre-existing, or already covered by a test). Reviewed at b86aa8e; swept against the rebased head f4d36ef — comments below say individually what that push already fixed.

On structure — this does follow the weight-synchronizer pattern, and it is the right direction. The PR does not add a parallel mechanism: it replaces HTTPWeightSynchronizer, which was already one. The two new classes subclass the same ABC, are built by create_weight_synchronizer, use the same timer key as all five siblings, mirror IPCWeightSynchronizer's is_stale semantics and buffer sizing, add their tests to the existing test_weight_synchronizer.py, and delete the SGLang isinstance branches that used to live inside refit_policy_generation. Two composition asks remain on the Megatron worker (constructor block, duplicated iterator) — both have small in-PR fixes spelled out inline, with the longer-term shape sketched separately.

Status after the f4d36ef push: the 9 pyrefly errors are fixed and the rebase cleared the staleness gate, but no head of this PR has ever produced test evidence — the only /ok to test died at lint. main has moved again (2 commits) and GitHub currently reports the branch CONFLICTING, so it likely needs one more rebase before a meaningful CI run.

Dependency changes in this PR, at a glance

Verified against uv.lock on this branch vs main (unchanged by the rebase — pyproject.toml is byte-identical between the two heads):

package main this PR
transformers base range >=5.5.0,<5.9.0 >=5.5.0,<5.13.0
transformers sglang extra ==5.6.0 ==5.12.1
transformers resolved for the mcore venv 5.8.1 5.12.1
transformers resolved for automodel/fsdp 5.5.0 5.5.0 (unchanged)
sglang ==0.5.12.post1 (PyPI release) git rev 3003d70f, unreleased sglang-miles branch
sglang-kernel 0.4.2.post2 0.4.5
kernels >=0.12.0,<0.13 >=0.14.1,<0.15
flashinfer-python 0.6.11.post1 0.6.14
flashinfer-cubin / flashinfer-jit-cache 0.6.11.post1 deleted
llguidance override >=1.3.0,<1.4.0 >=1.3.0 (cap dropped)

⚠️ Risk we want a decision on before merge (cc @yuki-97): the transformers bump is not contained to sglang. The lock resolves 5.12.1 for the mcore extra on its own, so every Megatron training venv moves 5.8.1 → 5.12.1 — four minor versions — in a PR whose test plan only covers the sglang recipes. The new comment in policy/__init__.py claims "the driver and policy venvs stay on 5.5.x", which the lock contradicts. Resolution: this PR should wait for #3498 to land and rebase on it. That PR (Automodel r0.6.0) makes the same <5.9.0 → <5.13.0 move with an actual transformers-5.12 migration and its own test surface, so the bump arrives already exercised and a transformers regression bisects to one PR instead of two upgrades landing together.

Findings already raised in @yuki-97's open threads (weight_transfer_mode — since deleted outright at f4d36ef — the vLLM pause_generation stubs, and the automodel NCCL_CUMEM_ENABLE guard) are deliberately not duplicated here; our independent checks agreed with all three.

The remaining comments, in severity order: CI status, dependency blast radius, two try/finally placement issues, the ColocatablePolicyInterface regression, setup-time vs refit-time validation, an error-message regression against main, test coverage for the new pure helpers, two stale --deselect lines, the two composition asks on the Megatron worker, and a bundled block of low-severity nits. All were re-verified against f4d36ef; each one still present at that head says so.

Generated by Claude Code

Comment thread nemo_rl/models/policy/utils.py Resolved
Comment thread pyproject.toml Resolved
Comment thread nemo_rl/models/policy/lm_policy.py Resolved
Comment thread nemo_rl/models/megatron/setup.py Resolved
Kh4L pushed a commit to Kh4L/NemoRL that referenced this pull request Aug 24, 2026
…he shared HF iterator

Demonstrates the review asks on NVIDIA-NeMo#3612:

1. Both policy-worker constructors lose their generation-backend branch.
   Transport state lives in a base-owned per-transport dict created on
   first use (AbstractPolicyWorker._refit_transport_state), and the torch
   reductions monkey patch installs inside connect_sglang_rollout_engines
   -- the same lazy shape as maybe_init_zmq on the ZMQ path.

2. megatron_sglang_weight_iterator.py is deleted. The Megatron SGLang
   path now composes the same two pieces every other transport uses:
   _iter_params_with_optional_kv_scales (with include_draft=False;
   SGLang keeps draft weights engine-side) and the shared
   iter_named_tensor_buckets packing -- identical in shape to the FSDP2
   SGLang path.

Also aligns weight-version bookkeeping with the DTensor path (the IPC
impl already tracks it in worker_state).

Signed-off-by: Terry Kong <terryk@nvidia.com>
@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from f4d36ef to f322100 Compare August 24, 2026 16:58
@Kh4L

Kh4L commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test f322100

Kh4L and others added 12 commits August 25, 2026 20:13
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
…he shared HF iterator

Demonstrates the review asks on NVIDIA-NeMo#3612:

1. Both policy-worker constructors lose their generation-backend branch.
   Transport state lives in a base-owned per-transport dict created on
   first use (AbstractPolicyWorker._refit_transport_state), and the torch
   reductions monkey patch installs inside connect_sglang_rollout_engines
   -- the same lazy shape as maybe_init_zmq on the ZMQ path.

2. megatron_sglang_weight_iterator.py is deleted. The Megatron SGLang
   path now composes the same two pieces every other transport uses:
   _iter_params_with_optional_kv_scales (with include_draft=False;
   SGLang keeps draft weights engine-side) and the shared
   iter_named_tensor_buckets packing -- identical in shape to the FSDP2
   SGLang path.

Also aligns weight-version bookkeeping with the DTensor path (the IPC
impl already tracks it in worker_state).

Signed-off-by: Terry Kong <terryk@nvidia.com>
…plicit weight version

- _refit_transport_state now uses a plain hasattr guard instead of
  __dict__.setdefault; identical behavior, readable at a glance.
- The colocated path keeps passing weight_version explicitly (now
  stored in the transport dict), so the demo changes nothing beyond
  the two review asks.

Signed-off-by: Terry Kong <terryk@nvidia.com>
Harden refit cleanup and setup validation, expose the policy interface, clean up lock and subgroup lifecycles, and install matching FlashInfer artifacts.

Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
Signed-off-by: Serge Panev <spanev@nvidia.com>
The pinned SGLang weight updater rejects tensor updates outside an explicit begin/end session. Wrap both real CUDA-IPC round-trip streams in that lifecycle so the tests exercise the same engine contract as the production synchronizer.

Signed-off-by: Serge Panev <spanev@nvidia.com>
@Kh4L
Kh4L force-pushed the recut/sglang-megatron-refit branch from 9336ee7 to 3b75899 Compare August 26, 2026 03:24
@Kh4L

Kh4L commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

/ok to test 3b75899

@yuki-97 yuki-97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-checked the four threads from my last pass at 3b75899 — all four look resolved. One follow-up on cc8a54e below.

Signed-off-by: Yuki Huang <yukih@nvidia.com>
@yuki-97 yuki-97 added CI:docs Run doctest and removed CI:L1 Run doctests, unit tests, and functional tests labels Aug 26, 2026
@yuki-97

yuki-97 commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

/ok to test 09c4efc

@yuki-97 yuki-97 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CI:L1 passed in 3b75899, after that 09c4efc only changes the comments. so changes to CI:docs to merge.

@yuki-97
yuki-97 enabled auto-merge (squash) August 26, 2026 13:42
@yuki-97
yuki-97 merged commit cfd9081 into NVIDIA-NeMo:main Aug 26, 2026
45 checks passed
asolergi-nv added a commit that referenced this pull request Aug 27, 2026
14 upstream commits; four of them produced seven conflicts. Each resolution below.

#3612 feat(sglang): megatron backend weight refit for sglang rollouts
  - weight_sync/factory.py: it rewrote the train_cluster/inference_cluster/
    refit_buffer_size_gb docstrings (SGLang owns its own process group, so it needs
    neither cluster handle). Took its wording and kept our refit_timeout_s entry, which
    it never saw. refit_timeout_s still reaches NcclReshardWeightSynchronizer and
    CollectiveWeightSynchronizer; the new SGLang synchronizer does not take it, which is
    correct -- our watchdog bounds a JOINT communicator and SGLang does not build one.
  - base_policy_worker.py: it added _refit_transport_state and
    connect_sglang_rollout_engines at the same insertion point as our
    stand_down_refit_watchdog. Disjoint additions; kept both.
  - pyrefly.toml: it swapped http_weight_synchronizer for sglang_weight_synchronizer.
    Corroborated by the merge deleting http_weight_synchronizer.py outright.

#3773 feat(sc): support PPO in single controller
  - single_controller_utils/setup.py: the SC path is no longer GRPO-only, so it renamed
    grpo_config to algo_cfg. Kept our nccl_reshard precondition guard and applied the
    rename to the val-period line inside it; grpo_config no longer appears anywhere.
  - L1_Functional_Tests_SingleController.sh: it added a ppo_async run_test and padded
    every non-fast entry to align with "run_test fast". Kept our annotation -- it says
    which of skip-vs-pass a green lane actually means, which its one-line version does
    not -- and adopted the alignment, including on our seven recovery entries, so the
    file does not end up half-converted.
  - pyrefly.toml: it re-sorted the list, moving vllm_remote_sparse_weight_synchronizer to
    its correct alphabetical slot. Our side had added membership.py AND held that entry in
    the old position, so taking our block verbatim would have duplicated it. Kept
    membership.py only; verified the result is sorted and has no duplicates.

#3545 fix(vllm): support native BF16 FlashInfer TRTLLM refit
  - vllm_backend.py: its _nrl_layerwise_reload_* class attributes landed where our
    model_update_group declaration is. Disjoint; kept both.
  - tests/unit/models/generation/test_vllm_backend.py: its layerwise-reload suite against
    our init_collective release tests plus the _RecordingGroup fixture. Disjoint; kept
    both. 53 tests collect.

#3768 feat: add MOPD to single-controller text path
  - Touched setup.py alongside #3773; no separate resolution needed.

Submodule: the merge advances Megatron-Bridge to d352aced (#3824). Verified the STAGED
pointer is upstream's and not our stale 8c46dc42 -- staging the local one is what breaks
the fast-forward check and `uv lock --check` together. Gym is untouched by the merge.

Verified after resolving: no conflict markers remain, all four lint hooks clean (the one
pyrefly error is the pre-existing unrelated transfer_queue import), and 841 unit tests pass
across single_controller, refit_watchdog, worker_refit_signatures and weight_sync -- up
from 725, because #3773 brings a large new SC suite that passes alongside ours.

Signed-off-by: asolergibert <asolergibert@nvidia.com>
yuekaizhang added a commit to yuekaizhang/RL that referenced this pull request Sep 9, 2026
- vllm_worker.py/vllm_backend.py: gate the draft module-sharing
  disable and the strict-cotraining/full-manifest-validation paths on
  the actual full-vs-partial stream signal (_draft_full_refit config
  flag / embed_tokens key presence via the renamed
  _is_full_draft_stream), not on the dspark/dflash method name --
  method names alone don't imply a DTensor-v2 full stream, so a future
  headless Megatron block-drafter using the same method name would
  otherwise hit a spurious "missing draft keys" refit failure.
- integration.py: shift input_ids/teacher_logits (and
  next_token_position_mask) left by one in Eagle3Runtime.compute_loss
  so the TTT forward trains h[t] + token[t+1] -> teacher_logits[t+1],
  matching vLLM's eagle proposer contract (previously trained the
  easier, misaligned h[t] + token[t] -> teacher_logits[t] task, which
  would look healthy in the loss curve while degrading refit
  acceptance).
- algorithms/utils.py: move finalize_draft_ratio_metrics out of the
  nemo_automodel-gated draft/integration.py into this dependency-free
  module and call it directly (no more try/except ImportError) --
  driver processes essentially never have nemo_automodel installed,
  so this was silently no-op'ing and leaving raw *_num/*_den keys in
  wandb instead of the finalized ratios. Also anchor the regex to
  draft_* so it can't rewrite unrelated *_num/*_den metric pairs.
- dtensor_policy_worker_v2.py: drop the dead stream_weights_via_http
  method (its stream_weights_via_http_impl callee was removed from
  utils.py in NVIDIA-NeMo#3612; this would ImportError the moment anything called
  it).
- policy/__init__.py, lm_policy.py, automodel/setup.py,
  dtensor_policy_worker_v2.py: drop DEFAULT_DRAFT_ALGO and read
  policy.draft.algo as a required key once draft is known to be
  enabled, instead of the forbidden cfg.get(key, MODULE_CONSTANT)
  pattern at 3 call sites (config-conventions) -- the exemplar YAML
  already supplies the default for every shipped recipe. Added the
  missing algo field to ppo_math_1B_megatron_single_controller.yaml's
  (disabled) draft block for consistency.
- automodel/setup.py, dtensor_policy_worker_v2.py: hoist the
  Eagle3DraftOptions/DSparkDraftOptions imports to module top (both
  files already import nemo_rl.models.policy there; no circular-import
  or heavy-dep reason to defer them).
- eagle3_llama.py: drop the unused (production-dead, only
  test-exercised) _kl_div_per_position checkpointed-chunking wrapper
  and correct the header/docstring claim -- the TTT loop's actual
  per-chunk compute_logits+KL loop caps the transient peak but, since
  no chunk is checkpointed, does not reduce the memory retained for
  backward across the step.
- dtensor_policy_worker_v2.py: fix a backwards comment about which
  path (sequence packing, not dynamic batching) can make microbatch
  counts differ across DP ranks.
- Test coverage: new parametrized table test for
  _draft_module_sharing_disable_required (all 3 cotrained methods x
  full/partial stream x load_format), rewrote the KL chunking test
  against the actual production chunking pattern instead of the
  deleted wrapper, and moved the ratio-metrics tests to
  tests/unit/algorithms/ (no longer nemo_automodel-gated).

Not addressed here: the lm_policy.py rebase conflict (main replaced
the blanket sequence-packing guard with Megatron-scoped guards in

Signed-off-by: Yuekai Zhang <zhangyuekai@foxmail.com>
NVIDIA-NeMo#3463) and the cross-PR config-schema/nightly-budget discussion with
NVIDIA-NeMo#3712/NVIDIA-NeMo#3713, both left for separate follow-up.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI:docs Run doctest Documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants