diff --git a/container/deps/vllm/install_vllm.sh b/container/deps/vllm/install_vllm.sh index 46b5fa769b7c..43b7383e8271 100755 --- a/container/deps/vllm/install_vllm.sh +++ b/container/deps/vllm/install_vllm.sh @@ -250,6 +250,56 @@ if [ "$DEVICE" = "cpu" ]; then python3 setup.py bdist_wheel --dist-dir=dist --py-limited-api=cp38 uv pip install dist/*.whl fi + +# Apply vendored vLLM patches for this ref. Used to carry upstream fixes +# that are missing from the pinned release wheel (e.g. PR backports +# dropped between rc and final tags). Skipped when: +# - VLLM_VER is >= 0.21.0 (upstream fix expected to be merged by then) +# - no patches// subdir exists, or it has no *.patch files +# +# rc/post/dev/+ suffixes are stripped before comparison (so v0.21.0rc0 +# also trips the skip). Non-semver refs (commit SHA, branch name) fall +# through to the directory-existence check. +VLLM_VER_BASE=$(echo "$VLLM_VER" | sed -E 's/(rc|\.post|\.dev|\+).*//') +PATCH_DIR="/tmp/deps/vllm/patches/${VLLM_REF}" +if [[ "$VLLM_VER_BASE" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ + printf '%s\n%s\n' "0.21.0" "$VLLM_VER_BASE" | sort -V | head -n1 | grep -qx "0.21.0"; then + echo "vLLM $VLLM_VER >= 0.21 — skipping vendored patches (assumed merged upstream)" +elif [ -d "$PATCH_DIR" ] && compgen -G "$PATCH_DIR/*.patch" > /dev/null; then + echo "\n=== Applying vendored vLLM patches from $PATCH_DIR ===" + # Run from / so cwd isn't ahead of the venv on sys.path. The build + # script is still inside the $INSTALLATION_DIR/vllm source clone here + # (from the earlier `cd vllm`), and that clone contains a `vllm/` + # package directory. Without the cd, `import vllm` resolves the + # source clone, vllm.__file__ points at /opt/vllm/vllm/__init__.py, + # and the patch lands on the build-only source tree — not on the + # installed wheel that the runtime actually imports. Same pattern as + # the post-install verification block above. + VLLM_SITE_PACKAGES=$(cd / && python3 -c "import vllm, os; print(os.path.dirname(os.path.dirname(vllm.__file__)))") + for p in "$PATCH_DIR"/*.patch; do + echo " $(basename "$p")" + # --batch: suppress interactive prompts. Without it, an unmatched + # file path in a hunk produces `File to patch:` on stdin + # and hangs `docker build`. + # --forward: skip patches that already appear applied or reversed — + # benign for re-runs and for wheels that already carry + # the fix (e.g. a v0.20.1.post1 hotfix). + # + # GNU patch exit codes: 0 = clean apply, 1 = hunks rejected or + # skipped via --forward, >=2 = fatal. Accept rc=1 (so the patches + # block doesn't become a brittle build gate); propagate >=2. + rc=0 + patch -p1 --batch --forward -d "$VLLM_SITE_PACKAGES" < "$p" || rc=$? + if [ "$rc" -ge 2 ]; then + echo " fatal: patch exited $rc" + exit "$rc" + elif [ "$rc" -eq 1 ]; then + echo " (already applied or partial; continuing)" + fi + done + echo "✓ vLLM patches applied" +fi + echo "✓ vLLM installation completed" echo "\n=== Installing LMCache from source ===" diff --git a/container/deps/vllm/patches/v0.20.1/0001-pr40932-remove-invalid-deepstack-boundary-check-for-qwen3-vl.patch b/container/deps/vllm/patches/v0.20.1/0001-pr40932-remove-invalid-deepstack-boundary-check-for-qwen3-vl.patch new file mode 100644 index 000000000000..41deb7385389 --- /dev/null +++ b/container/deps/vllm/patches/v0.20.1/0001-pr40932-remove-invalid-deepstack-boundary-check-for-qwen3-vl.patch @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright contributors to the vLLM project +# SPDX-License-Identifier: Apache-2.0 + +From 22631f80a01a04b06398952e77d7890ab660ab10 Mon Sep 17 00:00:00 2001 +From: Isotr0py +Date: Mon, 27 Apr 2026 15:27:06 +0800 +Subject: [PATCH] [Bugfix] Remove invalid deepstack boundary check for Qwen3-VL + (#40932) + +Signed-off-by: Isotr0py +--- + vllm/model_executor/models/qwen3_omni_moe_thinker.py | 11 ----------- + vllm/model_executor/models/qwen3_vl.py | 11 ----------- + 2 files changed, 22 deletions(-) + +diff --git a/vllm/model_executor/models/qwen3_omni_moe_thinker.py b/vllm/model_executor/models/qwen3_omni_moe_thinker.py +index ff8962c82..44e656ab8 100755 +--- a/vllm/model_executor/models/qwen3_omni_moe_thinker.py ++++ b/vllm/model_executor/models/qwen3_omni_moe_thinker.py +@@ -1778,11 +1778,6 @@ class Qwen3OmniMoeThinkerForConditionalGeneration( + return None # If vision tower is skipped + if getattr(self, "deepstack_input_embeds_num_tokens", 0) == 0: + return None +- if num_tokens > self.deepstack_input_embeds_num_tokens: +- raise ValueError( +- "Requested more deepstack tokens than available in buffer: " +- f"{num_tokens=} > {self.deepstack_input_embeds_num_tokens=}" +- ) + + # get deepstack_input_embeds from buffer, and clear the buffer + return IntermediateTensors( +@@ -1824,12 +1819,6 @@ class Qwen3OmniMoeThinkerForConditionalGeneration( + + # clear deepstack_input_embeds in buffer + if num_tokens > 0: +- if num_tokens > self.deepstack_input_embeds_num_tokens: +- raise ValueError( +- "Requested to clear more deepstack tokens than available in " +- "buffer: " +- f"{num_tokens=} > {self.deepstack_input_embeds_num_tokens=}" +- ) + for idx in range(self.deepstack_num_level): + self.deepstack_input_embeds[idx][:num_tokens].zero_() + self.deepstack_input_embeds_num_tokens = 0 +diff --git a/vllm/model_executor/models/qwen3_vl.py b/vllm/model_executor/models/qwen3_vl.py +index 4a0de15f0..2575f634b 100644 +--- a/vllm/model_executor/models/qwen3_vl.py ++++ b/vllm/model_executor/models/qwen3_vl.py +@@ -1716,11 +1716,6 @@ class Qwen3VLForConditionalGeneration( + return None # If vision tower is skipped + if getattr(self, "deepstack_input_embeds_num_tokens", 0) == 0: + return None +- if num_tokens > self.deepstack_input_embeds_num_tokens: +- raise ValueError( +- "Requested more deepstack tokens than available in buffer: " +- f"{num_tokens=} > {self.deepstack_input_embeds_num_tokens=}" +- ) + + # get deepstack_input_embeds from buffer, and clear the buffer + return IntermediateTensors( +@@ -1762,12 +1757,6 @@ class Qwen3VLForConditionalGeneration( + + # clear deepstack_input_embeds in buffer + if num_tokens > 0: +- if num_tokens > self.deepstack_input_embeds_num_tokens: +- raise ValueError( +- "Requested to clear more deepstack tokens than available in " +- "buffer: " +- f"{num_tokens=} > {self.deepstack_input_embeds_num_tokens=}" +- ) + for idx in range(self.deepstack_num_level): + self.deepstack_input_embeds[idx][:num_tokens].zero_() + self.deepstack_input_embeds_num_tokens = 0 +-- +2.43.0 +