Conversation
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit 717ee4a)
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit 03b9ee2)
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit c759c71)
Four independent dependency problems surfaced when running the nightly
suites on the vllm 0.28.0 / torch 2.13.0 bump. All of them are in the
resolved environment rather than in vLLM itself.
* cutlass-dsl: stop excluding `nvidia-cutlass-dsl-libs-base`. The exclusion
worked around cutlass#3259 (4.5.x libs-base and libs-cu13 overwrite 180
shared files, 99 with different content). At 4.6.x the wheels are split
cleanly: libs-base carries the whole Python `cutlass` DSL and libs-cu13
only the CUDA-13 `_cutlass_ir` extension, with zero overlapping files.
flashinfer 0.6.16's `gdn_prefill` imports `cutlass.cute` at module load
and vLLM 0.28 imports `gdn_prefill` eagerly, so with libs-base excluded
every Qwen3.5 worker and every Blackwell vLLM worker died with
`ModuleNotFoundError: No module named 'cutlass.cute'`. The mcore extra's
own `nvidia-cutlass-dsl==4.5.2` pin moves to 4.6.2 so the training venv
does not pick up the overlapping 4.5.x pair (flashinfer 0.6.8.post1
accepts >=4.4.2). The trtllm fork still resolves 4.5.0 through
tensorrt-llm; that venv is already unusable on torch 2.13 (see the PR).
* flash-attn: bound its source build with `MAX_JOBS=4`. No flash-attn 2.8.x
wheel exists for torch >= 2.11, so the bump switched both architectures
to an sdist build. flash-attn's setup.py sizes MAX_JOBS from the host's
free memory (not the cgroup) and cpu_count()//2 and runs nvcc with
--threads 4, which OOM-killed cicc on every nemo-ci runner
(`ResourceExhausted`). 4x4 built cleanly on both architectures.
* nvidia-modelopt: c3b913b9 -> 613e5e8b. The old vLLM plugin registers a
quant module on `fused_moe.layer.FusedMoE`, which 0.28 removed. ModelOpt
swallows the AttributeError inside `import_plugin`, leaving the linear
classes registered but the module absent from sys.modules; NeMo-RL's
explicit re-import in `vllm_quant_patch.py` then re-runs the decorators
and fails with `RowParallelLinear already registered`, killing every
fakequant nvfp4 test. 5dde396bd (Aug 4) ported the plugin to
`RoutedExperts`.
* numpy: cap the override at `<2.5`. vLLM 0.28 pins `numba==0.65.0`, whose
metadata requires `numpy<2.5`; the `numpy>=2.1.0` override forced 2.5.1
past it and numba refused to import in the vLLM venv ("Numba needs NumPy
2.4 or less"). Resolves to 2.4.6, still above tensorrt-llm's `<2.4` cap.
Lock diff beyond marker churn: numpy 2.5.1 -> 2.4.6, cutlass-dsl 4.5.2
dropped, libs-base 4.5.0/4.6.0/4.6.2 and libs-core 4.6.0/4.6.2 added,
modelopt 0.46.0.dev86 -> 0.47.0rc1.dev36.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
(cherry picked from commit 5ae0cee)
… vLLM 0.28 Two vLLM-internal APIs that the fp8 rollout integration relies on changed between 0.25.1 and 0.28.0. Both broke at worker construction on the nightlies and were misreported by the error extractor as import errors. * `make_fp8_moe_kernel` (now in `fused_moe/oracle/fp8.py`, still re-exported from `quantization/fp8.py`) dropped its `layer` kwarg; 0.25 only forwarded it to the FlashInfer TRTLLM experts. Passing it raised `TypeError: make_fp8_moe_kernel() got an unexpected keyword argument 'layer'` in every fp8 MoE test (qwen3.5-35B ep16tp2_fp8, moonlight fp8_e2e). * The attention layer no longer carries `calculate_kv_scales`; dynamic scales are a KV-cache dtype now (`kv_cache_uses_per_token_head_scales`) and the static-scale branch keys off `is_quantized_kv_cache`. Our refit-friendly copy of `BaseKVCacheMethod.process_weights_after_loading` failed with `AttributeError: 'Attention' object has no attribute 'calculate_kv_scales'` (qwen3-8b fp8_kvcache). Re-mirror upstream 0.28: per-token-head dtypes short-circuit, the fp8 branch uses `is_quantized_kv_cache`, and the host `_k_scale_cpu`/`_v_scale_cpu` copies are refreshed on refit. Parameters are still not deleted so refit can keep updating them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit 8318c80)
vLLM 0.28 (vllm-project/vllm#50411) calls the HF image processor with `do_rescale=False, do_normalize=False` and re-applies both on the GPU in the vision tower's dtype. The policy side normalizes the same images on the CPU in fp32 through the same processor, and the nightly token_mult_prob_error / gen_kl_error checks assume identical inputs on both sides, so default `mm_device_do_normalize` to False and keep the 0.25 path. Verified on CPU with transformers 5.12.1 that the fused path is exact in fp32 but differs by up to 0.019 (about 1.7% of the pixel std) in bf16; upstream has also already shipped a silent-corruption fix for the device path (vllm-project/vllm#55370). Users can opt back in via policy.generation.vllm_kwargs.mm_device_do_normalize=true. This is a parity fix, not a confirmed root cause: on the 0.28 nightlies both Qwen2.5-VL-3B recipes (DTensor and Megatron) show token_mult_prob_error of 2e2-2e4 against 1.02 on main with reward collapsing to 0.3, and the same-image-path Qwen2.5-Omni audio recipe is unaffected. See the PR description for the open investigation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit 170beee)
… find it
nemo-ci runs `docker buildx rm` / `docker buildx create` around every build
job, so the `trtllm-wheel-cache-*` cache mount lives exactly as long as one
build. The hermetic layer that compiles the wheel into that mount is shared
through the registry cache, though, so a rebuild that changes only NeMo-RL
source reuses the layer on a fresh builder, re-executes the release stage,
and dies at `[release 5/8]` with
RuntimeError: TRT-LLM cached wheel is required but was not found at
/root/.cache/trtllm-wheels/<key>. Refusing to compile TRT-LLM because
TRTLLM_REQUIRE_CACHED_WHEEL=1.
on every runner (PR #4080 pass 2: 4 images x 3 attempts, all identical).
Retrying cannot help because no runner keeps the mount between jobs.
Have the hermetic stage set TRTLLM_WHEEL_CACHE_MIRROR_DIR so the backend
also writes the content-addressed wheel into /opt/trtllm_wheels inside the
layer, and have the release stage rsync that mirror back into the (possibly
empty) mount before the trtllm venv prefetch. The backend skips the mirror
copy when the file already exists, so the release layer no longer
duplicates the wheel it inherits from hermetic; the final image content is
unchanged (/opt/trtllm_wheels already held this wheel).
This changes the hermetic TRT-LLM step, so the next build recompiles the
wheel once; after that any source-only rebuild works on any runner.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 037c678 |
The second nightly pass showed that ModelOpt 613e5e8b (main) breaks the Megatron side of the nvfp4 recipes while fixing the vLLM side: * fakequant (`grpo-qwen3-30ba3b-4n8g-megatron-qa-nvfp4`): `KeyError: GroupedQuantizer(...)` from Megatron-LM's `DistributedDataParallel.disable_forward_pre_hook`, reached through `MegatronQuantPolicyWorker.__init__`. The per-expert `GroupedQuantizer` modules are new in ModelOpt #1550 (2ded5aba3, 2026-08-07) and are not in the DDP hook bookkeeping NeMo-RL iterates. * real-quant (`grpo-nanov3-30ba3b-4n4g-megatron-qa-nvfp4-w4a16-real`): `CheckpointingException: Invalid access pattern for ShardedTensor(key='decoder.layers.1.mlp.experts.experts.16.linear_fc1.weight_quantizer._amax')` while saving the imported Megatron checkpoint; same per-expert quantizer layout. 5dde396bd (2026-08-04, "Fix vLLM 0.24+ compatibility") is the first commit whose vLLM plugin survives vLLM 0.28 (the `FusedMoE` registration is behind `_has_fused_moe_cls`) and it predates #1550 by 17 commits. Lock moves nvidia-modelopt 0.47.0rc1.dev36 -> 0.47.0.dev25; nothing else changes. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit e776e5820b1e32a39bba88997dd2bbaa6e71f8d8)
…ert loader On the second nightly pass the fakequant nvfp4 recipes got past worker construction and then failed at the first refit with IPCWeightManifestError: weight load failed: AttributeError: Layer model.layers.1.mixer.experts has no parameter 'w13_input_quantizer._amax' The Megatron side exports one `input_quantizer._amax` per expert projection (`...experts.16.up_proj.input_quantizer._amax`) and the vLLM-side ModelOpt MoE module owns a single fused `w13_input_quantizer` / `w2_input_quantizer`, so the values fan in with `max`. vLLM 0.25 loaded those names through the model-level parameter dict, which `_patch_named_parameters_to_include_buffers` extends with quantizer buffers. vLLM 0.28's `AutoWeightsLoader` hands every `experts.*` name to `RoutedExperts.load_weights` (`vllm/model_executor/layers/fused_moe/routed_experts.py`), which rewrites the name with the expert mapping (`experts.16.up_proj.` -> `experts.w13_`), strips the layer prefix and resolves the remainder with a single `getattr(self, param_name)`. A dotted buffer path cannot resolve that way. Apply the same rewrite ourselves, walk the dotted path on the expert module, fan the values in with `max`, and hand vLLM only the remaining weights. The routing keys off `get_expert_mapping` + `layer_name`, the two attributes vLLM's loader itself relies on, so it follows whatever prefix a model passes (`routed_experts_prefix` or none). Non-MoE amax buffers (dense layers, attention K/V) still take the existing path. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com> (cherry picked from commit e7c1848282e399b8820b13866bc1d42534a6252e)
vLLM 0.29.0 keeps the same torch 2.13.0 / CUDA 13 stack as 0.28.0; the only dependency changes are flashinfer-python/-cubin/-jit-cache 0.6.16.post3 → 0.6.18 (cu130 wheels for both arches) and a new unconditional `instanttensor>=0.1.9` requirement. instanttensor is the optional `load_format="instanttensor"` safetensors loader, only publishes x86_64 wheels, and its sdist compiles libaio/liburing/boost; vLLM imports it lazily and NeMo-RL never selects that load format, so an override keeps the wheel on x86_64 and skips it on aarch64 (GB200) instead of building it from source. `uv lock --check` passes on the pristine parent and on the new lock; the lock diff is limited to vllm, the three flashinfer packages and the new instanttensor entry. transformers stays at the already-locked 5.12.1 (0.29 requires >=5.10.4). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
vLLM 0.29 ships vllm-project/vllm#53666 and #50969: RayWorkerV2 now binds the torch.distributed TCPStore itself on a kernel-assigned port and holds the socket (`self._dist_init_store = store`) until init_process_group reuses it, so the probe/bind window that let the MessageQueue take the same port is gone, and `_select_tcpstore_port` no longer exists. `_patch_vllm_ray_executor_v2_tcpstore_port` therefore found no anchor and logged "may fail with EADDRINUSE" on every worker start. Detect the upstream marker, log at info level and leave the file alone. The port-arithmetic tests skip when the installed vLLM carries the upstream fix; a new test pins the no-warn/no-edit behaviour against a synthetic 0.29-style source. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…e extension vLLM 0.29 adds `WorkerBase.synchronize_device` (vllm-project/vllm#52914, used by the pause-completion path) and `WorkerBase.init_worker` asserts that a worker extension never carries an attribute the Worker already has. `VllmInternalWorkerExtension.synchronize_device`, which the sparse-delta refit invokes through `collective_rpc` to drain peers before cleaning up staged batch files, therefore killed every vLLM engine at init on the first 0.29 nightly: AssertionError: Worker class <class 'vllm.v1.worker.gpu_worker.Worker'> already has an attribute synchronize_device, which conflicts with the worker extension class <class '...VllmInternalWorkerExtension'>. Rename the extension method to `synchronize_sparse_refit_device` (the applier's own `synchronize_device` is untouched; it is not on the worker class) and update the RPC caller and its test. Add a `vllm`-marked unit test that mirrors vLLM's collision predicate over both extension classes so the next upstream name clash fails in CI instead of on a GPU job. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
… find it
nemo-ci runs `docker buildx rm` / `docker buildx create` around every build
job, so the `trtllm-wheel-cache-*` cache mount lives exactly as long as one
build. The hermetic layer that compiles the wheel into that mount is shared
through the registry cache, though, so a rebuild that changes only NeMo-RL
source reuses the layer on a fresh builder, re-executes the release stage,
and dies at `[release 5/8]` with
RuntimeError: TRT-LLM cached wheel is required but was not found at
/root/.cache/trtllm-wheels/<key>. Refusing to compile TRT-LLM because
TRTLLM_REQUIRE_CACHED_WHEEL=1.
on every runner (PR #4080 pass 2: 4 images x 3 attempts, all identical).
Retrying cannot help because no runner keeps the mount between jobs.
Have the hermetic stage set TRTLLM_WHEEL_CACHE_MIRROR_DIR so the backend
also writes the content-addressed wheel into /opt/trtllm_wheels inside the
layer, and have the release stage rsync that mirror back into the (possibly
empty) mount before the trtllm venv prefetch. The backend skips the mirror
copy when the file already exists, so the release layer no longer
duplicates the wheel it inherits from hermetic; the final image content is
unchanged (/opt/trtllm_wheels already held this wheel).
This changes the hermetic TRT-LLM step, so the next build recompiles the
wheel once; after that any source-only rebuild works on any runner.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
vLLM 0.29 moved `vllm/entrypoints/openai/engine/protocol.py` to `vllm/entrypoints/serve/engine/protocol.py` (vllm-project/vllm#54492). `VllmAsyncGenerationWorker.post_init_async` imported `ErrorResponse` from the old path when bringing up the OpenAI-compatible server, so every recipe that serves generation over HTTP (Gym / swe1 recipes) died on the first 0.29 nightly with ModuleNotFoundError: No module named 'vllm.entrypoints.openai.engine' Every other name the async worker imports from vLLM still resolves in 0.29.0, and the serving classes it subclasses changed only for this move. Add a `vllm`-marked test that walks `nemo_rl/models/generation/vllm` with `ast`, collects every `from vllm... import X` (they are mostly lazy, method-local imports that no unit test executes) and resolves them against the installed vLLM, so the next upstream module move fails in CI instead of in a 16-node nightly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
vLLM 0.29's AutoWeightsLoader (vllm-project/vllm#51665) skips a tied alias such as `lm_head.weight` and then asserts that its canonical `model.embed_tokens.weight` was loaded in the *same* load_weights call: ValueError: 'lm_head.weight' was skipped because it is tied to 'model.embed_tokens.weight' in Qwen2ForCausalLM, but 'model.embed_tokens.weight' was not found in the checkpoint, so the tied weight is uninitialized. Refit streams weights in transport-sized batches, so the two routinely land in different calls and every tied-embedding model refit from a DTensor/automodel policy died at its first weight update on the first 0.29 nightly (Qwen2.5-1.5B PPO, Gemma 4 E2B DAPO, ...). The alias never loads anything (the loader skips it), so drop it before the call. Use vLLM's own `_get_tied_embedding_params` so the alias set is exactly what the loader skips, and map checkpoint names through the model's `hf_to_vllm_mapper` first, as the loader does, so models whose vLLM parameter names differ from the checkpoint (Gemma) are covered. Applied to both the batched IPC/NCCL refit and the native reload_weights path; the MTP drafter still receives the unfiltered policy stream. On a vLLM without the helper the filter is a no-op, and that vLLM has no alias check either. A vllm-marked test pins the helper's existence so an upstream rename cannot silently turn the filter off. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…LLM >= 0.28
The nanov3 w4a16 real-quant recipe has failed since the vLLM 0.28 bump with
ModelOpt layerwise reload is incomplete for 23 layer(s):
model.layers.1.mixer.experts.routed_experts: 134701312/179601664 elements
i.e. exactly three quarters of every expert layer arriving, on 0.28 and 0.29
alike. Nemotron-H experts are non-gated (`ckpt_gate_proj_name="up_proj"`,
no up shard), and vLLM >= 0.28's `RoutedExperts.make_expert_params_mapping`
builds no fused gate/up mapping for that naming ("Unexpected gate/up
projection names: up_proj, . Fused gate/up mapping will be skipped").
`_batch_fused_modelopt_moe_weights` still emitted the non-gated w13 weight
and block scale as one batched 3-D tensor under `experts.0.up_proj`, which
therefore matched the per-expert entry for expert 0 and, being 3-D, took
`load_weights`' fused branch. That branch assumes a gate/up concatenation
and loads `chunk(2, dim=1)[expert_id]`: half of every expert's rows. Half
of w13 plus all of w2 is the 3/4.
Emit per-expert 2-D shards instead, the path the gated case already uses
and the one the initial disk load takes. w2 stays batched: its orientation
survives the fused branch's heuristic (last dim != hidden size) and it
loads correctly today (the gated w4a4 recipe passes).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…ient vLLM 0.29's `OpenAIServing._preflight` calls `self.engine_client.check_admission(n)` before every response (vllm-project/vllm#49445, `max_num_queued_reqs` / `max_num_queued_tokens` admission control). `_AsyncLLMHTTPClient`, the wrapper that keeps HTTP generation on the loop owning AsyncLLM request state, exposes an explicit member surface by design, so on the first 0.29 nightly that got past the `ErrorResponse` import every chat completion served to NeMo Gym 500ed with AttributeError: '_AsyncLLMHTTPClient' object has no attribute 'check_admission' and the Gym recipes died with `AsyncTrajectoryCollector aborting: 4 batch-worker failure(s) exceeded max_generation_failures=3`. Forward it like the other status-only members (it reads scheduler config and unfinished-request counters, so it does not need the engine loop) and let vLLM's HTTP-mapped overflow errors propagate. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…3 nvfp4 recipe The recipe asserts `MegatronQuantPolicyWorker.*723 TensorQuantizers found in model`, a line that only `mtq.print_quant_summary` emits, and the Megatron quant worker only calls it when it quantizes from scratch. When the cached quantized checkpoint (`<model>_modelopt_<cfg-hash>`, keyed on the quant config and calibration settings but not on the ModelOpt version) already exists on the cluster, the worker restores it instead and ModelOpt prints only `Inserted 723 quantizers`. Both 0.29 nightly runs took the restore path, trained fine (gen_kl 0.0017, reward 0.56) and then failed on this grep; the two most recent `main` nightlies never got a result for this recipe (checkout bounces), so the flake had no control. Accept either wording so the assertion does not depend on the cluster's cache state. The vLLM-side `720 TensorQuantizers found in model` check is unchanged (that path always prints the summary). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…ller tools/install_audio_deps.sh installed `torchcodec==0.11.1` next to `torchaudio==2.11.0` into the Megatron worker venv at test time. Per the torchcodec compatibility table, 0.11 is built for torch 2.11 only; 0.12+ use the stable ABI for torch >= 2.11. The image now ships torch 2.13, so the audio recipes were loading a torch-2.11 torchcodec against torch 2.13 (they passed on the 0.29 nightlies, but the pairing is unsupported). Move to torchcodec 0.16.0 — the version Gym's docker/install_codec_deps.sh already installs, so the two venvs agree — and keep torchaudio 2.11.0, which is the final torchaudio release and carries no torch pin. Update the audio-visual guide accordingly. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
The audio-visual guide described tools/install_audio_deps.sh as pinning `torchcodec>=0.3.0`; the script pins an exact version, now 0.16.0 to match the torch 2.13 image (see the previous commit, whose message mentioned this guide update ahead of it landing). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…rt amax
`route_moe_input_quantizer_amax` matched incoming fakequant amax names
against the expert module's vLLM `layer_name` and expert mapping, but refit
sends checkpoint names and vLLM only renames them inside `load_weights`
via the model's `hf_to_vllm_mapper`. For Nemotron-H that mapper turns
`backbone.` into `model.`, so
`backbone.layers.N.mixer.experts.E.up_proj.input_quantizer._amax` never
matched the module prefix `model.layers.N.mixer.experts.`, fell through to
`RoutedExperts.load_weights`, and the nano3 fakequant recipe died on
AttributeError: Layer model.layers.1.mixer.experts has no parameter
'w13_input_quantizer._amax' for checkpoint weight
'model.layers.1.mixer.experts.0.up_proj.input_quantizer._amax'
on the first 0.29 nightly that reached its refit (pass 5). Apply the
mapper to the name before matching, exactly as AutoWeightsLoader does, and
pass the model's mapper from the quant backend. Names the mapper drops
pass through untouched. Two tests cover the mapped and unmapped cases.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
…ibute VllmInternalWorkerExtension has no __init__ (vLLM mixes it into its Worker), so pyrefly flags the one-shot `_logged_tied_alias_drop` flag set in `_without_tied_embedding_aliases` as implicitly defined. Annotate it the way the class's other lazily created attributes are, which is what the lint check on the PR was failing on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 6ec3dd6 |
The lint workflow requires every pyrefly-clean file under nemo_rl/ to be listed in pyrefly.toml's project-includes; the new amax-routing module was not, which failed the Lint check. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
- `tests/unit/L0_Unit_Tests_Vllm_*.sh` first run the vLLM test files in the base venv, where vLLM is not installed, and rely on the `vllm` marker to deselect anything that imports it. Six tied-embedding-alias tests from e1c16e8 import `vllm_backend` in their bodies but were unmarked, so each shard failed on the first of them with `ModuleNotFoundError: No module named 'vllm'`. They now carry `@pytest.mark.vllm` like their neighbours. - `test_vllm_chat_template_wiring.py` stubs the vLLM surface the OpenAI server setup imports; it lacked `vllm.entrypoints.serve.engine.protocol`, where ae6507c imports `ErrorResponse` on vLLM 0.29, so all five wiring tests failed in the same base-venv pass. The fake tree now provides it. - torch 2.13 removed the named-tensor API, and with it `Tensor.names`; the Mooncake TQ checkpoint adapter's fast-path guard read `produced.names == (None, None)` and raised `AttributeError` in `_physical_keys`, failing `test_tq_mooncake_checkpoint.py`. The guard now goes through `_has_no_named_dims`, which treats a missing attribute as "no named dimensions" (torch 2.11 reported `(None, None)` for the same tensor). Verified in the worktree's torch-2.13 venv without vLLM: the shards' first pass over the vLLM test files and `tests/unit/data_plane` are green apart from the GPU-only generation tests. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
Conflicts: pyproject.toml (kept this branch's `numpy>=2.1.0,<2.5` override and took main's PyAV exclusion), uv.lock (regenerated from this branch's lock; the only changes are main's mooncake 0.3.11.post1 -> 0.3.13.post1 and wandb 0.28.1 -> 0.30.0 bumps, PyAV removed, huggingface-hub deduplicated to 1.32.0). Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test fc89153 |
Follow-up to 31dc3a8. The stand-in guard sat in the two callers of `_tied_embedding_aliases`, so tests that monkeypatch the detector to return aliases for a `SimpleNamespace` model (`test_prepare_reload_weight_iterator_drops_tied_aliases` and friends, now run in the `vllm`-only pass) saw the guard skip the filter and keep `lm_head.weight`. The guard now lives in `_tied_embedding_aliases` itself: a `None` model or one without `named_modules` yields no aliases, while a patched detector is honoured. Also adds vLLM 0.29's `vllm.entrypoints.serve.engine.protocol` to the fake vLLM tree in `test_vllm_generation.py`, the last stub that still only knew the 0.25 `openai.engine.protocol` location; the two OpenAI-server tests in the base-venv pass failed on the `ErrorResponse` import. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 000cd24 |
Brings #3566 (end-to-end TE NVFP4 training with per-token vLLM rollout) and three smaller commits onto the vLLM 0.29 bump. Conflicts and resolution: - pyproject.toml: main bumped vLLM to 0.26.0 / flashinfer 0.6.14 / nvidia-cutlass-dsl 4.6.0 and pinned quack-kernels==0.6.1; this branch keeps vLLM 0.29.0 / flashinfer 0.6.18 / cutlass-dsl 4.6.2 and takes no quack pin (vLLM 0.29 requires quack-kernels==0.6.4). TransformerEngine 2.18.0 (PyPI) and ModelOpt 43fd41a58 are taken from main: that ModelOpt revision is a release cherry-pick that carries #2054's RoutedExperts vLLM plugin and does not contain #1550's GroupedQuantizer, so the pin note is kept and reworded. flashinfer-cubin index and the cutlass 4.5.x dependency-metadata are main's. - uv.lock: regenerated from this branch's lock; only ModelOpt (0.46.0 @ 43fd41a5), TransformerEngine 2.15.0+git -> 2.18.0 (+ -cu13/-torch wheels, nvdlfw-inspect) and the dropped cutlass libs-base 4.5.0 entry changed. - quantization/fp8.py: main's refactor of the module lookup into quantization/utils.py::resolve_module_from_param_name is taken; the ambiguous-shard fix from 28beca5 (vLLM 0.29 lists `wkv` under two fused modules) is ported into the new resolver, and its tests call the renamed public get_module_from_param_name. - modelopt vllm_quant_backend.py and its test: main's per-expert non-gated W13 emission (superset of a3aa6cd) is taken. Also fixes the pyrefly error from 000cd24: `_tied_embedding_aliases` now accepts `torch.nn.Module | None`. Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test d104085 |
…server check vLLM 0.29 added `completion_tokens_details` to the OpenAI `UsageInfo` response model, so `test_vllm_http_server`'s exact comparison against the 0.25-shaped expected `usage` block failed on the extra key. Drop it in the test's version-normalisation step next to the other fields vLLM may or may not include. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 8e97d91 |
… vLLM 0.29 / torch 2.13 Three failures from the first full CI + nightly pass on the merged head: - Async checkpoint saves died at the first save with `DistNetworkError: ... EADDRINUSE` inside torch's process-based DCP checkpointer (`_async_process_executor`): rank 0 probes a port with `get_free_port()` and the daemons then rendezvous on it with a fresh GLOO group, but the port was taken again before the daemon bound it. It hit every DTensor save in the run (L1 GRPO resume, the converter round-trip test, the llama3.2-1b fsdp2tp2 tq_mooncake nightly recipe) after the Automodel bump made daemon creation synchronous across ranks. The Automodel checkpoint manager now opts the daemons into torch's `DCP_USE_PREFIX_STORE=1` mode when `MASTER_ADDR`/`MASTER_PORT` are present, so they join the training process group's TCPStore under a prefix and bind nothing. Unit-tested. - `nvfp4_pertoken.py` (from #3566) called `make_nvfp4_moe_kernel(..., layer=layer)`; vLLM 0.29 dropped that parameter (it already takes `per_token_activation`), so the new `te_nvfp4_pertoken_quick` GB200 recipe died with `TypeError: unexpected keyword argument 'layer'`. - `test_decode_vs_prefill.sh` OOMed on GB200 at the first MoE forward: with `gpu_memory_utilization=0.8` vLLM 0.29 sized a 93 GiB KV cache for Nemotron-3-Nano-30B (TP2) and the FlashInfer TRT-LLM BF16 MoE kernel then could not allocate its 4 GiB workspace (the same test is memory-marginal on `main`'s H100 runners too). The diagnostics tool gains a `--gpu-memory-utilization` flag and the functional test passes 0.7. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 0cc6fab |
|
/ok to test 51d7ee4 |
… gated-delta-net training stops using the TileLang backward kernel `grpo-qwen3.5-35ba3b-6n4g-async-1off-bf16-trtllm` (GB200) died on every run of this branch at step 1 of `MegatronPolicyWorker.train()` with `torch.AcceleratorError: CUDA error: misaligned address` raised from `fla/ops/common/backends/tilelang/chunk_bwd.py::chunk_bwd_dqkwg_tilelang`, the gated-delta-rule backward. `main`'s Sept 20 and Sept 21 nightlies pass the same recipe (20/20), and their Megatron worker logs contain no TileLang compiles at all. The difference is the flash-linear-attention version in the lockfile. `main` resolves fla-core / flash-linear-attention 0.5.2; this branch's lock still carried 0.5.1 because every `main` merge kept our `uv.lock` and re-locked without upgrading transitive packages. In fla-core 0.5.1 `TileLangBackend.is_enabled()` is the base-class default, so the TileLang kernels are used wherever `tilelang` imports (the `mcore` extra installs it for the fused DSA kernels), including Blackwell. fla-core 0.5.2 (fla-org/flash-linear-attention#640) turns TileLang on by default only on Hopper with Triton >= 3.4, keeps Triton the default everywhere else, and also fixes the kernel's value-head indexing; on GB200 that means the Triton backward that `main` runs. `uv lock --upgrade-package flash-linear-attention --upgrade-package fla-core`; the lock moves only those two entries (0.5.1 -> 0.5.2). `uv lock --check` and `tests/unit/test_dependency_pins.py` pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 5200efa |
…ution Each `main` merge on this branch kept our `uv.lock` and re-locked without upgrading, while `main` had relocked with upgrades, so ~150 transitive packages resolved older here than on `main` (pandas 2.3 vs 3.0, starlette, xgrammar, tensordict, nccl4py, cuda-pathfinder, ...). One of them, fla-core, caused the GB200 regression fixed in 5200efa; the rest are brought to parity here so the nightly compares the intended bumps and nothing else. `uv lock --upgrade`, then `uv lock --upgrade-package ray==2.56.1` to keep Ray at the version `main`'s nightlies run (the upgrade would have moved it to 2.58.0). 103 packages move; none of the pinned ones (vLLM 0.29.0, torch 2.13.0 / 2.11.0 per extra, triton, flashinfer 0.6.18, cutlass-dsl 4.6.2, TE 2.18.0, ModelOpt 43fd41a58, sglang, TRT-LLM, DeepEP, nixl) change. The only packages that still resolve below `main` are the deliberate ones: numpy 2.4.6 (numba 0.65 needs <2.5), llguidance 1.7.6 (vLLM 0.29's range) and the flash-attn sdist. `uv lock --check`, taplo and `tests/unit/test_dependency_pins.py` pass; the per-extra torch/triton exports are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test be440de |
@yfw, there was an error processing your request: See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/2/ |
|
/ok to test be440de |
…m the device size 0cc6fab lowered the test's gpu_memory_utilization to 0.7 so the FlashInfer TRT-LLM MoE workspace fits next to the KV cache on 186 GiB GB200 parts. On 80 GiB H100 parts that is too low: Nemotron-3-Nano-30B at TP2 takes 29.5 GiB per rank and vLLM's profiling peak another ~28 GiB, so 0.7 leaves no KV cache at all and the engine fails with `No available memory for the cache blocks` (L1_Functional_Tests_Other_1 on be440de). Use 0.7 above 120 GB of device memory and the old 0.8 below it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test 8d1f98a |
… the router on vLLM 0.29 Router replay for the per-token NVFP4 rollout (`te_nvfp4_pertoken_quick`) worked on vLLM 0.26 because NeMo-RL's `_patch_vllm_moe_routed_experts_capture` fires `router.select_experts` on the monolithic fused-MoE branch: vLLM 0.26 bound the `RoutedExpertsCapturer` callback to every layer's router, monolithic kernels never call the router, and the patch made them do so when capture is on. vLLM 0.29 changed the binding (`bind_routed_experts_capturer`): a monolithic kernel must report `supports_routing_replay_capture()`, the callback is bound to the kernel's experts *object*, the FlashInfer launch fills `routing_replay_out` itself, and any other monolithic kernel is rejected with ValueError. The router hook is only set for modular kernels, so NeMo-RL's runner patch became inert for the TRT-LLM NVFP4 kernel. Two problems follow for the per-token method: - `ModelOptNvFp4PerTokenFusedMoE.process_weights_after_loading` rebuilds the kernel on every cold and warm refit, so the experts-bound capture function is gone after the first weight update and every later rollout would return all-zero routes to Megatron's router replay. - The per-token FlashInfer launch with a replay buffer attached is the one combination this recipe exercises that 0.26 never ran; on the fixed head the recipe dies in the first post-refit generation with `CUDA error: an illegal memory access` (nemo-ci job 450220649). The per-token method now wraps its experts class (`host_captured_experts_cls`) so it reports no in-kernel capture, and a new vLLM source patch (`_patch_vllm_routed_experts_capture_router_fallback`) makes the 0.29 binder fall back to `router.set_capture_fn` for such kernels instead of raising. Capture then goes through the router hook the runner patch already fires, survives kernel rebuilds, and the kernel is launched without `routing_replay_out`, exactly as on 0.26. Kernels that support in-kernel capture keep using it. Both patches are `required` together. Unit tests: patch anchor against the installed vLLM, idempotency, fail-closed, an executable check of the patched binder for both kernel kinds, and the experts-class wrapper. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test af7d9e0 |
|
Recipes that pass on both this PR (final lock be440de: H100 lane 69151691, GB200 lane 69183689) and on H100 (108)
GB200 (31)
|
yfw
left a comment
There was a problem hiding this comment.
Team review: PR #4080, vLLM 0.26 → 0.29 and torch 2.11 → 2.13
Reviewed at head af7d9e0 by 8 agents (RL codebase, vLLM upstream, torch/packaging, bug finder, design, tests, description/threads, devil's advocate). No correctness bug was found in the code. Inline: one CI-coverage gap that should be fixed here (the three new DCP_USE_PREFIX_STORE tests never run), a handful of low-severity test/doc hardening items with committable blocks, and a description refresh.
Verified against the pinned sources (vLLM v0.29.0 98dff2a8, torch v2.13.0 cf30153c / v2.11.0 70d99e99): all ten source-patch anchors occur exactly once in 0.29 and every patch was executed against a scratch copy of its target file; the TCPStore patch takes the "upstream fixed" branch; all five injected worker extensions are collision-free against Worker/WorkerBase; 239 from vllm... import targets across the repo resolve; check_admission, make_fp8_moe_kernel, make_nvfp4_moe_kernel, _get_tied_embedding_params, SKIP_TENSORS/SKIP_LOAD_TENSORS, the KV-scale mirror and the DeepSeek V4 hook shadow (every exit path) match 0.29 line by line; the tied-alias failure and its fix were reproduced on CPU with real vLLM classes; the amax routing was probed with vLLM's real expert-mapping strings; the CUDA-IPC 'c' payload is byte-identical between the two torch tags and the 2.11 consumer checks version <= own; DCP_USE_PREFIX_STORE and the MASTER_ADDR/MASTER_PORT assert hold in torch 2.13; the lock is below main only for numpy, llguidance and the flash-attn local label (100 packages above, 399 equal).
Local runs (this host has no usable CUDA driver, so GPU-gated tests are unverified). --vllm-only over tests/unit/models/generation in a venv built from this lock's vllm extra: 415 passed, 7 skipped, 8 environment-only failures; every test this PR adds passed. No-vLLM pass (the L0 shards' mode): generation 506 passed, utils 486, data_plane 497. pre-commit run --all-files: clean. 55 mutants were run against the PR's new tests; every pinned fix went red except the vacuities called out inline.
Upstream root causes worth filing (the workarounds are correct, keep them):
- vLLM:
DeepseekV4ForCausalLM.load_weightsends withself.process_weights_after_loading()on every call (model.py#L1813-L1816) although the loader already runs the model-level hook (model_loader/utils.py#L145-L146); per-call invocation breaks partial/streamed loads (this PR'sCannot copy out of meta tensor). - vLLM:
AutoWeightsLoader._check_skipped_aliasesrequires a tied alias and its canonical to arrive in the sameload_weightscall (utils.py#L442-L470), which no batched weight-update client can guarantee.
FYI, no action. all_gather_single does not exist on torch 2.11, which the sglang/trtllm venvs (also installing nemo_rl) now pin; its only caller BatchedDataDict.all_gather has no production call site, and the rename does remove a per-call FutureWarning on 2.13. check_admission is a no-op today (queue limits default None). The ambiguous-shard detection in quantization/utils.py is generic but only DeepSeek V4 resolves it; four other 0.29 models share the shape and keep the pre-existing bf16-refit fallback (tracking issue at most).
Optional test hardening (verified candidates, not staged): an engine_client member sweep for _AsyncLLMHTTPClient; a WeightsMapper._map_name pin; an IPC tuple-position pin against torch's rebuild_cuda_tensor signature; the mapper hand-off at the tied-alias call sites; RPC caller/callee parity for synchronize_sparse_refit_device; the <= boundary in normalize_cuda_ipc_handle; || true on the nvidia-smi probe in test_decode_vs_prefill.sh; the remaining three injected worker extensions in test_vllm_worker_extension_names.py. Available on request.
Generated by Claude Code
Addresses the first review round on #4080. - tests/unit/models/automodel/test_automodel_checkpoint.py: the three `test_init_checkpointer_*` tests now carry `@pytest.mark.automodel`, so the Automodel shard collects them (the Models shards module-skip this file without nemo_automodel); the first one records the absence of `DCP_USE_PREFIX_STORE` before `init_checkpointer` writes it, so the value no longer leaks past teardown. - tests/unit/models/generation/test_vllm_nvfp4_pertoken.py: a test drives `ModelOptNvFp4PerTokenFusedMoE.process_weights_after_loading` and binds the recorded `make_nvfp4_moe_kernel` kwargs to the installed vLLM's signature, pinning both the dropped `layer=` kwarg and the host-captured experts class. - tests/unit/models/generation/test_vllm_import_targets.py: the walk also covers `nemo_rl/models/generation/dynamo` and `nemo_rl/modelopt/models/generation`, whose lazy `from vllm...` imports no unit test executes either. - tests/unit/models/generation/test_deepseek_v4_fp8.py: pins that vLLM ships `SKIP_LOAD_TENSORS` and `SKIP_TENSORS` as two distinct sets, so a rename cannot silently fall back to the single-set (double conversion) shape. - nemo_rl/utils/cuda_ipc.py: `normalize_cuda_ipc_handle(handle: Any) -> Any`; the docstring says what `'e'` handles mean for the torch split (the trainer emits them under `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`, and a torch 2.11 consumer still rejects them). - pyproject.toml: the instanttensor override keeps the aarch64 skip but states the real reason; 0.2.0 ships aarch64 wheels, so it is a choice, not a wheel-availability limit. - docker/Dockerfile: the release stage reads the TRT-LLM wheel from the mirror; it no longer re-seeds the cache mount. - patches.py / test_vllm_tcpstore_port.py: `RayWorkerProc`, the class that owns `create_dist_init_method` in vLLM 0.29 (`RayWorkerV2` does not exist). - fp8.py: `process_weights_after_loading_moe`'s docstring no longer names the `layer=` kwarg 0.29 removed. - docs: the two remaining "vLLM 0.25.1" statements in the GLM-5 and Nemotron Omni guides. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test e222247 |
The transitive relock in be440de moved cuda-bindings and cuda-python from 13.3.1 to 13.4.x (`main` is on 13.3.1). cuda-bindings 13.4 removed the `reserved` field of `cudaIpcMemHandle_t`, which tensorrt_llm 1.3.0rc21's `_ipc_utils.py::open_ipc_memory` reads while allocating the TP>1 all-reduce fusion workspace, so every multi-GPU TRT-LLM engine died at start with `AttributeError: 'cuda.bindings.runtime.cudaIpcMemHandle_t' object has no attribute 'reserved'` (GB200 nightly `grpo-qwen3-1.7b-2n4g-fsdp2-trtllm`, nemo-ci job 450733567). TP=1 TRT-LLM recipes never allocate that workspace and passed on the same lock; verified locally that 13.3.1 has the attribute and 13.4.2 does not. The `trtllm` extra pins `cuda-python<13.4` and `cuda-bindings<13.4`; the lock resolves the trtllm fork to 13.3.1 / cuda-core 1.0.1 while the torch-2.13 forks keep 13.4.x, which the vLLM and Megatron recipes passed with. `uv lock --check`, taplo and `tests/unit/test_dependency_pins.py` pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Yi-Fu Wu <yifu.wu@gmail.com>
|
/ok to test ab6b3f2 |
What does this PR do ?
Bumps vLLM from 0.26.0 to 0.29.0 (this branch started from 0.25.1;
mainmoved to 0.26.0 with #3566 on Sept 19). The vLLM 0.29.0 wheels are built against torch 2.13, so the vLLM / Megatron / Automodel / FSDP venvs move with it: torch 2.11.0 → 2.13.0, torchvision 0.26.0 → 0.28.0, triton 3.6.0 → 3.7.1, flashinfer 0.6.14 → 0.6.18, nvidia-cutlass-dsl 4.6.0 → 4.6.2, tilelang 0.1.9 → 0.1.12 in the vLLM venv, llguidance 1.8.0 → 1.7.6 (vLLM 0.29's own<1.8bound), numpy 2.5.3 → 2.4.6. The sglang and TRT-LLM venvs stay on torch 2.11.0, because their kernels are built against it; torch is now pinned per backend extra and a small IPC-handle shim lets the 2.11 inference venvs open weight handles produced by the 2.13 trainer.Full
nightlysuites on H100 (cw_dfw) and GB200 (oci_hsg) were compared recipe by recipe againstmain's own scheduled nightlies at this PR's merge point. Every regression the bump introduced is fixed on this branch and validated on hardware. The remaining failures are pre-existing onmain, infrastructure, or goldens sitting at their bounds, each with themaincontrol that shows it.Changes
Bullets are prefixed with the commit that made the change.
Dependencies (
pyproject.toml/uv.lock)0.26.0→0.29.0from PyPI (default cu130 wheels for both arches), torch2.11.0→2.13.0, torchvision0.26.0→0.28.0,flashinfer-python/-cubin/-jit-cache0.6.14→0.6.18(cu130 wheels for both arches, matching the ABI of the 0.29.0 vLLM wheels),nvidia-cutlass-dsl[cu13]4.6.0→4.6.2in thevllmextra (and4.5.2→4.6.2inmcore), tilelang<0.1.13(0.1.9 → 0.1.12 in the vLLM venv),llguidance>=1.7.0,<1.8.0(1.8.0 → 1.7.6: vLLM's own bound, whichmain's>=1.3.0floor bypassed). flash-attn switches from the pinnedcu13torch2.10GitHub wheels toflash-attn==2.8.1(sdist) because no 2.8.x wheel exists for torch ≥ 2.11.instanttensorkept to x86_64 via an override. vLLM 0.29 listsinstanttensor>=0.1.9unconditionally; it is the optionalload_format="instanttensor"safetensors loader, imported lazily, and NeMo-RL never selects it. 0.2.0 ships aarch64 wheels, so this is a choice to leave an unused dependency out of the GB200 image, not a wheel-availability limit.mainalready removed thenvidia-cutlass-dsl-libs-baseexclusion in feat: add end-to-end TE NVFP4 training with per-token vLLM rollout #3566 (4.6 split thelibs-base/libs-cu13wheels cleanly; the 4.5.x pair overwrote 180 shared files). This PR moves thevllmextra 4.6.0 → 4.6.2 (flashinfer 0.6.18's requirement) and themcorepin 4.5.2 → 4.6.2 so the training venv does not inherit the overlapping 4.5.x pair, drops feat: add end-to-end TE NVFP4 training with per-token vLLM rollout #3566'squack-kernels==0.6.1pin (0.29 pins 0.6.4), and keeps a NOTE inpyproject.tomlon why the exclusion must not come back. Thetrtllmfork stays on 4.5.0 via tensorrt-llm.MAX_JOBS=4via[tool.uv.extra-build-variables]. flash-attn'ssetup.pysizes its parallelism from the host's free memory (not the cgroup) andcpu_count()//2, with--threads 4per nvcc; every nemo-ci build attempt on both architectures died withResourceExhaustedafter dozens ofKilled "$CICC_PATH/cicc". 4×4 builds cleanly on aarch64 (2h47m) and x86 (~2h, alongside TE and flash-mla).<2.5(resolves 2.4.6). vLLM 0.29 pinsnumba==0.65.0, whose metadata requiresnumpy<2.5; thenumpy>=2.1.0override forced 2.5.1 past it and numba refused to import in the vLLM venv ("Numba needs NumPy 2.4 or less"). Not fatal by itself — vLLM's optional-module probe catches it — but it left a broken numba in the venv and polluted every trace.main's43fd41a58, now with a do-not-move note. The pin must not move past2ded5aba3(ray.sub requires submission from NeMo RL home directory, blocking external workflow organization #1550): that commit introducesGroupedQuantizer, which breaks the Megatron fakequant DDP hooks and real-quant dist-ckpt loading (observed on ModelOptmain,613e5e8b).43fd41a58is a release cherry-pick that carries Direct support for megatron bridge checkpoints #2054 (theRoutedExpertsvLLM plugin this bump needs; the pre-Direct support for megatron bridge checkpoints #2054 plugin registered onfused_moe.layer.FusedMoE, which 0.29 no longer has) and excludes ray.sub requires submission from NeMo RL home directory, blocking external workflow organization #1550.main's lock resolves. fla-core 0.5.1 enabled flash-linear-attention's TileLang kernels wherevertilelangimports (themcoreextra installs it for the fused DSA kernels), including Blackwell, and its TileLang gated-delta-rule backward faults there withCUDA error: misaligned address; 0.5.2 ([Bug] GDN precision error in triton3.5 and h20 fla-org/flash-linear-attention#640) turns TileLang on by default only on Hopper with Triton ≥ 3.4 and keeps Triton elsewhere. The GB200grpo-qwen3.5-35ba3b-6n4g-async-1off-bf16-trtllmrecipe died at step 1 ofMegatronPolicyWorker.train()on every run of this branch whilemain(0.5.2) passes it; it had been mis-cleared against a Sept 10maincontrol that still carried 0.5.1. This branch's lock had lagged because eachmainmerge kept ouruv.lockand re-locked without upgrading transitive packages.main's resolution (uv lock --upgrade, with Ray held atmain's 2.56.1). About 150 packages had resolved older here than onmainfor the reason above (pandas 2.3 vs 3.0, starlette, xgrammar, tensordict, nccl4py, cuda-pathfinder, …); 103 move, none of the pinned ones (vLLM, torch per extra, triton, flashinfer, cutlass-dsl, TE, ModelOpt, sglang, TRT-LLM, DeepEP, nixl) change, and the only packages still belowmainare the deliberate numpy<2.5, llguidance<1.8and the flash-attn sdist. The nightlies in the Test plan ran on this lock.trtllmvenv stays on cuda-bindings / cuda-python 13.3. The relock above moved them to 13.4.x, and cuda-bindings 13.4 removed thereservedfield ofcudaIpcMemHandle_tthat tensorrt_llm 1.3.0rc21's_ipc_utils.pyreads while allocating the TP>1 all-reduce fusion workspace; the GB200grpo-qwen3-1.7b-2n4g-fsdp2-trtllmrecipe died at engine start withAttributeError: … has no attribute 'reserved'(450733567) while the TP=1 TRT-LLM recipes passed. Thetrtllmextra pins both<13.4(main's versions); the torch-2.13 forks keep 13.4.x, which the vLLM and Megatron recipes passed with.vLLM source patches / worker (
nemo_rl/models/generation/vllm/)VllmInternalWorkerExtension.synchronize_devicerenamed tosynchronize_sparse_refit_device. vLLM 0.29 addsWorkerBase.synchronize_device([Bugfix][DP] Synchronize the device on pause completion vllm-project/vllm#52914) andWorkerBase.init_workerasserts that a worker extension never shadows aWorkerattribute, so every vLLM engine died at init withAssertionError: Worker class … already has an attribute synchronize_device, which conflicts with the worker extension class. The extension method only drained peers before the sparse-delta refit removed staged batch files; its RPC caller and test follow the rename. An AST scan of both extension classes against 0.29'sWorker/WorkerBaseshows no other overlap, and a newvllm-marked unit test mirrors vLLM's collision predicate so the next upstream name clash fails in CI rather than on a GPU job.ErrorResponseimported fromvllm.entrypoints.serve.engine.protocol. vLLM 0.29 movedopenai/engine/protocol.pyout of the openai package ([Frontend] Move engine/protocol.py out openai folder vllm-project/vllm#54492);VllmAsyncGenerationWorker.post_init_asyncimported it from the old path when bringing up the OpenAI-compatible server, so every recipe that serves generation over HTTP (Gym, swe1, mopd) died withModuleNotFoundError: No module named 'vllm.entrypoints.openai.engine'. A newvllm-marked test walksnemo_rl/models/generation/vllmwithast, collects every unguardedfrom vllm… importstatement in the vLLM, Dynamo and ModelOpt generation trees (109 today, mostly lazy, method-local imports no unit test executes) and resolves them against the installed vLLM.check_admissionproxied through_AsyncLLMHTTPClient. vLLM 0.29'sOpenAIServing._preflightcallsengine_client.check_admission(n)before every response ([Core] Addmax_num_queued_reqsandmax_num_queued_tokensfor queue size management vllm-project/vllm#49445, queue admission control). The async worker's engine-client wrapper exposes an explicit member surface by design, so every/v1/chat/completions500ed withAttributeError: '_AsyncLLMHTTPClient' object has no attribute 'check_admission'and the Gym-path recipes died onAsyncTrajectoryCollector aborting: … batch-worker failure(s) exceeded max_generation_failures. Forwarded like the other status-only members; unit-tested.load_weights. vLLM 0.29'sAutoWeightsLoader(Fix weight tying vllm-project/vllm#51665) skips a tied alias such aslm_head.weightand then asserts that its canonicalmodel.embed_tokens.weightwas loaded in the sameload_weightscall. Refit streams weights in transport-sized batches, so the two routinely land in different calls and every tied-embedding model refit from a DTensor/automodel policy died at its first weight update (ppo_qwen2_5_1_5b_gsm8k_1n8g_automodel_noncolocated{,_async},dapo_gemma4_e2b_it_1n8g_fsdp2_automodel,vlm_grpo_gemma4_e4b_geo3k_1n8g_automodel) withValueError: 'lm_head.weight' was skipped because it is tied to 'model.embed_tokens.weight' … was not found in the checkpoint. The alias never loads anything, so it is now dropped up front using vLLM's own_get_tied_embedding_paramsand the model'shf_to_vllm_mapper(so Gemma-style renamed parameters are covered), on both the batched refit and the nativereload_weightspath; the MTP drafter still sees the unfiltered stream. Unit-tested; avllm-marked test pins the helper's existence._patch_vllm_ray_executor_v2_tcpstore_portrecognises the upstream fix. vLLM 0.29 ships [Bugfix] Avoid TCPStore port collision for co-located non-DP Ray engines vllm-project/vllm#53666 and #50969:RayWorkerProc.create_dist_init_methodbinds the torch.distributed TCPStore itself on a kernel-assigned port and holds the socket (self._dist_init_store = store) untilinit_process_groupreuses it, so the probe/bind window this patch closed on 0.25 is gone and_select_tcpstore_portno longer exists. Without this change the patch found no anchor and logged "Engines spanning nodes may fail with EADDRINUSE" on every worker start. It now logs at info and leaves the file alone; the port-arithmetic unit tests skip when the installed vLLM carries the upstream marker, and a new test pins the no-warn/no-edit behaviour against a 0.29-style source. The reservedVLLM_PORTband still governs the MessageQueue and API-server ports._patch_vllm_shm_broadcast_bind_retryre-targeted: vLLM 0.29 binds theMessageQueueremote socket to port 0 directly (no probe/bind race any more) but ignoresVLLM_PORT; the patch restores reserved-band selection with bind retries so engine sockets stay out of the ephemeral range.mm_device_do_normalizedefaults toFalse. vLLM 0.29 ([Model] Fused mm preprocess normalisation on the Device vllm-project/vllm#50411) calls the HF image processor withdo_rescale=False, do_normalize=Falseand re-applies both on the GPU in the vision tower's dtype. The policy normalizes the same images on the CPU in fp32 through the same processor, and the nightly probability-error checks assume identical inputs on both sides. Verified on CPU with transformers 5.12.1: the fused path is exact in fp32 but differs by up to 0.019 (≈1.7 % of the pixel std) in bf16; with the default flipped the Qwen2.5-VL clevr DTensor recipe is back atmain-level metrics (token_mult_prob_error 1.019, gen_kl 0.0008, vs 198 / 0.14 with on-device normalization). Opt back in withpolicy.generation.vllm_kwargs.mm_device_do_normalize=true.fp8 generation (
quantization/fp8.py)make_fp8_moe_kernelno longer takes alayerkwarg (0.25 forwarded it only to the FlashInfer TRTLLM experts). Both call sites updated; the unit test's expected kwargs follow.process_weights_after_loading_kvre-mirrored on 0.29'sBaseKVCacheMethod.process_weights_after_loading: the attention layer no longer hascalculate_kv_scales(dynamic scales are a KV-cache dtype now,kv_cache_uses_per_token_head_scales), the static-scale branch keys offis_quantized_kv_cache, and the host_k_scale_cpu/_v_scale_cpucopies are refreshed on refit. Parameters are still kept so refit can update them.ModelOpt fakequant / real-quant refit (
nemo_rl/modelopt/models/generation/)input_quantizer._amaxrouted around vLLM's expert loader, with checkpoint names mapped throughhf_to_vllm_mapperfirst. The Megatron side exports oneinput_quantizer._amaxper expert projection (…experts.16.up_proj.input_quantizer._amax); the vLLM ModelOpt MoE module owns a single fused quantizer per projection group (w13_input_quantizer/w2_input_quantizer). vLLM 0.25 loaded these through the model-level parameter dict, which NeMo-RL patches to include quantizer buffers; 0.29'sAutoWeightsLoaderhands everyexperts.*name toRoutedExperts.load_weights, which rewrites it with the expert mapping and resolves the result with a singlegetattr— a dotted buffer name cannot resolve that way and every fakequant nvfp4 MoE refit died withAttributeError: Layer … has no parameter 'w13_input_quantizer._amax'. Newvllm_quant_moe_amax.route_moe_input_quantizer_amaxapplies the model'shf_to_vllm_mapper(Nemotron-H:backbone.→model.) and then the same rewrite vLLM does, walks the dotted path on the expert module, fans the values in withmax, and hands the remaining weights to vLLM's loader. Unit-tested (mapped and unmapped cases); validated by the GB200distillation_nano3_30ba3b_4n4g_megatron_qa_nvfp4_modelopt_specrecipe.Distributed (
collectives.py)all_gather_into_tensor→all_gather_single(torch 2.13 API).Docker (
docker/Dockerfile,3rdparty/TensorRT-LLM-workspace/_backend.py)main's fix(docker): persist the TRT-LLM wheel #4153 mirrors the content-addressed wheel into/opt/trtllm_wheelsin the hermetic layer and has the release stage read it from there. This PR's residual is3rdparty/TensorRT-LLM-workspace/_backend.pynot re-copying a mirror that already exists (a stage inheriting the mirror from an earlier layer no longer duplicates it into its own) and the matching Dockerfile comment.Audio deps (
tools/install_audio_deps.sh,docs/guides/grpo-audio-visual.md)torchcodec==0.11.1→0.16.0in the test-time audio installer. Per the torchcodec compatibility table, 0.11 is built for torch 2.11 only while 0.12+ use the stable ABI for torch ≥ 2.11; the image now ships torch 2.13. 0.16.0 is what Gym'sdocker/install_codec_deps.shalready installs, so the two venvs agree.torchaudio==2.11.0stays: it is the final torchaudio release and carries no torch pin. Guide updated to state the exact pins.Tests (
tests/test_suites/)grpo-qwen3-30ba3b-4n8g-megatron-qa-nvfp4.shaccepts ModelOpt'sInserted 723 quantizersline as well as723 TensorQuantizers found in model. The latter comes frommtq.print_quant_summary, which the Megatron quant worker only calls when it quantizes from scratch; when the cached quantized checkpoint (<model>_modelopt_<cfg-hash>, keyed on quant config + calibration settings, not on the ModelOpt version) exists it restores it and only theInserted …line is printed.torch per backend extra (
pyproject.toml/uv.lock)torch>=2.11,<2.14withtorchvision>=0.26,<0.29; thevllm,mcore,automodelandfsdpextras pintorch==2.13.0/torchvision==0.28.0, and thesglangandtrtllmextras pintorch==2.11.0/torchvision==0.26.0. The extras are declared mutually conflicting under[tool.uv], so uv resolves each one in its own fork and the lockfile carries both torch versions. The driver venv (no extra) resolves 2.13.0 today; the<2.14ceiling keeps a future relock from silently moving it. This is what makes sglang-kernel 0.4.5 and TRT-LLM usable again: both ship extensions built against torch 2.11 and failed at import on 2.13 (undefined symbol _ZNK2at10TensorBase14const_data_ptr…/_ZNR5torch7Library4_def…). Verified withuv export --frozen --extra <name>per fork and by the sglang / TRT-LLM recipes on both SKUs.received sharable handle from a future version of torch that this version does not know how to handle. torch 2.13 ([CUDA] Fix CUDA IPC deserialization mismatch withexpandable_segmentsonFABRIC_HANDLEpytorch/pytorch#179618) bumped theSHAREABLE_HANDLE_VERSIONbyte inCUDACachingAllocator::shareIpcHandlefrom 2 to 3 for the expandable-segment header; the'c'(plaincudaMalloc) payload is byte-identical, and the consumer only checksversion <= own. Newnemo_rl/utils/cuda_ipc.py::normalize_cuda_ipc_handlerewrites the version byte of'c'handles to 2 and leaves'e'handles (whose format did change) and raw 64-byte handles alone; applied inrebuild_cuda_tensor_from_ipcand in the sglang_rebuild_cuda_tensor_modifiedpatch. The shim never consults the consumer: it lowers the version byte of any'c'handle above 2 and leaves'e'handles alone (see Notes). 13 unit tests.DeepseekV4ForCausalLM.load_weightsnow ends withself.process_weights_after_loading(), which recomputes the first layer's hyper-connection broadcast fromhc_attn_fn; NeMo-RL streams a refit through buffer-sizedload_weightscalls while vLLM's layerwise reload still holds that parameter on the meta device, so the 16-node DeepSeek V4 Flash recipe died at its first refit withCannot copy out of meta tensor.deepseek_v4_fp8.prepare_refitnow shadows the hook on the instance,finalize_refitlifts it and runs it once afterfinalize_layerwise_reload, andrestore_refitlifts it on failure too. Unit-tested; see Known issues for the follow-on OOM this uncovered.DeepseekV4ForCausalLMapacked_modules_mappingin whichwkvis a shard of two fused modules (fused_wqa_wkvandfused_wkv_wgate); the fp8 module lookup resolved shards by leaf name alone, sentattn.wkvto a module that does not exist, and refit the weight unquantized. Shard names listed under more than one fused module are now left to the parent-aware DeepSeek V4 remap. (b) 0.29 split the layerwise-reload skip list intoSKIP_TENSORS(stay off the meta device) andSKIP_LOAD_TENSORS(loader not wrapped or counted); 0.25.1 did both with one set. The immediately loaded expert tensors were therefore counted, vLLM ranprocess_weights_after_loadingon each expert layer as soon as its last expert arrived, andfinalize_refitconverted the already kernel-layout weights and scales a second time (verified with a probe: scales doubled twice per refit).prepare_refitregisters the names in both sets and returns a per-setSkipNamesrecord. Both were caught by running thevllm-marked unit tests thatmainadded with feat: add DeepSeek V4 Flash GRPO support - Automodel path #3595/fix(vllm): support BF16 TRTLLM NCCL reshard refit #3659 against 0.29; those tests are ported to 0.29's API in the same commit (WeightsMapper.get_rename_mapper,Fp8MoEMethodreading the MoE config, the import-target test skippingtry/except ImportErrorfallbacks).main's Mooncake TQ checkpoint adapter (feat(data-plane): add Mooncake storage checkpoints #3898) guarded its fast path withproduced.names == (None, None);Tensor.namesno longer exists on 2.13, so_physical_keysraisedAttributeError(caught bytest_tq_mooncake_checkpoint.py). The guard now treats a missing attribute as "no named dimensions". The same commit marks the tied-alias unit testsvllmand adds vLLM 0.29'sErrorResponsemodule to the chat-template wiring test's fake vLLM tree, so the L0 shards' no-vLLM pass over the vLLM test files is green again.mainmerged through a9c3178 (Sept 22). Notable merges: chore(deps): bump mooncake and wandb #4179 (mooncake 0.3.13.post1 / wandb 0.30.0), build: exclude PyAV from shipped container #4184 (PyAV exclusion) and feat: add end-to-end TE NVFP4 training with per-token vLLM rollout #3566 (TE NVFP4 end-to-end; onmainit bumped vLLM to 0.26.0 / flashinfer 0.6.14 / cutlass-dsl 4.6.0, moved TE to the 2.18.0 PyPI wheels and ModelOpt to43fd41a58, and refactored the fp8 module lookup intoquantization/utils.py). Resolution: this branch's vLLM 0.29.0 / flashinfer 0.6.18 / cutlass-dsl 4.6.2 pins stay (0.29 pinsquack-kernels==0.6.4, so feat: add end-to-end TE NVFP4 training with per-token vLLM rollout #3566's 0.6.1 pin is not taken); TE 2.18.0 and ModelOpt43fd41a58(a release cherry-pick that carries Direct support for megatron bridge checkpoints #2054 and still excludes ray.sub requires submission from NeMo RL home directory, blocking external workflow organization #1550'sGroupedQuantizer) come frommain; the ambiguous-shard fix from 28beca5 is ported into the new resolver. feat: add fp32 LM head toggle #4096'snemotron_h.pyfp32 LM head patch anchors occur exactly once in 0.29's copy and its anchor tests pass. The Sept 21–22 merges (fix(ppo): rebuild the critic optimizer on a warm start #4213, fix(vllm): strip staged token arrays from capture responses #4206, feat(mopd): support multiple teacher checkpoints in full-vocabulary o… #4045, fix(LOO-Norm-Advantage): Fix spike in LOO normed advantage. #4158, feat(nemo-gym): wire sharded stacks into training #3373, fix(nemo-gym): preserve replica identity during routing #3374, ci: install research folder dependencies & discover research test suites #4216, test: re-enable nemotron omni clevr megatron single-controller L1 test #4231, feat(draft): preserve split-step normalization parity #3706) had no conflicts.torch 2.13 / vLLM 0.29 fallout found by the GitHub CICD and the rerun nightly (0cc6fab, 8e97d91)
_async_process_executor) rendezvous its save daemons on a port rank 0 probes withget_free_port(); the port was re-taken before the daemon bound it and every DTensor async save died at the first checkpoint withDistNetworkError … EADDRINUSE(the L1 GRPO resume test, the converter round-trip test, and thellama3.2-1b fsdp2tp2 tq_mooncakenightly recipe at step 10). Surfaced by Automodel 72daceffa making daemon creation synchronous across ranks;mainon torch 2.11 does not reach it.init_checkpointernow sets torch'sDCP_USE_PREFIX_STORE=1whenMASTER_ADDR/MASTER_PORTare present, so the daemons attach to the process group's store under a prefix. Three unit tests.nvfp4_pertoken.py(from feat: add end-to-end TE NVFP4 training with per-token vLLM rollout #3566) no longer passeslayer=tomake_nvfp4_moe_kernel. vLLM 0.29 dropped the parameter (it takesper_token_activation); the new GB200te_nvfp4_pertoken_quickrecipe died withTypeError: unexpected keyword argument 'layer'; a unit test binds the rebuild's kwargs to the installed factory's signature.2.long_generation_decode_vs_prefill.pygains--gpu-memory-utilization; the L1 test passes 0.7 on GB200 (at 0.8 the FlashInfer TRT-LLM BF16 MoE workspace no longer fit next to 0.29's KV cache) and keeps 0.8 on 80 GiB parts, where 0.7 leaves no KV cache at all.test_vllm_http_serverignores theusage.completion_tokens_detailsfield vLLM 0.29 added toUsageInfo._patch_vllm_moe_routed_experts_capturefiresrouter.select_expertson the monolithic fused-MoE branch when the router carries a capture callback. 0.29'sbind_routed_experts_capturerbinds monolithic kernels to the experts object instead (the FlashInfer launch then fillsrouting_replay_outitself) and rejects kernels without in-kernel capture; the per-token method rebuilds its kernel on every refit, so that binding would be lost after the first weight update and replay would silently see all-zero routes.host_captured_experts_clswraps the method's experts class to report no in-kernel capture, and_patch_vllm_routed_experts_capture_router_fallbackmakes the 0.29 binder fall back torouter.set_capture_fnfor such kernels instead of raising. Kernels that support in-kernel capture keep using it. 7 unit tests (anchor against the installed vLLM, idempotency, fail-closed, an executable check of the patched binder, the wrapper).test_init_checkpointer_*tests carry theautomodelmark so the Automodel shard runs them; a per-token test binds the kernel rebuild to the installedmake_nvfp4_moe_kernelsignature; the import-target walk covers the Dynamo and ModelOpt generation trees; the DeepSeek V4 tests pin vLLM's two distinct skip sets;normalize_cuda_ipc_handleis annotated; stale comments (RayWorkerProc, TRT-LLM mirror, instanttensor, fp8 docstring) and two docs mentions of vLLM 0.25.1 are corrected.docs/guides/dynamo-generation.mdanddocs/guides/grpo-audio-visual.mdversion / pin one-liners, andpyrefly.tomlincludes forvllm_quant_moe_amax.pyandcuda_ipc.py.Known issues not fixed here
grpo-deepseek-v4-flash-0731-16n8g-automodel-cp8ep128(H100) — fails on the trainer side, as onmainDTensorPolicyWorkerV2.get_logprobs()withDeepEP error: timeout (dispatch CPU)— the same point and error as themaincontrol at this PR's earlier base (446267140).main's Sept 20 / 21 nightlies fail it earlier, at vLLM engine start. vLLM path at parity; trainer side broken onmain.grpo-qwen3-30ba3b-4n4g-megatron-te-nvfp4-pertoken-quick(GB200) — resolved; kept here as the record of a 0.29 behaviour changemake_nvfp4_moe_kernel(layer=)was dropped in 0.29 — fixed in 0cc6fab. (2) 0.29 binds routed-experts capture for monolithic kernels to the kernel's experts object and lets the FlashInfer launch fillrouting_replay_outitself; this method rebuilds its kernel on every refit, so router replay would have silently lost its routes after the first refit. af7d9e0 wraps the experts class to report no in-kernel capture and adds a source patch so 0.29's binder falls back to the router hook (the path NeMo-RL's existing_patch_vllm_moe_routed_experts_capturefires, and how this recipe captured on 0.26) instead of raising. (3) With (1) in place the recipe died in the first post-refit generation withCUDA error: an illegal memory accessin the FlashInfer TRT-LLM NVFP4 MoE kernel (450220649). That launch had the in-kernel replay buffer attached — the one FlashInfer launch this recipe exercises that 0.26 never ran. With af7d9e0 routing the capture through the router (so the kernel launches without a replay buffer) the recipe passes 20/20 (450730474) with gen-KL 0.0083 and median prob-error 1.060, matchingmain's 0.0082 / 1.058, and router-replay validation on. The per-token FlashInfer launch withrouting_replay_outattached remains unusable on flashinfer 0.6.18 and is worth reporting upstream.mainpasses the recipe on Sept 20 and aborts on Sept 21 in the HybridEP handle import (see W4A4 below).AttributeError: module 'torch.multiprocessing.reductions' has no attribute '_rebuild_cuda_tensor_original'megatrontp1_sglang)main(Sept 20 and 21 fail identically); refits 1–10 through the new torch-2.13→2.11 IPC path work. The colocated fsdp2 + sglang recipe passes on H100 (450/450) and on GB200 reaches step ~295/450 before its 150-min Slurm limit, whichmainalso hits. Non-colocated sglang passes on both SKUs.grpo-qwen3-30ba3b-4n4g-megatron-qa-nvfp4-w4a4-realmaintoo. (a)MegatronQuantPolicyWorkerranks abort at step 1 in DeepEP's HybridEP buffer allocation (cuMemImportFromShareableHandleinNVLCoordinator::open_handles_from_other_ranks, then SIGABRT) —mainSept 21, 19 and 18 abort identically, Sept 20 and 16 pass; node/fabric dependent. (b) When it runs, it trains 2/2 with reward and accuracy passing andjs_divergence_error[2]at the 0.007 bound (0.00694 / 0.00718 on the two 0.29 samples vs 0.0039–0.0044 onmain; gen-KL 0.023 vs 0.015). The H100 W4A16 recipe matchesmainexactly, so (b) is the NVFP4 activation-quant kernels on Blackwell, not NVFP4 weights generally.nixl_epextension built against torch 2.11 —nixl_ep_cpp.cpython-313-…so: undefined symbol _ZN3c104impl3cow23materialize_cow_storage…has_nixl_ep()probe catches it and disables the optionalnixl_epall2all backend, but the traceback appears in every vLLM worker log and nemo-ci's error extractor picks it as the "cause" of unrelated failures. Bump nixl to a torch-2.13 build in a follow-up.grpo-nemotron3-super-120BA12B-8n4g-megatron(GB200) — host OOM during the step-5 checkpoint savemainthis recipe dies at its first refit (Unquantized FlashInfer TRTLLM refit does not yet support a co-trained MTP drafter; Sept 20 and 21). On 0.29 it refits and trains all 5 steps, then the Megatron dist-ckpt save host-OOMs the 4-GPU GB200 nodes. The save had never been reached before; not a vLLM path.mopd-qwen3-1.7b-3n4g-megatron-pack-single-controller-fullvocab(GB200) — never starts: pyxis cannot extract the sandbox squashfsUSES_SANDBOX=1recipe in the GB200 nightly;mainSept 20 and 21 fail identically at Slurm step 0. nemo-ci provisioning on oci_hsg. The H100 3n8g twin passes.llama3_2_1b_instruct_1n4g_fsdp2tp1_v3(GB200) step time 14.8–15.1 s vs< 14.5(main13.7–15.1 s).clevr_8n4g_megatron_generation_v1(GB200, Megatron generation) final accuracy 0.59–0.62 against> 0.6/+0.01(main0.59–0.64).mxfp8_rollouts_flashinfer(GB200, Megatron generation): 10/10, one-step prob-error spike and 57 s steps vs< 48(mainfails the same recipe on prob-error and step time).sft_gpt_oss_20b_1n8ggrad_norm[50] > 10.0: PR 9.74–10.12,main9.83–10.20.mainSept 20 hit it on both mopd recipes. All three passed on retry.VLLM_ALLREDUCE_USE_FLASHINFER=1)vllm_cfg.env_varsif one appears.Notes for reviewers
uv sync --extra sglanganduv sync --extra trtllmproduce torch-2.11 venvs, everything else torch 2.13. The base rangetorch>=2.11,<2.14is what the driver venv resolves from; the ceiling is deliberate so a relock cannot move the driver past what the inference venvs can talk to. If a future sglang-miles / TRT-LLM release moves to torch 2.13, drop their pins; the shim then sees only version-3 handles on both sides and can be removed.'c'handles, and never consults the consumer. It lowers the version byte of any'c'handle above 2. torch 2.13 changed the wire format of expandable-segment ('e') handles along with the version byte; those are left alone. The trainer emits'e'handles underPYTORCH_CUDA_ALLOC_CONF=expandable_segments:True, which vLLM (same torch) opens fine and a torch 2.11 consumer (sglang / TRT-LLM, colocated) rejects with torch's generic "future version" error, so keep that allocator setting off for those two backends; none of the shipped sglang / TRT-LLM recipes sets it.main. After be440de the lock is at or abovemain's resolution for every package except the deliberate pins; the lock diff is therefore larger than the dependency bullets alone would suggest. Compare againstmainwithgit show origin/main:uv.lockif in doubt.DCP_USE_PREFIX_STORE. The flag is torch's own opt-in (torch/distributed/checkpoint/_async_process_executor.py); it is set only when the training rendezvous variables are present, and an explicit value in the environment wins. The daemons then need no free port at all, which is also the right behaviour on hosts where the ephemeral range is contended.docker/Dockerfilestill forces Gym's server venvs toNEMO_GYM_VLLM_VERSION=0.25.1, which works against this branch (all Gym-path recipes pass). Gym is being bumped separately in build: bump vllm to 0.29.0 Gym#3505 (vLLM 0.29 + flashinfer 0.6.18; it has to drop Gym'spycountryexclusion because vLLM 0.29 importsmistral_commoneagerly); a follow-up RL PR will bump the Gym submodule and that ARG together once it merges.[tool.uv]exclusion that was load-bearing at 4.5.x (180 overlapping files, verified from the wheels; 0 at 4.6.x).uv pip installof the overlapping pair silently picks a winner per file, which is why themcorepin moved to 4.6.2 too.instanttensoris the one uv override that drops a dependency on one arch. 0.2.0 does ship aarch64 wheels; the skip keeps an unused loader out of the GB200 image, and lifting the marker is all it takes to enableload_format="instanttensor"there.mm_device_do_normalize=Falseflips a vLLM default that, in 0.29, only Qwen2-VL / Qwen2.5-VL opt into (every other VLM is forced off by vLLM itself), so it affects those two families. It restores the 0.25 behaviour byte-for-byte on the processor side; the cost is the CPU normalize vLLM's change was avoiding.main's43fd41a58; this PR adds the "do not move past ray.sub requires submission from NeMo RL home directory, blocking external workflow organization #1550" note inpyproject.toml(GroupedQuantizer, see the e991336 bullet).vllm-marked unit suite (429 items undertests/unit/models/generation,utils,policyon this head; 2 need a CUDA device) was re-run against the branch lock + vLLM 0.29 after everymainmerge, alongside the no-vLLM pass the L0 shards do over the same files.Test plan
Nightlies. Full
nightlysuites on both SKUs on the final lock (be440de; the later commits touch a functional-test script, the per-token NVFP4 capture path, tests, comments, docs, and — in ab6b3f2 — the trtllm venv's cuda-bindings pin, validated separately below). H100 lane 69151691 and GB200 lane 69183689, both complete. Both are compared recipe by recipe withmain's Sept 20 scheduled nightly (68861701, RL 880a37a — exactly the merge point of d104085, vLLM 0.26.0) and Sept 21 (69003177, RL 612d527, one commit behind the 094a932 that 51d7ee4 merged). Two earlier full passes drove the fixes: fe6945a (H100 68529938, GB200 68535477) found the DeepSeek V4 refit changes; 8e97d91 (H100 68945599, GB200 68937624) found the three torch-2.13 items in 0cc6fab and the fla-core lag in 5200efa.Targeted validation of the fixes made after 8e97d91. EADDRINUSE fix, H100 69074478:
llama3_2_1b_instruct_1n8g_fsdp2tp2_temp0_8_topp0_9_topk50_tq_mooncakesaves every checkpoint and runs to step 470/500 before the recipe's own 180-min Slurm limit, exactly wheremainstops. fla-core relock, GB200 69076114:qwen3_5_35ba3b_6n4g_async_1off_bf16_trtllmtrains 16 steps with gen-KL 0.0010–0.0012 and no fault, where every earlier 0.29 run died at step 1, then hitsDisk quota exceededon the oci_hsg CI filesystem at step 17 (450537401);w4a4_realaborted a fourth time in the DeepEP handle import thatmainhits too. trtllm cuda-bindings pin, GB200 69220858:qwen3_1_7b_2n4g_fsdp2_trtllmon ab6b3f2 passes 10/10 (gen-KL 0.0010, reward 0.875, 22 s/step; 451393519). Per-token NVFP4 capture path, GB200 69135677:te_nvfp4_pertoken_quickpasses 20/20 withmain-level goldens.clevr_8n4g_megatron_generation_v1rerun (GB200 69074482): 50/50 steps, lands in the same 0.59–0.64 final-accuracy band asmain.mainfailsmainpassesThe "PR fails,
mainpasses" rows are itemised in the table below with their cause. Several GB200 jobs on this lane spent an hour or more in the code-snapshot copy while the oci_hsg CI project's Lustre quota was full; the ones that then died onDisk quota exceededare marked as infrastructure.GitHub CICD (L0 unit shards incl. the no-vLLM and
--vllm-onlypasses over the vLLM tests, L1 functional tests on both SKUs) is green on the current head ab6b3f2 (run 35711529567), as it was on af7d9e0. One H100 shard,L1_Functional_Tests_Megatron_4, is flaky on this branch and onmain's runners: in two of five runsgrpo_megatron_generation_colocated.shtrains its 2 steps and the driver then exits non-zero without output; a plain re-run passes, and the GB200 copy of the shard passes every time.Results — nightly, final lock be440de (both lanes complete)
PR test pipelines: H100 69151691 · GB200 69183689.
maincontrols: the Sept 21 scheduled nightly 69003177 (RL 612d527, one commit behind the 094a932 that 51d7ee4 merged) and the Sept 20 one 68861701 (RL 880a37a, exactly the merge point of d104085; vLLM 0.26.0 on both). One row per recipe (checkout bounces retried; latest attempt shown).PR fails, main passes (11)
llm_grpo_llama3_2_1b_instruct_1n4g_fsdp2tp1_v3GB200
mean(total_step_time, -6, -1) < 14.5misses at 14.9–15.1 s on every PR lane (14.92 on 8e97d91, 15.07 on be440de). PR runs 14.8–15.1 s;main13.9 s on Sept 20 (pass), 13.7–15.1 s over Sept 8–9. About 5 % on a 1-GPU-per-node recipe whose generation share is unchanged; see Known issues.llm_grpo_nanov3_30BA3B_4n4g_megatron_generation_noncolocated_mxfp8_rolloutsGB200
token_mult_prob_error1.25 and step time 24.8 s all pass, buttoken_mult_prob_error[10] < 2.0reads 464.6 — one outlier token on the last step while the distribution-level check stays flat. Same shape as the_flashinfertwin's step-10 spike (22.8) on the previous lane andmain's Sept 20 miss on that twin (median 1.56). Passed on the 8e97d91 lane and onmainSept 21; vLLM is not in this recipe's loop.llm_grpo_qwen2_5_math_1_5b_instruct_1n4g_megatron_single_controller_syncGB200
Disk quota exceededbefore its first step. Passed on the 8e97d91 lane (single-controller sync, 450/450) and onmainSept 20;mainSept 21 failed the same recipe without a metric table. (main Sept 21: failed.)llm_grpo_qwen3_1_7b_2n4g_fsdp2_trtllmGB200
AttributeError: 'cuda.bindings.runtime.cudaIpcMemHandle_t' object has no attribute 'reserved'intensorrt_llm/_ipc_utils.py::open_ipc_memorywhile allocating the TP>1 all-reduce fusion workspace at engine start.uv lock --upgradehad moved cuda-bindings / cuda-python 13.3.1 → 13.4.x (mainis on 13.3.1), and 13.4 dropped thereservedfield TensorRT-LLM 1.3.0rc21 reads. Thetrtllmextra now pins both<13.4; on that pin the recipe passes 10/10 (451393519, gen-KL 0.0010). The TP=1 TRT-LLM recipes (qwen2_5_0_5b_1n4g_megatron_trtllm_noncolocated_async,qwen3_5_35ba3b_6n4g_async_1off_bf16_trtllm) never allocate that workspace and passed on the same lock.llm_grpo_qwen3_30ba3b_4n4g_megatron_qa_nvfp4_w4a4_realGB200
main.MegatronQuantPolicyWorkerranks abort at step 1 in DeepEP's HybridEP buffer allocation:cuMemImportFromShareableHandlefails inNVLCoordinator::open_handles_from_other_ranksand the process SIGABRTs before any golden.mainfails identically on Sept 21 (449569744), Sept 19 (447326502) and Sept 18 (445908944); it passed on Sept 20 and 16; our retry (449824224) aborted the same way. Node/fabric-dependent. When the recipe does run on 0.29 (two earlier samples) it trains 2/2 with reward and accuracy passing andjs_divergence_error[2]at the 0.007 bound (0.00694 / 0.00718;main0.0039–0.0044) — see Known issues. (main Sept 21: Signal/OOM-Kill.)llm_grpo_qwen3_30ba3b_4n4g_megatron_te_nvfp4_pertoken_quickGB200
make_nvfp4_moe_kernel() got an unexpected keyword argument 'layer'(vLLM 0.29 dropped the parameter) is fixed in 0cc6fab. With that fix (450220649) the recipe refits and dies in the first generation withCUDA error: an illegal memory accessin the FlashInfer TRT-LLM NVFP4 MoE kernel. 0.29 also changed how routed experts are captured for monolithic kernels (bound to the kernel's experts object, which this method rebuilds on every refit, so router replay would have silently lost its routes after the first refit); af7d9e0 routes the capture through the router hook NeMo-RL already patches in, as on 0.26, so the kernel launches without a replay buffer. With both fixes the recipe passes 20/20 (450730474: gen-KL 0.0083, median prob-error 1.060 —mainSept 20: 0.0082 / 1.058; router replay validation on).mainpasses on Sept 20 and aborts on Sept 21 in the HybridEP handle import (see W4A4). (main Sept 21: Uncaught Exception.)llm_grpo_gemma3_1b_it_1n8g_fsdp2tp1_tq_simpleH100
median(token_mult_prob_error) < 1.1passes at 1.009;mean(total_step_time, -6, -1) < 14misses at 19.3 s while ~120 lane jobs shared the cluster. Passed on the 8e97d91 rerun and onmainSept 20/21 in quieter lanes.llm_grpo_llama3_2_1b_instruct_1n8g_megatron_temp0_8_topp0_9_topk50_tq_mooncakeH100
mainsits on the same edge (Sept 20 pass at 171 min, Sept 21 TIMEOUT at 176 min). (main Sept 21: Slurm time limit.)llm_mopd_qwen3_1_7b_3n8g_megatron_packH100
cluster readyagainst an 18-min limit. Passed on the 8e97d91 rerun (retry) and onmainSept 21. Not retried for the same reason as the SFT recipe above. (main Sept 20: Signal/OOM-Kill.)llm_sft_gpt_oss_20b_1n8g_fsdp8ep8_automodelH100
grad_norm[50] > 10.0misses at 9.88; PR runs 9.74–10.12,main9.83–10.20 (fails Sept 20 at 9.83, passes Sept 21). The bound sits inside the noise of this recipe on both branches.llm_sft_qwen3_0_6B_1n8g_megatron_yarn_128kH100
cluster ready(about 120 lane jobs launched together after the checkout-bounce retries) and the 15-min Slurm limit fired during teardown. SFT, no vLLM. Not retried: a retry of a completed recipe short-circuits on the existing metrics.PR fails, main had no usable result (2)
llm_dpo_qwen2_5_math7b_1n8g_megatron_fused_linear_logprobsH100
main. DPO, no vLLM;train/accuracy[10] >= 0.5at 0.406.mainbounced at checkout on Sept 20 and 21; fails on Sept 8 and 9. (main Sept 21: died at checkout (shared-workspaceBad address); Sept 20: died at checkout (shared-workspaceBad address).)llm_grpo_nanov3_30ba3b_3n8g_megatron_dynamo_swe1H100
main.ValueError: NeMo Gym does not support generation backend 'dynamo';mainfails whenever it gets past checkout (Sept 8, 9; bounced Sept 20/21). (main Sept 21: died at checkout (shared-workspaceBad address); Sept 20: died at checkout (shared-workspaceBad address).)PR fails, main fails (27)
llm_dpo_nanov3_30B3AB_1n4g_fsdp4ep4_automodelGB200
main. DPO, no vLLM.mean(total_step_time, -5, -1) < 5at 17.3 s;mainSept 20 / 21 fail the same check at 16.5 / 16.1 s plus loss goldens.llm_grpo_moonlight_16ba3b_4n4g_megatronGB200
main.KeyError: decoder.layers.N.self_attention.linear_q_proj.layer_norm_weight from model not in checkpointat Megatron load;mainSept 20 and 21 fail identically.llm_grpo_nanov3_30BA3B_4n4g_megatron_generation_colocated_reshard_async_gymGB200
main. Megatron generation; Gym rejects the rollouts (pydantic ValidationError … NeMoGymChatCompletionMessageForTraining) until the time limit.mainSept 20 and 21 fail identically. On the be440de lane the node died first (CUDA driver error: uncorrectable NVLink error).llm_grpo_nanov3_30BA3B_4n4g_megatron_generation_noncolocated_mxfp8_rollouts_flashinferGB200
main. Now trains 10/10 (main's #3630 fixed the earlier abort) and misses two goldens:token_mult_prob_error[10] < 2.0(a single-step spike to 22.8; median 1.32 passes, gen-KL 0.042 passes) andmedian(total_step_time) < 48(57.2 s; 55.9 s on the be440de lane, where the prob-error checks passed).mainSept 20 fails the same recipe onmedian(token_mult_prob_error) < 1.5(1.56) and the same step-time bound (55.1 s); Sept 15–19 fail too. Megatron generation with FlashInfer MXFP8 rollouts; vLLM is not in the loop.llm_grpo_nanov3_30ba3b_4n4g_megatron_qa_nvfp4_w4a16_realGB200
main. Completes 1/1 and missesgen_kl_error[1] < 0.003(0.0116) andmax(token_mult_prob_error) < 1.05(1.094);mainSept 20 misses the same two checks with the same values (0.0112 / 1.094).llm_grpo_nemotron3_super_120BA12B_8n4g_megatronGB200
mainthe recipe dies at its first refit (Unquantized FlashInfer TRTLLM refit does not yet support a co-trained MTP drafter; Sept 20 and 21). On 0.29 it refits and trains all 5 steps, then the Megatron dist-ckpt save host-OOMs the 4-GPU nodes. See Known issues.llm_grpo_qwen2_5_math_1_5b_instruct_1n4g_fsdp2tp1_sglangGB200
main. Colocated sglang on GB200 reaches step ~295/450 and hits the 150-min Slurm limit;mainSept 20 and 21 hit the same limit.llm_grpo_qwen2_5_math_1_5b_instruct_1n4g_megatrontp1_sglangGB200
main. Colocated sglang dies at the step-10 async checkpoint ontorch.multiprocessing.reductions._rebuild_cuda_tensor_original;mainSept 20/21 fail identically (same step, same attribute) and then sit until the Slurm limit.llm_mopd_qwen3_1_7b_3n4g_megatron_pack_single_controller_fullvocabGB200
failed to create container filesystemextracting the sandbox squashfs on oci_hsg.mainSept 20 and 21 fail identically; the H100 3n8g twin passes.llm_dapo_nanov3_5_30BA3B_4n8g_automodelH100
main.ValueError: Couldn't instantiate the backend tokenizer(HF asset not in the CI cache);mainSept 20 and 21 fail identically.llm_dpo_mistral_nemo_instruct_2407_1n8g_fsdp2tp8_actckpt_longH100
main. DPO, no vLLM;train/loss[1] < 0.70at 0.7087 —mainSept 20 fails on the identical value (0.7087241); Sept 21 could not submit to Slurm.llm_dpo_nanov3_30B3AB_1n8g_fsdp8ep8_automodel_v2H100
main. DPO, no vLLM;mean(total_step_time, -5, -1) < 5at 5.32 s;mainSept 20 fails the same check (5.20 s) plus two loss goldens; Sept 21 could not submit to Slurm.llm_grpo_deepseek_v4_flash_0731_16n8g_automodel_cp8ep128H100
main. The refit and step-1 generation complete; the run dies inDTensorPolicyWorkerV2.get_logprobs()withDeepEP error: timeout (dispatch CPU), the same point and error as themaincontrol at this PR's earlier base (446267140).main's Sept 20 / 21 nightlies (vLLM 0.26.0) fail the recipe earlier, at vLLM engine start (RuntimeError: Worker failed with error ''×11, then the step is cancelled).llm_grpo_llama3_2_1b_instruct_1n8g_fsdp2tp2_temp0_8_topp0_9_topk50_tq_mooncakeH100
get_free_port()probe that is re-taken before the daemon binds it (DistNetworkError … EADDRINUSE). Same crash in the GitHub L1 GRPO checkpoint test and the converter test on this head.init_checkpointernow opts the daemons intoDCP_USE_PREFIX_STORE=1so they join the training TCPStore instead of binding a port. Validated on the fixed head (450067839: no EADDRINUSE, every 10-step checkpoint saved, 470/500 steps until the recipe's 180-min Slurm limit — exactly wheremain(torch 2.11, never reaches the bug) stops on Sept 20 and 21). What remains is that pre-existing time limit.llm_grpo_moonlight_16ba3b_4n8g_megatronH100
main.KeyError: decoder.layers.N.self_attention.linear_q_proj.layer_norm_weight from model not in checkpointat Megatron load;mainSept 20 and 21 fail identically.llm_grpo_moonlight_16ba3b_4n8g_megatron_fp8_e2eH100
main.mainSept 20 and 21 fail this recipe at the same point (Megatron load).llm_grpo_moonlight_16ba3b_4n8g_megatron_tq_simpleH100
main.KeyError: decoder.layers.N.self_attention.linear_q_proj.layer_norm_weight from model not in checkpointat Megatron load;mainSept 20 fails identically, Sept 21 bounced at checkout.llm_grpo_qwen2_5_math_1_5b_instruct_2n8g_megatrontp1_sglangH100
main. Colocated sglang dies at the step-10 async checkpoint ontorch.multiprocessing.reductions._rebuild_cuda_tensor_original;mainSept 20/21 fail identically (same step, same attribute) and then sit until the Slurm limit.llm_grpo_qwen3_30ba3b_4n8g_megatron_qa_nvfp4H100
main. Fake-quant NVFP4 ranks abort at step 1 (Fatal Python error: Aborted,cumem_allocator.cpponmain);mainSept 20 fails identically, Sept 21 bounced at checkout.llm_grpo_qwen3_5_35ba3b_2n8g_automodel_ep16H100
main.RuntimeError: DeepEP error: timeout (dispatch CPU)at step 1;mainSept 20 and 21 fail identically.llm_grpo_qwen3_8_27b_2n8g_megatron_tp4pp2cp2H100
main. Dies in transformers at model load;mainSept 20 and 21 fail identically.llm_ppo_qwen2_5_1_5b_gsm8k_2n8g_megatron_valuetp2sp_dynbatch_noncolocated_async_single_controllerH100
main. Config validation:SingleController checkpointing with a replay-checkpoint-capable sampler requires checkpointing.save_period;mainSept 20 and 21 fail identically.llm_prorlv2_qwen2_5_math_1_5b_instruct_1n8g_fsdp2tp1_v2_tq_mooncakeH100
main. Slurm time limit at step 376/450 (refits healthy);mainSept 20 and 21 hit the same limit.vlm_vlm_grpo_nemotron_omni_30ba3b_clevr_1n8g_megatron_tp8ep8_v1H100
main. CUDA OOM at step 1;mainSept 20 and 21 fail identically.vlm_vlm_grpo_nemotron_omni_30ba3b_mmpr_4n8g_megatron_tp8ep16_v1H100
main.AssertionError: defer_fp32_logits must be True if logprob_chunk_size is set;mainSept 20 and 21 fail identically.vlm_vlm_grpo_qwen3_5_35ba3b_geo3k_2n8g_automodel_ep16H100
main.RuntimeError: DeepEP error: timeout (dispatch CPU)at step 1;mainSept 21 (Sept 20 bounced at checkout) fail identically.vlm_vlm_grpo_qwen3_5_35ba3b_geo3k_2n8g_automodel_ep16_tq_simpleH100
main.RuntimeError: DeepEP error: timeout (dispatch CPU)at step 1;mainSept 21 (Sept 20 bounced at checkout) fail identically.PR passes, main fails (2)
llm_dapo_gemma4_e2b_it_1n8g_fsdp2_automodelllm_sft_gpt_oss_20b_1n4g_fsdp4ep4_automodelPass on both PR and main: 139 (H100 108, GB200 31); the list is in a comment below to keep this description under GitHub's size limit.
🤖 Generated with Claude Code