rebase: close the remaining vLLM 0.29 drift found by CI 3009

Five further removals/renames, each traced from a failing 3009 job:

- StageEngineCoreProcManager mirrors the parent __init__ rather than calling
  super(), and 0.29 added _request_shutdown_timeout there. The inherited
  shutdown() read it, raised AttributeError, and left the engine core
  subprocesses running, so the test process hung at interpreter exit until
  the 15 minute job timeout. That is the whole of the Engine&Model Executor
  exit-124: its pytest reported 567 passed, then sat idle for 663s. The
  failure surfaced only as a StagePool warning, never as a test failure.

- PromptUpdateDetails.select_text is gone; prompt updates are token oriented
  now. MiniCPM-o 4.5 kept three text-based image/video/audio replacements,
  which crashed stage-0 startup and account for the whole MiniCPM-o cluster.
  They encode the text and select the unk placeholder by token id instead.

- Qwen3-Omni: 0.29 marks audio_feature_lengths keep_on_cpu, while the audio
  tower derives device placement from feature_lens, so the mask comparison
  mixed cuda and cpu tensors. Moved explicitly, matching upstream
  Qwen3OmniMoeThinker._process_audio_input.

- AttentionBackend.get_kv_cache_shape is gone; the per-layer shape comes from
  the spec via compute_layer_kv_cache_shape_bytes, whose content dim is in
  bytes.

- WeightsMapper.get_unstacked_mapper is renamed to get_rename_mapper.

tests/engine/test_async_omni_engine_abort.py::test_abort fails here both with
and without these changes, so it is environmental rather than a regression.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0146Xu5Sgj4vXxLxvLjV5YPj
Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
tzhouamandClaude Opus 5 committed 2026-09-10 00:41:53 +08:00
1 parent 9a399adba8
commit c1e32fc01d
5 files changed
+42 -19

No files matched your search

