build: bump vllm to 0.29.0 - #4080
Draft
yfw wants to merge 15 commits into
Draft
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)
Supply unused CP-local position IDs to satisfy Megatron-Core's MTP assertion. Preserve existing positions and exclude multimodal, subclass, and learned-absolute-position paths. Add regression tests. (cherry picked from commit 2cbc29d)
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do ?
Bumps vLLM from 0.25.1 to 0.29.0 (released 2026-09-09), which moves the whole environment to torch 2.13.0 (torchvision 0.28.0, flashinfer 0.6.18, nvidia-cutlass-dsl 4.6.2, tilelang 0.1.12, llguidance 1.7.x). Supersedes #4058 (the 0.28.0 bump): every commit from that branch is carried over unchanged, plus the two 0.28 → 0.29 commits at the top. Draft until the nightlies on both SKUs have been compared against
main.0.28.0 → 0.29.0 delta that matters here (checked against the 0.29.0 wheel metadata and source):
flashinfer-python/-cubin/-jit-cache0.6.16.post3→0.6.18(cu130 wheels for both arches) and a new unconditionalinstanttensor>=0.1.9;transformers>=5.10.4is already satisfied by the locked 5.12.1.make_fp8_moe_kernelsignature,is_quantized_kv_cache/kv_cache_uses_per_token_head_scales,RoutedExperts.load_weightsname resolution,mm_device_do_normalize) is unchanged in 0.29.0.mm_device_do_normalizeencoder-cudagraph safe vllm-project/vllm#55370 (encoder-cudagraph fix for on-device image normalization); we default that path off anyway.Changes
Bullets are prefixed with the commit that made the change.
Dependencies (
pyproject.toml/uv.lock)0.25.1→0.28.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.13→0.6.16.post3,nvidia-cutlass-dsl[cu13]4.5.2→4.6.2in thevllmextra, tilelang<0.1.13,llguidance>=1.7.0,<1.8.0. 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.nvidia-cutlass-dsl-libs-baseis no longer excluded. The exclusion worked around cutlass#3259 (the 4.5.xlibs-baseandlibs-cu13wheels overwrite 180 shared files, 99 with different content). 4.6 split the wheels cleanly:libs-baseships the entire PythoncutlassDSL andlibs-cu13only the_cutlass_ir.cu13extension, zero overlapping files (verified by unzipping 4.6.0 and 4.6.2). flashinfer 0.6.16+'sgdn_prefill.pyimportscutlass.cuteat module load and vLLM 0.28+'sqwen_gdn_linear_attn.pyimportsgdn_prefilleagerly, so with the exclusion in place every Qwen3.5 worker and every Blackwell vLLM worker died withModuleNotFoundError: No module named 'cutlass.cute'(17 nightly tests plus an external 64-node super run). Themcoreextra's ownnvidia-cutlass-dsl==4.5.2pin moves to 4.6.2 so the training venv does not inherit the 4.5.x overlapping pair (flashinfer 0.6.8.post1 accepts>=4.4.2). The only fork still on 4.5.0 istrtllm(via tensorrt-llm), which is unusable on torch 2.13 anyway — see Known issues.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"(6 attempts, 22–63 kills each). 4×4 built cleanly on aarch64 (2h47m) and x86 (~2h, alongside TE and flash-mla).<2.5(resolves 2.4.6). vLLM 0.28+ 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.c3b913b9→5dde396bd(Aug 4). The old vLLM plugin registers a quant module onfused_moe.layer.FusedMoE, which 0.28 removed. ModelOpt swallows thatAttributeErrorinsideimport_plugin, leavingRowParallelLinear/ColumnParallelLinear/… registered but the module gone fromsys.modules; NeMo-RL's explicit re-import invllm_quant_patch.pyre-runs the decorators and every fakequant nvfp4 test died withAssertionError: RowParallelLinear already registered!. 5dde396bd ported the plugin toRoutedExperts. It is pinned there deliberately and 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 (found when the first 0.28 pass ran on ModelOptmain,613e5e8b).0.28.0→0.29.0,flashinfer-python/-cubin/-jit-cache0.6.16.post3→0.6.18.instanttensorscoped to x86_64 via an override: 0.29 lists it unconditionally, but it is the optionalload_format="instanttensor"safetensors loader, only publishes x86_64 wheels (cp310–cp314), and its sdist compiles libaio, liburing and boost with setuptools. vLLM imports it lazily insideinstanttensor_weights_iteratorand NeMo-RL never selects that load format, so the wheel is installed on x86_64 and skipped on aarch64 (GB200) instead of being built from source in the SBSA image.main): numpy 2.5.1 → 2.4.6, cutlass-dsl 4.5.2 dropped,libs-base4.5.0/4.6.0/4.6.2 andlibs-core4.6.0/4.6.2 added, modelopt 0.46.0.dev86 → 0.47.0.dev25+g5dde396bd, vllm 0.29.0, flashinfer 0.6.18, instanttensor 0.1.9 (x86_64 only).uv lock --checkpasses on the pristine parent and on the new lock;tests/unit/test_dependency_pins.pypasses.vLLM source patches / worker (
patches.py,vllm_worker.py)_patch_vllm_shm_broadcast_bind_retryre-targeted: 0.28+ 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. Still applies verbatim on 0.29.0._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:RayWorkerV2.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 the patch closed on 0.25–0.28 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.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, pause-completion path) andWorkerBase.init_workerasserts that a worker extension never shadows aWorkerattribute, so every vLLM engine on the first 0.29 nightly died at init withAssertionError: Worker class <vllm.v1.worker.gpu_worker.Worker> 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 recipes:qwen3_30ba3b_thinking_swe1_16n8g_megatron_cp2_r3_async_gym,qwen3_1_7b_1n8g_megatron_super_swe1) died on pass 3 withModuleNotFoundError: 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. A newvllm-marked test walksnemo_rl/models/generation/vllmwithast, collects all 86from vllm… importstatements (mostly lazy, method-local imports no unit test executes) and resolves them against the installed vLLM.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 on pass 3 (ppo_qwen2_5_1_5b_gsm8k_1n8g_automodel_noncolocated{,_async},dapo_gemma4_e2b_it_1n8g_fsdp2_automodel; the llama3.2-1b DTensor recipes were still running) 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.mm_device_do_normalizedefaults toFalse. vLLM 0.28+ (#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. On the second 0.28 pass this brought the Qwen2.5-VL clevr DTensor recipe back to main-level metrics (token_mult_prob_error 1.019, gen_kl 0.0008, vs 198 / 0.14 before). Opt back in withpolicy.generation.vllm_kwargs.mm_device_do_normalize=true.fp8 generation (
quantization/fp8.py)make_fp8_moe_kernellost itslayerkwarg in 0.28 (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.28'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. Unchanged in 0.29.0.ModelOpt fakequant refit (
nemo_rl/modelopt/models/generation/)input_quantizer._amaxrouted around vLLM's expert loader. 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.28+'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 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 (3 tests); the loader contract is identical in 0.29.0.Megatron (
nemo_rl/models/megatron/)mtp_inputs.input_ids is not Nonefor packed sequences whoseposition_idsareNone; this failed onmain's nightlies Sept 7–9 too (grpo-nemotron3-super-120BA12B-16n8g-megatronand the external 64-node super run). Not a vLLM issue; carried here because the nightlies cannot be compared without it. Regression tests included.Distributed (
collectives.py)all_gather_into_tensor→all_gather_single(torch 2.13 API).Docker (
docker/Dockerfile,3rdparty/TensorRT-LLM-workspace/_backend.py)docker buildx rm/createaround every build job, so thetrtllm-wheel-cache-*cache mount that the hermetic stage compiles the wheel into lives for exactly one build, while the hermetic layer is shared through the registry cache. A rebuild that changes only NeMo-RL source (pass 2 below) therefore reused the layer on a fresh builder, re-executed the release stage, and died on every runner at[release 5/8]withTRT-LLM cached wheel is required but was not found … TRTLLM_REQUIRE_CACHED_WHEEL=1(4 images × 3 attempts, identical). The hermetic stage now setsTRTLLM_WHEEL_CACHE_MIRROR_DIR=/opt/trtllm_wheelsso the backend also writes the content-addressed wheel into the layer, the release stage rsyncs that mirror back into the (possibly empty) mount before the trtllm venv prefetch, and the backend skips the mirror copy when the file already exists so the release layer no longer duplicates the wheel. Final image content is unchanged (/opt/trtllm_wheelsalready held this wheel). Costs one TRT-LLM recompile on the next build; afterwards source-only rebuilds work on any runner. Independent of the vLLM bump and can be split out if preferred.Known issues not fixed here
sgl_kernel/.../common_ops.abi3.so: undefined symbol _ZNK2at10TensorBase14const_data_ptr…3003d70f) pinstorch==2.11.0andsglang-kernel==0.4.5; sglang 0.5.19 moved totorch==2.13.0andsglang-kernel==0.4.6.post1. Needs a sglang-miles rebase; a speculative pin bump is not safe because a faileduv sync --extra sglangfails the entire image build. Notemain's sglang tests also fail today ontorch.multiprocessing.reductions._rebuild_cuda_tensor_original.tensorrt_llm/libs/libth_common.so: undefined symbol _ZNR5torch7Library4_def…inTrtllmAsyncGenerationWorker.__init__torch<=2.13.0a0).ModelOpt layerwise reload is incomplete for 23 layer(s): …routed_experts: 134701312/179601664 elementsgrpo-nanov3-30ba3b-4n4g-megatron-qa-nvfp4-w4a16-real(GB200)RoutedExperts. The w4a4 sibling passes. Needs an audit of the real-quant refit against the new MoE weight layout.mainSept 8 ran to completion and failed only on golden metrics.vlm_grpo-qwen3-omni-30ba3b-audiomcq-4n8g-megatron.v1, gloo recv timeout (1800 s) insideVllmGenerationWorker.generatemain. 0.28+ runs external-DP engines in lockstep (execute_dummy_batchon idle ranks), and this is the only recipe where one DP rank finishes a step with no work while another still generates. Not yet root-caused.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) —Unquantized FlashInfer TRTLLM refit does not yet support a co-trained MTP draftermainSept 8 identical). Blackwell picks the FlashInfer TRTLLM BF16 MoE backend, and #3545 rejects co-trained MTP on that path. Either addmoe_backend: tritonto the recipe'svllm_kwargs(supported since 0.25) or extend_weight_update_lifecycleto include the drafter's TRTLLM modules in the layerwise reload. Separate PR.VLLM_ALLREDUCE_USE_FLASHINFERdefault0→1inenvs.py)VLLM_ALLREDUCE_USE_FLASHINFER=0invllm_cfg.env_varsif a TP recipe regresses.Notes for reviewers
[tool.uv]exclusion that was load-bearing at 4.5.x. I verified the 4.5.2 overlap (180 files) and the 4.6.x non-overlap (0 files) directly from the PyPI wheels, anduv pip installof the overlapping 4.5.2 pair does not error — it silently picks a winner per file — which is why themcorepin had to move to 4.6.2 as well.instanttensoris the one place this PR relies on a uv override marker to drop a dependency on one arch. If someone wantsload_format="instanttensor"on GB200 later, the sdist would need an aarch64 wheel or a toolchain in the SBSA image.mm_device_do_normalize=Falsechanges a vLLM default for every multimodal recipe. It restores the 0.25 behaviour byte-for-byte on the processor side; the cost is the CPU normalize vLLM's PR was avoiding.main, with a "do not move past" note inpyproject.toml; the reason (GroupedQuantizer) is in the 7696b89 message.tests/unit/test_dependency_pins.py,test_vllm_quant_moe_amax.py,test_vllm_modelopt_real_quant_config.py, the newtest_vllm_tcpstore_port.py::test_patch_recognizes_upstream_fix_and_leaves_source_alone(executed outside thevllmmarker filter).test_vllm_fp8_quantization.pyand thevllm-marked port tests need the rebuilt image (no vLLM in the host venv).Test plan
nightlysuite on both SKUs against the PR head, compared per test with the same-daymainnightly. Results table will be appended in the format used on build: bump vllm to 0.28.0 #4058 / build: bump vllm 0.20.0 → 0.25.1 #3280 once the pipelines finish.disabled.txtqwen3.5 entries: not yet run.Results — nightly
Pass 1 — PR head b863e80, nemo-ci#67220071 (tests: H100 67285306, GB200 67285294). Images built uncached in 142 min (x86) / 177 min (sbsa); traces confirm vllm 0.29.0, flashinfer 0.6.18, torch 2.13.0, ModelOpt 0.47.0.dev25+g5dde396bd,
instanttensorpresent on x86 only and no source build on sbsa. Every vLLM-based recipe failed at engine init on thesynchronize_deviceassertion above (~12 min per job), so this pass only yields results for the non-generation recipes (SFT/DPO/RM/distillation-without-vLLM) and confirms the two known ABI failures (sglang-kernel). 17 H100 jobs additionally bounced at git checkout on the corruptedAg1sxjDZu/10/5wSiZLJL2/7workspaces and were retried (some up to three times). Once the assertion was confirmed systematic, the 113 remaining vLLM-generation jobs in this pass were cancelled to free the clusters for pass 2; the 31 non-generation and sglang jobs (SFT/DPO, both SKUs) were left to finish and count as this PR's results for those recipes.Pass 2 — PR head 8b6709e (adds the
synchronize_devicerename), nemo-ci#67287391. Never produced an image: all four builds failed at[release 5/8]on the missing TRT-LLM cached wheel described under Docker above (12 attempts across 9 different runners). Cancelled.Pass 3 — PR head 168aaac (adds the wheel mirror), nemo-ci#67293195. The hermetic TRT-LLM step recompiles once (≈80 min on arm in pass 1); the uv layers stay cached. Compared against the Sept 9
mainnightly nemo-ci#66919718 (H100 68 pass / 32 fail, GB200 26 pass / 14 fail) and the 0.28 PR's second pass nemo-ci#67131233 (H100 85 / 15, GB200 25 / 15).Pass 1's kept non-generation jobs so far: 15 H100 + 2 GB200 passed; failures are all pre-existing golden misses that fail identically on
main(dpo_qwen2_5_math7b_1n8g_megatron_fused_linear_logprobsaccuracy 0.406 < 0.5,dpo_nanov3_30B3AB_1n8g_fsdp8ep8_automodel_v2loss/step-time,dpo_nanov3_30B3AB_1n4g_fsdp4ep4_automodelstep-time,dpo_mistral_nemo_instruct_2407_1n8g_fsdp2tp8_actckpt_longloss 0.7087 < 0.70) plus the known sglang-kernel ABI failures on both SKUs.Pass 3 interim (02:50 PT): vLLM engines start (extension injected cleanly),
eagle3,r3_async_single_controllerand the SFT yarn recipe already pass. Two more 0.29 regressions found and fixed: da2f1dc (ErrorResponseimport, Gym/swe1/mopd recipes) and 9a4a747 (tied-embedding alias assertion at refit, every tied-embedding DTensor/automodel recipe). Other failures so far: offline HF cache misses (nanov3.5, qwen3-8-27b), the sglang ABI break, pre-existing moonlight / qwen3.5-automodel-ep16 failures.gemma3_1b_it_1n8g_fsdp2tp1andnanov3_30BA3B_2n8g_megatron_lorafailed early without a driver excerpt in the job trace and are still being root-caused.Per-test table will be appended when pass 3's test children finish.
🤖 Generated with Claude Code