diff --git a/tests/attention/test_fish_kvcache_attn.py b/tests/attention/test_fish_kvcache_attn.py index d38380e13..4a8ea17e8 100644 --- a/tests/attention/test_fish_kvcache_attn.py +++ b/tests/attention/test_fish_kvcache_attn.py @@ -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): diff --git a/tests/diffusion/quantization/test_component_routing.py b/tests/diffusion/quantization/test_component_routing.py index 943411779..6aebd6b60 100644 --- a/tests/diffusion/quantization/test_component_routing.py +++ b/tests/diffusion/quantization/test_component_routing.py @@ -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) diff --git a/vllm_omni/engine/stage_engine_core_proc_manager.py b/vllm_omni/engine/stage_engine_core_proc_manager.py index b14c62d8f..94a3e58b1 100644 --- a/vllm_omni/engine/stage_engine_core_proc_manager.py +++ b/vllm_omni/engine/stage_engine_core_proc_manager.py @@ -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, diff --git a/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_omni_llm.py b/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_omni_llm.py index b4821c603..88eddbf19 100644 --- a/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_omni_llm.py +++ b/vllm_omni/model_executor/models/minicpmo_4_5/minicpmo_4_5_omni_llm.py @@ -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("") + + 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), - ), - "", + ) ) 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), - "", - ) + 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), - "", - ) + return _select_unk_positions(self.get_audio_prompt_texts(audio_len)) return [ *base_updates, diff --git a/vllm_omni/model_executor/models/qwen3_omni/qwen3_omni_moe_thinker.py b/vllm_omni/model_executor/models/qwen3_omni/qwen3_omni_moe_thinker.py index 96b9d6e30..16b9d020d 100644 --- a/vllm_omni/model_executor/models/qwen3_omni/qwen3_omni_moe_thinker.py +++ b/vllm_omni/model_executor/models/qwen3_omni/qwen3_omni_moe_thinker.py @@ -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)