+13 -4
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
import pytest
import torch
@@ -214,11 +214,20 @@ def test_fish_kvcache_backend_wraps_only_model_instance(monkeypatch):
assert fish_kvcache_backend.get_fish_kvcache_attn_stats()["small_hit_count"] == 1
def _vllm_kv_cache_shape(num_blocks, block_size, num_kv_heads, head_size):
def _vllm_kv_cache_shape(num_blocks, block_size, num_kv_heads, head_size, dtype=torch.float16):
# Source of truth for the paged KV cache layout the backend must unpack.
from vllm.v1.attention.backends.flash_attn import FlashAttentionBackend
# vLLM 0.29 removed AttentionBackend.get_kv_cache_shape; the per-layer shape
# now comes from the spec, with the content dim expressed in bytes.
from vllm.v1.kv_cache_interface import FullAttentionSpec, compute_layer_kv_cache_shape_bytes
return FlashAttentionBackend.get_kv_cache_shape(num_blocks, block_size, num_kv_heads, head_size)
spec = FullAttentionSpec(
block_size=block_size,
num_kv_heads=num_kv_heads,
head_size=head_size,
dtype=dtype,
)
num_pages, num_heads, num_states, content_bytes = compute_layer_kv_cache_shape_bytes(spec, num_blocks)
return (num_pages, num_heads, num_states, content_bytes // dtype.itemsize)
def test_fish_kvcache_backend_unbinds_kv_on_vllm_cache_layout(monkeypatch):
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Tests for component routing for quantization."""
from unittest.mock import MagicMock
@@ -372,7 +372,8 @@ class TestComponentApplyVllmMapper:
"thinker.model": "thinker.language_model.model",
"talker.model": "talker.language_model.model",
}
).get_unstacked_mapper()
# vLLM 0.29 renamed get_unstacked_mapper to get_rename_mapper.
).get_rename_mapper()
config.apply_vllm_mapper(mapper)
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
"""Process manager for omni stage engine subprocesses.
@@ -83,6 +83,12 @@ class StageEngineCoreProcManager(CoreEngineProcManager):
if local_engine_count <= 0:
raise ValueError(f"local_engine_count must be > 0, got {local_engine_count}")
# Mirrors the vLLM 0.29 parent __init__: the inherited shutdown() reads
# this to bound how long in-flight requests may drain. Omitting it makes
# shutdown raise AttributeError, which leaves the engine core
# subprocesses alive and hangs interpreter exit until the job timeout.
self._request_shutdown_timeout = vllm_config.shutdown_timeout
context = get_mp_context()
common_kwargs: dict[str, object] = {
"vllm_config": vllm_config,
@@ -3640,6 +3640,17 @@ class MiniCPMO45OmniLLMMultiModalProcessor(BaseMultiModalProcessor[MiniCPMO45Omn
additional_placeholders.append((modality, sub_pattern))
placeholders += additional_placeholders
# vLLM 0.29 removed PromptUpdateDetails.select_text: prompt updates are
# token oriented now, so encode the replacement text and select the unk
# placeholder positions by token id instead of by text.
unk_token_id = tokenizer.convert_tokens_to_ids("<unk>")
def _select_unk_positions(text: str) -> PromptUpdateDetails:
return PromptUpdateDetails.select_token_id(
tokenizer.encode(text, add_special_tokens=False),
unk_token_id,
)
image_max_slice_nums = hf_processor_mm_kwargs.get("max_slice_nums")
image_use_image_id = hf_processor_mm_kwargs.get("use_image_id")
@@ -3648,14 +3659,13 @@ class MiniCPMO45OmniLLMMultiModalProcessor(BaseMultiModalProcessor[MiniCPMO45Omn
image_size = images.get_image_size(item_idx)
return PromptUpdateDetails.select_text(
return _select_unk_positions(
self.get_image_prompt_texts(
image_size,
item_idx,
max_slice_nums=None if image_max_slice_nums is None else int(image_max_slice_nums), # type: ignore[arg-type]
use_image_id=None if image_use_image_id is None else bool(image_use_image_id),
),
"<unk>",
)
)
def get_video_replacement(item_idx: int):
@@ -3664,10 +3674,7 @@ class MiniCPMO45OmniLLMMultiModalProcessor(BaseMultiModalProcessor[MiniCPMO45Omn
frame_size = videos.get_frame_size(item_idx)
num_frames = videos.get_num_frames(item_idx)
return PromptUpdateDetails.select_text(
self.get_video_prompt_texts(frame_size, num_frames),
"<unk>",
)
return _select_unk_positions(self.get_video_prompt_texts(frame_size, num_frames))
get_replacement = {
"image": get_image_replacement,
@@ -3694,10 +3701,7 @@ class MiniCPMO45OmniLLMMultiModalProcessor(BaseMultiModalProcessor[MiniCPMO45Omn
else:
audio_len = audios.get_audio_length(item_idx)
return PromptUpdateDetails.select_text(
self.get_audio_prompt_texts(audio_len),
"<unk>",
)
return _select_unk_positions(self.get_audio_prompt_texts(audio_len))
return [
*base_updates,
@@ -1084,7 +1084,10 @@ class Qwen3OmniMoeConditionalGenerationMixin(Qwen2_5OmniConditionalGenerationMix
audio_input: Qwen2_5OmniAudioFeatureInputs,
) -> tuple[torch.Tensor, ...]:
input_features = audio_input["input_features"]
audio_feature_lengths = audio_input["audio_feature_lengths"]
# vLLM 0.29 marks audio_feature_lengths keep_on_cpu, and the audio tower
# derives device placement from feature_lens, so move it explicitly.
# Mirrors upstream Qwen3OmniMoeThinker._process_audio_input.
audio_feature_lengths = audio_input["audio_feature_lengths"].to(input_features.device, non_blocking=True)
audio_output_lengths = _get_feat_extract_output_lengths(audio_feature_lengths)