From b4fa09f756e6cd5b861a59d5fcb15c6f22f046b4 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 11:54:07 +0800 Subject: [PATCH 01/25] Refactor output processing for multimodal capabilities in vLLM-omni - Introduced OmniRequestState to manage multimodal request states. - Enhanced MultimodalOutputProcessor to handle various output types including images, text, and latents. - Implemented methods for accumulating multimodal tensors and processing outputs. - Updated output handling to ensure compatibility with vLLM's base processor while allowing custom modality handlers. --- vllm_omni/engine/output_processor.py | 598 ++++++++++++++++++--------- 1 file changed, 397 insertions(+), 201 deletions(-) diff --git a/vllm_omni/engine/output_processor.py b/vllm_omni/engine/output_processor.py index 8f5fcb357..2b573c40b 100644 --- a/vllm_omni/engine/output_processor.py +++ b/vllm_omni/engine/output_processor.py @@ -1,215 +1,411 @@ -""" -Output processing for multimodal outputs in vLLM-omni. -""" +from typing import Dict, Callable, Optional, Any, Union -from typing import List, Dict, Any, Optional, Callable, Union -from vllm.outputs import RequestOutput, CompletionOutput -from vllm.v1.outputs import ModelRunnerOutput as EngineCoreOutput +import torch + +from vllm.v1.engine.output_processor import OutputProcessor as VLLMOutputProcessor +from vllm.v1.engine.output_processor import OutputProcessorOutput, RequestState, RequestOutputCollector +from vllm.transformers_utils.tokenizer_group import TokenizerGroup +from vllm.v1.engine import FinishReason +from vllm.v1.metrics.stats import IterationStats +from vllm.sampling_params import RequestOutputKind +from vllm.transformers_utils.tokenizer import AnyTokenizer +from vllm.v1.engine import EngineCoreRequest +from vllm.v1.engine.detokenizer import IncrementalDetokenizer +from vllm.v1.engine.logprobs import LogprobsProcessor +from vllm.v1.engine.parallel_sampling import ParentRequest +from vllm.logger import init_logger +from vllm.v1.engine import EngineCoreOutput -class MultimodalOutputProcessor: - """Handles multimodal output processing for vLLM-omni.""" - - def __init__(self): - self.output_handlers: Dict[str, Callable] = { - "image": self._process_image_output, - "text+image": self._process_text_image_output, - "latents": self._process_latents_output, - "text": self._process_text_output, - "pooling": self._process_pooling_output, - } - - def process_output(self, engine_core_output: Any) -> List[RequestOutput]: - """Process engine core output and return formatted RequestOutput.""" - if engine_core_output is None: - return [] - - # If it's already a RequestOutput, return as is - if isinstance(engine_core_output, RequestOutput): - return [engine_core_output] - - # If it's a list of RequestOutputs, return as is - if isinstance(engine_core_output, list): - return engine_core_output - - # Otherwise, process based on output type - output_type = self._detect_output_type(engine_core_output) - handler = self.output_handlers.get(output_type, self._process_pooling_output) - - return handler(engine_core_output) +logger = init_logger(__name__) - def _build_request_output( + +class OmniRequestState(RequestState): + + def __init__( self, - source: Any, - completion_outputs: List[CompletionOutput], - ) -> RequestOutput: - """Helper to construct RequestOutput with safe defaults.""" + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.mm_type: Optional[str] = None + self.mm_accumulated: Optional[torch.Tensor] = None - return RequestOutput( - request_id=getattr(source, "request_id", "unknown"), - prompt=getattr(source, "prompt", ""), - prompt_token_ids=getattr(source, "prompt_token_ids", []), - prompt_logprobs=getattr(source, "prompt_logprobs", None), - outputs=completion_outputs, - finished=getattr(source, "finished", True), + @classmethod + def from_new_request( + cls, + tokenizer: AnyTokenizer, + request: EngineCoreRequest, + prompt: Optional[str], + parent_req: Optional[ParentRequest], + request_index: int, + queue: Optional[Any], + log_stats: bool, + ) -> "OmniRequestState": + + if sampling_params := request.sampling_params: + if not sampling_params.detokenize: + tokenizer = None + output_kind = sampling_params.output_kind + logprobs_processor = LogprobsProcessor.from_new_request( + tokenizer=tokenizer, + request=request, + ) + detokenizer = IncrementalDetokenizer.from_new_request( + tokenizer=tokenizer, + request=request, + ) + max_tokens_param = sampling_params.max_tokens + else: + logprobs_processor = None + detokenizer = None + max_tokens_param = None + assert request.pooling_params is not None + output_kind = request.pooling_params.output_kind + + return cls( + request_id=request.request_id, + parent_req=parent_req, + request_index=request_index, + lora_name=(request.lora_request.name + if request.lora_request is not None else None), + output_kind=output_kind, + prompt=prompt, + prompt_token_ids=request.prompt_token_ids, + logprobs_processor=logprobs_processor, + detokenizer=detokenizer, + max_tokens_param=max_tokens_param, + arrival_time=request.arrival_time, + queue=queue, + log_stats=log_stats, ) - def _detect_output_type(self, output: Any) -> str: - """Detect the type of output based on its content.""" - if hasattr(output, 'output_type'): - return output.output_type - - # Check for image-related attributes - if hasattr(output, 'image') or hasattr(output, 'images'): - if hasattr(output, 'text') or hasattr(output, 'texts'): - return "text+image" + def add_multimodal_tensor(self, tensor: Optional[torch.Tensor], + mm_type: Optional[str]) -> None: + if tensor is None: + return + try: + if mm_type: + self.mm_type = (mm_type or "").lower() + t = tensor.detach() + try: + t = t.to("cpu") + except Exception: + pass + if self.mm_accumulated is None: + self.mm_accumulated = t else: - return "image" - - # Check for latent-related attributes - if hasattr(output, 'latents') or hasattr(output, 'latent_representation'): - return "latents" - - # Check for pooling output - if hasattr(output, 'pooler_output') and output.pooler_output is not None: - return "pooling" - - # Default to text - return "text" - - def _process_text_output(self, output: Any) -> List[RequestOutput]: - """Process text output.""" - if isinstance(output, RequestOutput): - return [output] - - # Create a mock RequestOutput for text - completion_output = CompletionOutput( - index=0, - text=getattr(output, 'text', ''), - token_ids=getattr(output, 'token_ids', []), - cumulative_logprob=getattr(output, 'cumulative_logprob', 0.0), - logprobs=getattr(output, 'logprobs', None), - finish_reason=getattr(output, 'finish_reason', 'length') - ) - - return [self._build_request_output(output, [completion_output])] + self.mm_accumulated = torch.cat([self.mm_accumulated, t], dim=0) + except Exception: + pass - def _process_image_output(self, output: Any) -> List[RequestOutput]: - """Process image output.""" - # For image outputs, we need to create a special RequestOutput - # that can handle image data + # Override: do not route to pooling-only path; always create completion + # outputs, and attach pooling_result into the CompletionOutput. + def make_request_output( + self, + new_token_ids: list[int], + pooling_output: Optional[torch.Tensor], + finish_reason: Optional[FinishReason], + stop_reason: Optional[Union[int, str]], + kv_transfer_params: Optional[dict[str, Any]] = None, + num_cached_tokens: Optional[int] = None, + ) -> Optional[Any]: + finished = finish_reason is not None + final_only = self.output_kind == RequestOutputKind.FINAL_ONLY + + if not finished and final_only: + return None + + if num_cached_tokens is not None: + # Keep num_cached_tokens in RequestOutput for compatibility + try: + self.num_cached_tokens = num_cached_tokens # type: ignore[attr-defined] + except Exception: + pass + + request_id = self.request_id + output = self._new_completion_output(new_token_ids, finish_reason, + stop_reason) + + if self.parent_req is None: + outputs = [output] + else: + request_id, outputs, finished = self.parent_req.get_outputs( + request_id, output) + if not outputs: + return None + + return self._new_request_output(request_id, outputs, finished, + kv_transfer_params) + + def _new_completion_output( + self, + token_ids: list[int], + finish_reason: Optional[FinishReason], + stop_reason: Optional[Union[int, str]] + ) -> Any: + # Reuse base text/logprobs logic, then annotate with pooling_result. + base_output = super()._new_completion_output(token_ids, finish_reason, + stop_reason) + try: + if self.mm_accumulated is not None: + tensor = self.mm_accumulated + try: + tensor = tensor.detach().to("cpu") + except Exception: + pass + # Attach on the completion output for downstream consumers. + if not hasattr(base_output, "multimodal_output"): + setattr(base_output, "multimodal_output", {}) + setattr(base_output, "multimodal_output", {self.mm_type: tensor}) + except Exception as e: + logger.warning("Error in _new_completion_output", e) + pass + return base_output + + +class MultimodalOutputProcessor(VLLMOutputProcessor): + """Handles multimodal output processing by normalizing EngineCoreOutput + before delegating to the base vLLM OutputProcessor. + + Strategy: + - Route by EngineCoreOutput.output_type when present + ("image", "text+image", "latents", "text"). + - Fallback to pooling/text heuristics when output_type is absent. + - Mutate EngineCoreOutput in-place to ensure vLLM's base processor can + produce the correct RequestOutput/PoolingRequestOutput. + - Allow custom per-modality handlers via register_handler(). + """ + def __init__(self, tokenizer: TokenizerGroup, log_stats: bool, engine_core_output_type: Optional[str] = None): + super().__init__(tokenizer=tokenizer, log_stats=log_stats) + self.output_handlers: Dict[str, Callable[[EngineCoreOutput], None]] = {} + self._reqid_to_mm_type: Dict[str, str] = {} + self.request_states: dict[str, OmniRequestState] = {} + self.engine_core_output_type = engine_core_output_type - # Extract image data - image_data = getattr(output, 'image', None) - if image_data is None: - image_data = getattr(output, 'images', [None])[0] - - # Create a completion output with image data - completion_output = CompletionOutput( - index=0, - text="", # No text for pure image output - token_ids=[], - cumulative_logprob=0.0, - logprobs=None, - finish_reason="stop" + def register_handler(self, modality: str, + handler: Callable[[EngineCoreOutput], None]) -> None: + self.output_handlers[modality.lower()] = handler + + def add_request( + self, + request: EngineCoreRequest, + prompt: Optional[str], + parent_req: Optional[ParentRequest] = None, + request_index: int = 0, + queue: Optional[RequestOutputCollector] = None, + ) -> None: + request_id = request.request_id + if request_id in self.request_states: + raise ValueError(f"Request id {request_id} already running.") + + tokenizer = None if not self.tokenizer else \ + self.tokenizer.get_lora_tokenizer(request.lora_request) + + req_state = OmniRequestState.from_new_request(tokenizer=tokenizer, + request=request, + prompt=prompt, + parent_req=parent_req, + request_index=request_index, + queue=queue, + log_stats=self.log_stats) + self.request_states[request_id] = req_state + self.lora_states.add_request(req_state) + if parent_req: + self.parent_requests[parent_req.request_id] = parent_req + + def process_outputs( + self, + engine_core_outputs: list[EngineCoreOutput], + engine_core_timestamp: Optional[float] = None, + iteration_stats: Optional[IterationStats] = None, + ) -> OutputProcessorOutput: + self._reqid_to_mm_type.clear() + for eco in engine_core_outputs: + mm_type = (getattr(eco, "output_type", None) or "").lower() + if mm_type: + self._reqid_to_mm_type[eco.request_id] = mm_type + self._route_and_normalize(eco) + + # Build RequestOutputs without delegating to base, so we can keep ids + request_outputs: list[Any] = [] + reqs_to_abort: list[str] = [] + for eco in engine_core_outputs: + req_id = eco.request_id + req_state = self.request_states.get(req_id) + if req_state is None: + continue + + # 1) Stats + self._update_stats_from_output(req_state, eco, + engine_core_timestamp, + iteration_stats) + + new_token_ids = eco.new_token_ids + pooling_output = eco.pooling_output + finish_reason = eco.finish_reason + stop_reason = eco.stop_reason + kv_transfer_params = eco.kv_transfer_params + num_cached_tokens = eco.num_cached_tokens + req_state.is_prefilling = False + + # 2) Detokenize and logprobs when text path + assert req_state.detokenizer is not None + assert req_state.logprobs_processor is not None + stop_string = req_state.detokenizer.update( + new_token_ids, finish_reason == FinishReason.STOP) + if stop_string: + finish_reason = FinishReason.STOP + stop_reason = stop_string + req_state.logprobs_processor.update_from_output(eco) + + # 2.5) Accumulate multimodal tensors in RequestState + try: + mm_type = (getattr(eco, "output_type", self.engine_core_output_type) or "").lower() + if pooling_output is not None and isinstance(req_state, OmniRequestState): + req_state.add_multimodal_tensor(pooling_output, mm_type) + except Exception: + pass + + # 3) Create RequestOutput objects, forcing combined mode to keep ids + pooling_for_make = pooling_output + if pooling_output is not None and new_token_ids: + # Do not consume pooling path now; keep ids and attach mm later + pooling_for_make = None + + ro = req_state.make_request_output(new_token_ids, pooling_for_make, + finish_reason, stop_reason, + kv_transfer_params, + num_cached_tokens) + if ro: + # Attach accumulated multimodal payload if any + try: + if isinstance(req_state, OmniRequestState) and req_state.mm_accumulated is not None: + mm_key = req_state.mm_type or "latents" + if not hasattr(ro, "multimodal_output"): + setattr(ro, "multimodal_output", {}) + ro.multimodal_output[mm_key] = req_state.mm_accumulated + except Exception as e: + logger.warning("Error in process_outputs", e) + pass + if req_state.queue is not None: + req_state.queue.put(ro) + else: + request_outputs.append(ro) + + # 4) Free completed + if finish_reason is not None: + self.request_states.pop(req_id) + parent_req = req_state.parent_req + if parent_req and not parent_req.child_requests: + self.parent_requests.pop(parent_req.request_id, None) + if not eco.finished: + reqs_to_abort.append(req_id) + self._update_stats_from_finished(req_state, finish_reason, + iteration_stats) + # Cleanup per-request mm state + if isinstance(req_state, OmniRequestState): + req_state.mm_accumulated = None + req_state.mm_type = None + + return OutputProcessorOutput( + request_outputs=request_outputs, + reqs_to_abort=reqs_to_abort, ) - - # Add image data to the completion output - completion_output.image = image_data - - return [ - self._build_request_output(output, [completion_output]) - ] - def _process_text_image_output(self, output: Any) -> List[RequestOutput]: - """Process combined text and image output.""" - # Extract text and image data - text_data = getattr(output, 'text', '') - image_data = getattr(output, 'image', None) - - if image_data is None: - image_data = getattr(output, 'images', [None])[0] - - # Create a completion output with both text and image - completion_output = CompletionOutput( - index=0, - text=text_data, - token_ids=getattr(output, 'token_ids', []), - cumulative_logprob=getattr(output, 'cumulative_logprob', 0.0), - logprobs=getattr(output, 'logprobs', None), - finish_reason="stop" - ) - - # Add image data to the completion output - completion_output.image = image_data - - return [ - self._build_request_output(output, [completion_output]) - ] - - def _process_latents_output(self, output: Any) -> List[RequestOutput]: - """Process latent representation output.""" - # Extract latent data - latent_data = getattr(output, 'latents', None) - if latent_data is None: - latent_data = getattr(output, 'latent_representation', None) - - # Create a completion output with latent data - completion_output = CompletionOutput( - index=0, - text="", # No text for latent output - token_ids=[], - cumulative_logprob=0.0, - logprobs=None, - finish_reason="stop" - ) - - # Add latent data to the completion output - completion_output.latents = latent_data - - return [ - self._build_request_output(output, [completion_output]) - ] - - def _process_pooling_output(self, output: Any) -> List[RequestOutput]: - """Process pooling output (hidden states, embeddings, etc.).""" - # Extract pooling data - pooling_data = getattr(output, 'pooler_output', None) - if pooling_data is None: - pooling_data = getattr(output, 'hidden_states', None) - - # Create a completion output with pooling data - completion_output = CompletionOutput( - index=0, - text="", # No text for pooling output - token_ids=[], - cumulative_logprob=0.0, - logprobs=None, - finish_reason="stop" - ) - - # Add pooling data to the completion output - completion_output.pooler_output = pooling_data - - return [ - self._build_request_output(output, [completion_output]) - ] - - def process_outputs(self, engine_core_outputs: List[EngineCoreOutput], **kwargs) -> List[RequestOutput]: - """Process multiple engine core outputs.""" - all_outputs = [] - - for engine_core_output in engine_core_outputs: - outputs = self.process_output(engine_core_output) - all_outputs.extend(outputs) - - return all_outputs - - def add_output_handler(self, output_type: str, handler: Callable) -> None: - """Add a custom output handler for a specific output type.""" - self.output_handlers[output_type] = handler - - def remove_output_handler(self, output_type: str) -> None: - """Remove an output handler for a specific output type.""" + # ---- routing helpers ---- + def _route_and_normalize(self, eco: EngineCoreOutput) -> None: + output_type = (getattr(eco, "output_type", self.engine_core_output_type) or "").lower() + + # Custom handler first (if registered) if output_type in self.output_handlers: - del self.output_handlers[output_type] + try: + self.output_handlers[output_type](eco) + # Fall through to default fixups in case the handler left gaps + except Exception: + pass + + if output_type == "image": + self._process_image_output(eco) + elif output_type in ("text+image", "text,image", "image+text"): + self._process_text_image_output(eco) + elif output_type in ("latents", "latent"): + self._process_latents_output(eco) + elif output_type in ("audio", "speech"): + self._process_audio_output(eco) + elif output_type == "text": + self._process_text_output(eco) + else: + # Fallback heuristic + if eco.pooling_output is not None: + self._process_pooling_output(eco) + else: + self._process_text_output(eco) + + # ---- modality processors ---- + def _process_image_output(self, eco: EngineCoreOutput) -> None: + """Ensure image tensors are surfaced via pooling_output for vLLM.""" + if eco.pooling_output is None: + tensor = self._extract_from_multimodal_outputs( + eco, keys=("image", "images", "pixel_values", "pixels")) + if tensor is not None: + eco.pooling_output = tensor + + def _process_text_image_output(self, eco: EngineCoreOutput) -> None: + """Allow text+image outputs. Text path stays as new_token_ids; + image/latents route via pooling_output.""" + # Preserve text tokens as-is; ensure pooling_output carries image/latents + if eco.pooling_output is None: + tensor = self._extract_from_multimodal_outputs( + eco, keys=("image", "images", "pixel_values", "pixels", + "latent", "latents", "z")) + if tensor is not None: + eco.pooling_output = tensor + + def _process_latents_output(self, eco: EngineCoreOutput) -> None: + """Ensure latent tensors are surfaced via pooling_output.""" + if eco.pooling_output is None: + tensor = self._extract_from_multimodal_outputs( + eco, keys=("latent", "latents", "z", "posterior")) + if tensor is not None: + eco.pooling_output = tensor + + def _process_audio_output(self, eco: EngineCoreOutput) -> None: + """Ensure audio tensors are surfaced via pooling_output.""" + if eco.pooling_output is None: + tensor = self._extract_from_multimodal_outputs( + eco, keys=("audio", "audios", "wav", "waveform", + "audio_pcm", "pcm")) + if tensor is not None: + eco.pooling_output = tensor + + def _process_text_output(self, eco: EngineCoreOutput) -> None: + """No-op; base processor will detokenize new_token_ids → text.""" + return + + def _process_pooling_output(self, eco: EngineCoreOutput) -> None: + """Optional sanity checks for pooling tensor.""" + if eco.pooling_output is None: + return + if not isinstance(eco.pooling_output, torch.Tensor): + # Best-effort: convert to tensor if it's a list/ndarray-like + try: + eco.pooling_output = torch.as_tensor(eco.pooling_output) + except Exception: + pass + + def _extract_from_multimodal_outputs( + self, eco: EngineCoreOutput, keys: tuple[str, ...] + ) -> Optional[torch.Tensor]: + mm = getattr(eco, "multimodal_outputs", None) + if not isinstance(mm, dict): + return None + for k in keys: + v = mm.get(k) + if isinstance(v, torch.Tensor): + return v + # Try the first tensor in the dict as a fallback + for v in mm.values(): + if isinstance(v, torch.Tensor): + return v + return None \ No newline at end of file From 20cfd15cc61009eb1bab926b6a671635d582be6d Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Wed, 22 Oct 2025 14:37:55 +0800 Subject: [PATCH 02/25] Add entrypoint class and stage management --- vllm_omni/config/__init__.py | 28 + vllm_omni/engine/arg_utils.py | 51 ++ vllm_omni/entrypoints/omni_llm.py | 679 ++++-------------- vllm_omni/entrypoints/stage_manager.py | 145 ++-- vllm_omni/entrypoints/utils.py | 25 + vllm_omni/inputs/__init__.py | 0 vllm_omni/inputs/data.py | 12 + .../model_executor/stage_configs/__init__.py | 0 .../stage_configs/qwen2_5_omni.yaml | 34 + vllm_omni/outputs.py | 9 + 10 files changed, 354 insertions(+), 629 deletions(-) create mode 100644 vllm_omni/engine/arg_utils.py create mode 100644 vllm_omni/entrypoints/utils.py create mode 100644 vllm_omni/inputs/__init__.py create mode 100644 vllm_omni/inputs/data.py create mode 100644 vllm_omni/model_executor/stage_configs/__init__.py create mode 100644 vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml create mode 100644 vllm_omni/outputs.py diff --git a/vllm_omni/config/__init__.py b/vllm_omni/config/__init__.py index e48a874a7..2f53543ee 100644 --- a/vllm_omni/config/__init__.py +++ b/vllm_omni/config/__init__.py @@ -1,6 +1,13 @@ """ Configuration module for vLLM-omni. """ +from vllm.config import ModelConfig +from typing import Optional +from pydantic.dataclasses import dataclass +from pydantic import ConfigDict +from vllm.config import config + +import vllm_omni.model_executor.models as me_models from .stage_config import ( OmniStageConfig, @@ -11,7 +18,28 @@ from .stage_config import ( create_dit_stage_config, ) + +@config +@dataclass(config=ConfigDict(arbitrary_types_allowed=True)) +class OmniModelConfig(ModelConfig): + """Configuration for Omni models, extending the base ModelConfig.""" + + stage_id: int = 0 + model_stage: str = "thinker" + model_arch: str = "Qwen2_5OmniForConditionalGeneration" + engine_output_type: Optional[str] = None + + @property + def registry(self): + return me_models.OmniModelRegistry + + @property + def architectures(self) -> list[str]: + return [self.model_arch] + + __all__ = [ + "OmniModelConfig", "OmniStageConfig", "DiTConfig", "DiTCacheConfig", diff --git a/vllm_omni/engine/arg_utils.py b/vllm_omni/engine/arg_utils.py new file mode 100644 index 000000000..2f744480c --- /dev/null +++ b/vllm_omni/engine/arg_utils.py @@ -0,0 +1,51 @@ +from typing import Optional +from dataclasses import dataclass +from vllm.engine.arg_utils import EngineArgs +from vllm.utils import FlexibleArgumentParser + +from vllm_omni.config import OmniModelConfig + + +@dataclass +class OmniEngineArgs(EngineArgs): + stage_id: int = 0 + model_stage: str = "thinker" + model_arch: str = "Qwen2_5OmniForConditionalGeneration" + engine_output_type: Optional[str] = None + + @staticmethod + def add_cli_args(parser: FlexibleArgumentParser) -> FlexibleArgumentParser: + """Shared CLI arguments for vLLM engine.""" + parser.add_argument( + "--engine-output-type", + type=str, + default=EngineArgs.engine_output_type, + help=( + "Declare EngineCoreOutput.output_type (e.g., 'text', 'image', " + "'text+image', 'latent'). This will be written into " + "model_config.engine_output_type for schedulers to use." + ), + ) + parser.add_argument("--model-stage", type=str, default=EngineArgs.model_stage, + help="Declare model stage (e.g., 'thinker', 'talker', 'token2wav'). This will be written into model_config.model_stage for schedulers to use.") + return parser + + def create_model_config(self) -> OmniModelConfig: + # First, get the base ModelConfig from the parent class + base_config = super().create_model_config() + + # Create OmniModelConfig by copying all base config attributes + # and adding the new omni-specific fields + config_dict = base_config.__dict__.copy() + + # Add the new omni-specific fields + config_dict['stage_id'] = self.stage_id + config_dict['model_stage'] = self.model_stage + config_dict['model_arch'] = self.model_arch + config_dict['engine_output_type'] = self.engine_output_type + + # Create and return the OmniModelConfig instance + omni_config = OmniModelConfig(**config_dict) + omni_config.hf_config.architectures = omni_config.architectures + + return omni_config \ No newline at end of file diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index 8223c6f20..aee4fcf15 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -1,576 +1,149 @@ -""" -Core OmniLLM and AsyncOmniLLM classes for multi-stage processing. -""" +from typing import Union, Sequence, Optional, Any +import cloudpickle +from pydantic import ValidationError -from typing import List, Dict, Any, Optional, Union, Callable +from vllm.inputs import PromptType +from vllm.sampling_params import SamplingParams from vllm.entrypoints.llm import LLM -from vllm.v1.engine.async_llm import AsyncLLM + from vllm.v1.engine.llm_engine import LLMEngine -from vllm.outputs import RequestOutput, LoRARequest +from vllm.engine.arg_utils import HfOverrides +from vllm.usage.usage_lib import UsageContext +from vllm.config import CompilationConfig, is_init_field +from vllm.utils import Counter +from vllm.logger import init_logger +import vllm.envs as envs -from ..config import OmniStageConfig -from .stage_manager import StageManager -from ..engine.output_processor import MultimodalOutputProcessor -from ..engine.diffusion_engine import DiffusersPipelineEngine +from vllm_omni.entrypoints.utils import load_stage_configs_from_model +from vllm_omni.entrypoints.stage_manager import Stage +from vllm_omni.engine.arg_utils import OmniEngineArgs +from vllm_omni.outputs import OmniRequestOutput +logger = init_logger(__name__) -class OmniLLM(LLM): - """Extended LLM supporting multiple engines and stage-based processing.""" - - def __init__( - self, - stage_configs: List[OmniStageConfig], - log_stats: bool = False, - **kwargs - ): - # Use the first stage's model as the default model for LLM - default_model = stage_configs[0].model_path if stage_configs else "test-model" - # Track whether we should launch engines in multiprocess mode. - self.multiprocess_mode = kwargs.pop("multiprocess_mode", False) - - # Fix configuration validation issues - # Ensure max_num_batched_tokens is at least as large as max_model_len - if 'max_model_len' in kwargs and 'max_num_batched_tokens' in kwargs: - if kwargs['max_num_batched_tokens'] < kwargs['max_model_len']: - kwargs['max_num_batched_tokens'] = kwargs['max_model_len'] - elif 'max_model_len' in kwargs: - # If max_model_len is set but max_num_batched_tokens is not, set it to max_model_len - kwargs['max_num_batched_tokens'] = kwargs['max_model_len'] +class OmniLLM: + def __init__(self, model: str, stage_configs = None, log_stats: bool = False, **kwargs): + if stage_configs is None: + self.initialize_stage_configs(model) else: - # Set reasonable defaults to avoid validation errors - kwargs['max_model_len'] = 2048 - kwargs['max_num_batched_tokens'] = 2048 + self.stage_configs = stage_configs - super().__init__(model=default_model, **kwargs) - self.stage_configs = stage_configs - self.log_stats = log_stats - self.stage_manager = StageManager(stage_configs, log_stats) - self.output_processor = MultimodalOutputProcessor() - self._initialize_stage_engines() + self.stage_list = [] + self.initialize_stages(model) + + def initialize_stage_configs(self, model: str): + self.stage_configs = load_stage_configs_from_model(model) - def _initialize_stage_engines(self) -> None: - """Initialize LLMEngine instances for each stage.""" - self.stage_manager.initialize_engines() + def initialize_stages(self, model: str): + for stage_config in self.stage_configs: + stage = Stage(stage_config) + omni_llm = OmniLLM(model=model, **stage_config.engine_args) + stage.set_engine(omni_llm) + self.stage_list.append(stage) def generate( self, - stage_args_list: Optional[List[Dict[str, Any]]] = None, - use_tqdm: Union[bool, Callable[..., Any]] = True, - lora_request: Optional[Union[List[LoRARequest], LoRARequest]] = None, - priority: Optional[List[int]] = None, - *, - prompt: Optional[str] = None, - stage_overrides: Optional[Dict[int, Dict[str, Any]]] = None, - ) -> List[RequestOutput]: - """Main generation interface - orchestrates multi-stage processing.""" - if stage_args_list is None: - if prompt is None: + prompts: Union[PromptType, Sequence[PromptType]], + sampling_params_list: Optional[Union[SamplingParams, + Sequence[SamplingParams]]] = None, + ) -> list[OmniRequestOutput]: + """Generate text outputs for the given prompts.""" + final_outputs: list[OmniRequestOutput] = [] + for stage_id, stage in enumerate(self.stage_list): + if stage_id > 0: + engine_inputs = stage.process_engine_inputs(self.stage_list, prompts) + else: + engine_inputs = prompts + engine_outputs = self._run_generation(stage, sampling_params_list[stage_id], engine_inputs) + stage.set_engine_outputs(engine_outputs) + if hasattr(stage, 'final_output') and stage.final_output: + final_outputs.append(OmniRequestOutput( + stage_id=stage_id, + final_output_type=stage.final_output_type, + request_output=engine_outputs)) + return final_outputs + + def _run_generation(self, stage: Stage, sampling_params: SamplingParams, prompts: Union[PromptType, Sequence[PromptType]]): + engine_outputs = [] + for ro in stage.engine.generate(prompts, sampling_params): + engine_outputs.append(ro) + return engine_outputs + + +class StageLLM(LLM): + def __init__(self, + model: str, + compilation_config: Optional[Union[int, dict[str, Any], + CompilationConfig]] = None, + hf_overrides: Optional[HfOverrides] = None, + **kwargs): + """LLM constructor.""" + if "disable_log_stats" not in kwargs: + kwargs["disable_log_stats"] = True + + if "worker_cls" in kwargs: + worker_cls = kwargs["worker_cls"] + # if the worker_cls is not qualified string name, + # we serialize it using cloudpickle to avoid pickling issues + if isinstance(worker_cls, type): + kwargs["worker_cls"] = cloudpickle.dumps(worker_cls) + + if "kv_transfer_config" in kwargs and isinstance( + kwargs["kv_transfer_config"], dict): + from vllm.config import KVTransferConfig + raw_config_dict = kwargs["kv_transfer_config"] + try: + kwargs["kv_transfer_config"] = KVTransferConfig( + **raw_config_dict) + except ValidationError as e: + logger.error( + "Failed to convert 'kv_transfer_config' dict to " + "KVTransferConfig object. Dict: %s. Error: %s", + raw_config_dict, e) + # Consider re-raising a more specific vLLM error or ValueError + # to provide better context to the user. raise ValueError( - "prompt must be provided when stage_args_list is not supplied" - ) - stage_args_list = self._build_stage_args_from_config( - prompt, stage_overrides or {} - ) + f"Invalid 'kv_transfer_config' provided: {e}") from e - if len(stage_args_list) != len(self.stage_configs): - raise ValueError( - f"Number of stage arguments ({len(stage_args_list)}) must match " - f"number of stage configs ({len(self.stage_configs)})" - ) - - # Process through each stage sequentially - current_output = None - - for i, (stage_config, stage_args) in enumerate(zip(self.stage_configs, stage_args_list)): - stage_engine = self.stage_manager.get_engine(i) - - # Prepare input for this stage - processed_input = self._process_stage_inputs( - stage_config, stage_args or {}, current_output - ) - - # Execute stage - stage_output = self._execute_stage( - stage_engine, processed_input, lora_request, priority, stage_config - ) - - # Update for next stage - current_output = stage_output - stage_config.stage_output = stage_output - - # Process final output - final_output = self.output_processor.process_output(current_output) - return final_output - - def _build_stage_args_from_config( - self, - prompt: str, - stage_overrides: Dict[int, Dict[str, Any]], - ) -> List[Dict[str, Any]]: - """Derive per-stage argument dictionaries from configuration defaults.""" - stage_args: List[Dict[str, Any]] = [] - for stage_config in self.stage_configs: - combined: Dict[str, Any] = dict(stage_config.default_stage_args or {}) - override = stage_overrides.get(stage_config.stage_id) - if override: - combined.update(override) - if stage_config.engine_type == "AR": - combined["prompt"] = prompt - stage_args.append(combined) - return stage_args + if hf_overrides is None: + hf_overrides = {} - def _process_stage_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Prepare input for specific stage.""" - if stage_config.engine_type == "AR": - return self._process_ar_inputs(stage_config, stage_args, previous_output) - elif stage_config.engine_type == "DiT": - return self._process_dit_inputs(stage_config, stage_args, previous_output) + if compilation_config is not None: + if isinstance(compilation_config, int): + compilation_config_instance = CompilationConfig( + level=compilation_config) + elif isinstance(compilation_config, dict): + predicate = lambda x: is_init_field(CompilationConfig, x[0]) + compilation_config_instance = CompilationConfig( + **dict(filter(predicate, compilation_config.items()))) + else: + compilation_config_instance = compilation_config else: - raise NotImplementedError(f"Unknown engine type: {stage_config.engine_type}") + compilation_config_instance = CompilationConfig() - def _process_ar_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Process inputs for AR stage.""" - combined = dict(stage_config.default_stage_args or {}) - combined.update(stage_args) - combined.setdefault("prompt", "") - combined.setdefault("max_tokens", 100) - combined.setdefault("temperature", 0.7) - - # If we have previous output (e.g., from a previous AR stage), - # we might want to use it as context - if previous_output is not None: - # Extract text from previous output if available - if hasattr(previous_output, 'outputs') and previous_output.outputs: - last_output = previous_output.outputs[-1] - if hasattr(last_output, 'text'): - combined["prompt"] = last_output.text + " " + combined["prompt"] - - return combined - - def _process_dit_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Process inputs for DiT stage.""" - combined = dict(stage_config.default_stage_args or {}) - combined.update(stage_args) - - dit = stage_config.dit_config - if dit is not None: - combined.setdefault("height", getattr(dit, "height", 512)) - combined.setdefault("width", getattr(dit, "width", 512)) - combined.setdefault( - "num_inference_steps", getattr(dit, "num_inference_steps", 50) - ) - combined.setdefault( - "guidance_scale", getattr(dit, "guidance_scale", 7.5) - ) - else: - combined.setdefault("height", 512) - combined.setdefault("width", 512) - combined.setdefault("num_inference_steps", 50) - combined.setdefault("guidance_scale", 7.5) - - # Handle image inputs if present - if "image" in stage_args: - # For now, we'll pass the image path directly - # In a full implementation, this would involve VAE encoding - combined["image"] = stage_args["image"] - - # If we have previous output from an AR stage, we might want to use it - if previous_output is not None: - # Extract text from previous AR output - if hasattr(previous_output, 'outputs') and previous_output.outputs: - last_output = previous_output.outputs[-1] - if hasattr(last_output, 'text'): - combined["prompt"] = last_output.text - - combined.setdefault("prompt", stage_args.get("prompt", "")) - - return combined - - def _execute_stage( - self, - stage_engine: Optional[Union[LLMEngine, DiffusersPipelineEngine]], - processed_input: Dict[str, Any], - lora_request: Optional[Union[List[LoRARequest], LoRARequest]] = None, - priority: Optional[List[int]] = None, - stage_config: Optional[OmniStageConfig] = None, - ) -> Any: - """Execute a single stage.""" - # DiT via diffusers backend - if stage_config and stage_config.engine_type == "DiT": - dit = stage_config.dit_config - if dit and getattr(dit, "use_diffusers", False): - # Lazy-init executor per stage - if not hasattr(self, "_dit_engines"): - self._dit_engines = {} - exec_inst = self._dit_engines.get(stage_config.stage_id) - if exec_inst is None: - exec_inst = DiffusersPipelineEngine( - dit_config=dit, - model_path=stage_config.model_path, - log_stats=self.log_stats, - multiprocess_mode=self.multiprocess_mode, - ) - - self._dit_engines[stage_config.stage_id] = exec_inst - - return exec_inst.generate( - prompt=processed_input.get("prompt", ""), - height=processed_input.get("height", getattr(dit, "height", 512)), - width=processed_input.get("width", getattr(dit, "width", 512)), - num_inference_steps=processed_input.get( - "num_inference_steps", getattr(dit, "num_inference_steps", 30) - ), - guidance_scale=processed_input.get( - "guidance_scale", getattr(dit, "guidance_scale", 5.0) - ), - negative_prompt=processed_input.get("negative_prompt"), - seed=processed_input.get("seed"), - image=processed_input.get("image"), - ) - - # Use the parent LLM's generate method for AR text generation - prompt = processed_input.get("prompt", "") - max_tokens = processed_input.get("max_tokens", 100) - temperature = processed_input.get("temperature", 0.7) - - # Generate using the base LLM class - from vllm.sampling_params import SamplingParams - - sampling_params = SamplingParams( - max_tokens=max_tokens, - temperature=temperature, - top_p=processed_input.get("top_p", 1.0), - frequency_penalty=processed_input.get("frequency_penalty", 0.0), - presence_penalty=processed_input.get("presence_penalty", 0.0), - stop=processed_input.get("stop", None) + engine_args = OmniEngineArgs( + model=model, + hf_overrides=hf_overrides, + compilation_config=compilation_config_instance, + **kwargs, ) - - # Use the parent class's generate method - outputs = super().generate([prompt], sampling_params) - - # Return the first output (we're processing one prompt at a time) - if outputs: - return outputs[0] + + # Create the Engine (autoselects V0 vs V1) + self.llm_engine = LLMEngine.from_engine_args( + engine_args=engine_args, usage_context=UsageContext.LLM_CLASS) + self.engine_class = type(self.llm_engine) + + self.request_counter = Counter() + self.default_sampling_params: Union[dict[str, Any], None] = None + + if envs.VLLM_USE_V1: + supported_tasks = self.llm_engine \ + .get_supported_tasks() # type: ignore else: - # Fallback to mock output if generation fails - from vllm.outputs import RequestOutput, CompletionOutput - - mock_output = CompletionOutput( - index=0, - text="Generation failed", - token_ids=[], - cumulative_logprob=0.0, - logprobs=None, - finish_reason="error" - ) - - return RequestOutput( - request_id="fallback_request", - prompt=prompt, - prompt_token_ids=[], - prompt_logprobs=None, - outputs=[mock_output], - finished=True - ) + supported_tasks = self.llm_engine.model_config.supported_tasks + logger.info("Supported_tasks: %s", supported_tasks) -class AsyncOmniLLM(LLM): - """Extended LLM class supporting multiple engines and stage-based processing.""" - - def __init__( - self, - stage_configs: List[OmniStageConfig], - log_stats: bool = False, - **kwargs - ): - # Use the first stage's model for the base LLM - if stage_configs and stage_configs[0].model_path: - model = stage_configs[0].model_path - else: - model = "Qwen/Qwen3-0.6B" - - # Fix configuration validation issues - # Ensure max_num_batched_tokens is at least as large as max_model_len - if 'max_model_len' in kwargs and 'max_num_batched_tokens' in kwargs: - if kwargs['max_num_batched_tokens'] < kwargs['max_model_len']: - kwargs['max_num_batched_tokens'] = kwargs['max_model_len'] - elif 'max_model_len' in kwargs: - # If max_model_len is set but max_num_batched_tokens is not, set it to max_model_len - kwargs['max_num_batched_tokens'] = kwargs['max_model_len'] - else: - # Set reasonable defaults to avoid validation errors - kwargs['max_model_len'] = 2048 - kwargs['max_num_batched_tokens'] = 2048 - - super().__init__(model=model, **kwargs) - self.stage_configs = stage_configs - self.log_stats = log_stats - self.stage_manager = StageManager(stage_configs, log_stats) - self.output_processor = MultimodalOutputProcessor() - - def _initialize_async_stage_engines(self) -> None: - """Initialize AsyncLLM instances for each stage.""" - self.stage_manager.initialize_async_engines() - - async def generate_async( - self, - stage_args_list: Optional[List[Dict[str, Any]]] = None, - use_tqdm: Union[bool, Callable[..., Any]] = True, - lora_request: Optional[Union[List[LoRARequest], LoRARequest]] = None, - priority: Optional[List[int]] = None, - *, - prompt: Optional[str] = None, - stage_overrides: Optional[Dict[int, Dict[str, Any]]] = None, - ) -> List[RequestOutput]: - """Async generation interface - orchestrates multi-stage processing.""" - - if stage_args_list is None: - if prompt is None: - raise ValueError( - "prompt must be provided when stage_args_list is not supplied" - ) - stage_args_list = self._build_stage_args_from_config( - prompt, stage_overrides or {} - ) - - if len(stage_args_list) != len(self.stage_configs): - raise ValueError( - f"Number of stage arguments ({len(stage_args_list)}) must match " - f"number of stage configs ({len(self.stage_configs)})" - ) - - # Process through each stage sequentially - current_output = None - - for i, (stage_config, stage_args) in enumerate(zip(self.stage_configs, stage_args_list)): - stage_engine = self.stage_manager.get_async_engine(i) - - # Prepare input for this stage - processed_input = self._process_stage_inputs( - stage_config, stage_args or {}, current_output - ) - - # Execute stage asynchronously - stage_output = await self._execute_stage_async( - stage_engine, processed_input, lora_request, priority, stage_config - ) - - # Update for next stage - current_output = stage_output - stage_config.stage_output = stage_output - - # Process final output - final_output = self.output_processor.process_output(current_output) - return final_output - - def _build_stage_args_from_config( - self, - prompt: str, - stage_overrides: Dict[int, Dict[str, Any]], - ) -> List[Dict[str, Any]]: - stage_args: List[Dict[str, Any]] = [] - for stage_config in self.stage_configs: - combined: Dict[str, Any] = dict(stage_config.default_stage_args or {}) - override = stage_overrides.get(stage_config.stage_id) - if override: - combined.update(override) - if stage_config.engine_type == "AR": - combined["prompt"] = prompt - stage_args.append(combined) - return stage_args - - def _process_stage_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Prepare input for specific stage (same as OmniLLM).""" - if stage_config.engine_type == "AR": - return self._process_ar_inputs(stage_config, stage_args, previous_output) - elif stage_config.engine_type == "DiT": - return self._process_dit_inputs(stage_config, stage_args, previous_output) - else: - raise NotImplementedError(f"Unknown engine type: {stage_config.engine_type}") - - def _process_ar_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Process inputs for AR stage (same as OmniLLM).""" - combined = dict(stage_config.default_stage_args or {}) - combined.update(stage_args) - combined.setdefault("prompt", "") - combined.setdefault("max_tokens", 100) - combined.setdefault("temperature", 0.7) - - if previous_output is not None: - if hasattr(previous_output, 'outputs') and previous_output.outputs: - last_output = previous_output.outputs[-1] - if hasattr(last_output, 'text'): - combined["prompt"] = last_output.text + " " + combined["prompt"] - - return combined - - def _process_dit_inputs( - self, - stage_config: OmniStageConfig, - stage_args: Dict[str, Any], - previous_output: Optional[Any] - ) -> Dict[str, Any]: - """Process inputs for DiT stage (same as OmniLLM).""" - combined = dict(stage_config.default_stage_args or {}) - combined.update(stage_args) - - dit = stage_config.dit_config - if dit is not None: - combined.setdefault("height", getattr(dit, "height", 512)) - combined.setdefault("width", getattr(dit, "width", 512)) - combined.setdefault( - "num_inference_steps", getattr(dit, "num_inference_steps", 50) - ) - combined.setdefault( - "guidance_scale", getattr(dit, "guidance_scale", 7.5) - ) - else: - combined.setdefault("height", 512) - combined.setdefault("width", 512) - combined.setdefault("num_inference_steps", 50) - combined.setdefault("guidance_scale", 7.5) - - if "image" in stage_args: - combined["image"] = stage_args["image"] - - if previous_output is not None: - if hasattr(previous_output, 'outputs') and previous_output.outputs: - last_output = previous_output.outputs[-1] - if hasattr(last_output, 'text'): - combined["prompt"] = last_output.text - - combined.setdefault("prompt", stage_args.get("prompt", "")) - - return combined - - async def _execute_stage_async( - self, - stage_engine: AsyncLLM, - processed_input: Dict[str, Any], - lora_request: Optional[Union[List[LoRARequest], LoRARequest]] = None, - priority: Optional[List[int]] = None, - stage_config: Optional[OmniStageConfig] = None, - ) -> Any: - """Execute a single stage asynchronously.""" - # DiT via diffusers backend (sync call inside async for MVP) - if stage_config and stage_config.engine_type == "DiT": - dit = stage_config.dit_config - if dit and getattr(dit, "use_diffusers", False): - if not hasattr(self, "_dit_engines"): - self._dit_engines = {} - exec_inst = self._dit_engines.get(stage_config.stage_id) - if exec_inst is None: - from vllm_omni.engine.diffusion_engine import ( - DiffusersPipelineEngine, - ) - - pipeline_name = getattr(dit, "diffusers_pipeline", None) - device_cfg = getattr(dit, "device_config", None) - model_cfg = getattr(dit, "model_config", None) - if isinstance(device_cfg, dict): - device = device_cfg.get("device") - dtype = device_cfg.get("dtype") - else: - device = getattr(device_cfg, "device", None) - dtype = getattr(device_cfg, "dtype", None) - - if dtype is None: - if isinstance(model_cfg, dict): - dtype = model_cfg.get("dtype") - else: - dtype = getattr(model_cfg, "dtype", None) - - exec_inst = DiffusersPipelineEngine( - model_path=stage_config.model_path, - pipeline_name=pipeline_name, - device=device, - dtype=dtype, - ) - self._dit_engines[stage_config.stage_id] = exec_inst - - return exec_inst.generate( - prompt=processed_input.get("prompt", ""), - height=processed_input.get("height", getattr(dit, "height", 512)), - width=processed_input.get("width", getattr(dit, "width", 512)), - num_inference_steps=processed_input.get( - "num_inference_steps", getattr(dit, "num_inference_steps", 30) - ), - guidance_scale=processed_input.get( - "guidance_scale", getattr(dit, "guidance_scale", 5.0) - ), - negative_prompt=processed_input.get("negative_prompt"), - seed=processed_input.get("seed"), - image=processed_input.get("image"), - ) - - # Use the parent LLM's generate method for AR text generation - prompt = processed_input.get("prompt", "") - max_tokens = processed_input.get("max_tokens", 100) - temperature = processed_input.get("temperature", 0.7) - - # Generate using the base LLM class - from vllm.sampling_params import SamplingParams - - sampling_params = SamplingParams( - max_tokens=max_tokens, - temperature=temperature, - top_p=processed_input.get("top_p", 1.0), - frequency_penalty=processed_input.get("frequency_penalty", 0.0), - presence_penalty=processed_input.get("presence_penalty", 0.0), - stop=processed_input.get("stop", None) - ) - - # Use the parent class's generate method - outputs = super().generate([prompt], sampling_params) - - # Return the first output (we're processing one prompt at a time) - if outputs: - return outputs[0] - else: - # Fallback to mock output if generation fails - from vllm.outputs import RequestOutput, CompletionOutput - - mock_output = CompletionOutput( - index=0, - text="Generation failed", - token_ids=[], - cumulative_logprob=0.0, - logprobs=None, - finish_reason="error" - ) - - return RequestOutput( - request_id="fallback_request", - prompt=prompt, - prompt_token_ids=[], - prompt_logprobs=None, - outputs=[mock_output], - finished=True - ) + self.supported_tasks = supported_tasks \ No newline at end of file diff --git a/vllm_omni/entrypoints/stage_manager.py b/vllm_omni/entrypoints/stage_manager.py index 22a03d51c..a130abc6f 100644 --- a/vllm_omni/entrypoints/stage_manager.py +++ b/vllm_omni/entrypoints/stage_manager.py @@ -2,86 +2,79 @@ Stage manager for orchestrating multiple engines in vLLM-omni. """ -from typing import List, Optional, Union +import importlib +from typing import List, Union from vllm.v1.engine.llm_engine import LLMEngine from vllm.v1.engine.async_llm import AsyncLLM -from ..engine.diffusion_engine import DiffusionEngine +from vllm.inputs import TextPrompt +from vllm.v1.engine import EngineCoreOutput -from ..config import OmniStageConfig +from vllm_omni.inputs.data import OmniTokensPrompt -class StageManager: - """Manages multiple stage engines for multi-stage processing.""" +class Stage: + def __init__(self, stage_config): + self.stage_config = stage_config + self.engine = None + self.async_engine = None + self.stage_id = stage_config.stage_id + self.engine_args = stage_config.engine_args + self.model_stage = stage_config.engine_args.model_stage + if hasattr(stage_config, 'engine_input_source'): + self.engine_input_source = stage_config.engine_input_source + else: + self.engine_input_source = [] + self.engine_output_type = stage_config.engine_args.engine_output_type + self.engine_outputs = None + if hasattr(stage_config, 'custom_process_input_func'): + # Import the module specified in the config (already a full module path) + module_path, func_name = stage_config.custom_process_input_func.rsplit('.', 1) + module = importlib.import_module(module_path) + self.custom_process_input_func = getattr(module, func_name) + else: + self.custom_process_input_func = None + + if hasattr(stage_config, 'final_output'): + self.final_output = stage_config.final_output + else: + self.final_output = False + + if hasattr(stage_config, 'final_output_type'): + self.final_output_type = stage_config.final_output_type + else: + self.final_output_type = None + + def set_engine(self, engine: LLMEngine) -> None: + """Initialize the engine for the stage.""" + self.engine = engine + + def set_async_engine(self, async_engine: AsyncLLM) -> None: + """Initialize the async engine for the stage.""" + self.async_engine = async_engine - def __init__(self, stage_configs: List[OmniStageConfig], log_stats: bool = False): - self.stage_configs = stage_configs - self.log_stats = log_stats - self.engine_list: List[Union[LLMEngine, DiffusionEngine]] = [] - self.async_engine_list: List[AsyncLLM] = [] - self._initialized = False - self._async_initialized = False + def set_engine_outputs(self, engine_outputs: EngineCoreOutput) -> None: + """Set the engine output for the stage.""" + self.engine_outputs = engine_outputs - def initialize_engines(self) -> None: - """Initialize LLMEngine instances for each stage.""" - if self._initialized: - return - - # For now, create placeholder engines - # In a full implementation, this would create actual engines - for stage_config in self.stage_configs: - # Placeholder - would create actual engine here - self.engine_list.append(None) - - self._initialized = True - - def initialize_async_engines(self) -> None: - """Initialize AsyncLLM instances for each stage.""" - if self._async_initialized: - return - - # For now, create placeholder engines - # In a full implementation, this would create actual engines - for stage_config in self.stage_configs: - # Placeholder - would create actual engine here - self.async_engine_list.append(None) - - self._async_initialized = True - - def get_engine(self, stage_id: int) -> LLMEngine: - """Get the engine for a specific stage.""" - if not self._initialized: - self.initialize_engines() - - if stage_id >= len(self.engine_list): - raise IndexError(f"Stage {stage_id} not found. Available stages: 0-{len(self.engine_list)-1}") - - return self.engine_list[stage_id] - - def get_async_engine(self, stage_id: int) -> AsyncLLM: - """Get the async engine for a specific stage.""" - if not self._async_initialized: - self.initialize_async_engines() - - if stage_id >= len(self.async_engine_list): - raise IndexError(f"Async stage {stage_id} not found. Available stages: 0-{len(self.async_engine_list)-1}") - - return self.async_engine_list[stage_id] - - def get_stage_config(self, stage_id: int) -> OmniStageConfig: - """Get the configuration for a specific stage.""" - if stage_id >= len(self.stage_configs): - raise IndexError(f"Stage config {stage_id} not found. Available stages: 0-{len(self.stage_configs)-1}") - - return self.stage_configs[stage_id] - - def get_num_stages(self) -> int: - """Get the number of stages.""" - return len(self.stage_configs) - - def cleanup(self) -> None: - """Clean up resources.""" - # Clean up engines if needed - self.engine_list.clear() - self.async_engine_list.clear() - self._initialized = False - self._async_initialized = False + def process_engine_inputs(self, stage_list, prompt: Union[OmniTokensPrompt, TextPrompt] = None) -> List[Union[OmniTokensPrompt, TextPrompt]]: + """Process the engine input for the stage.""" + if self.custom_process_input_func is None: + engine_inputs = [] + if len(self.engine_input_source) == 0: + raise ValueError("engine_input_source is empty") + source_stage_id = self.engine_input_source[0] + source_outputs = stage_list[source_stage_id].engine_outputs + multi_modal_data = {source_output.request_id: + prompt.get('multi_modal_data', None) for source_output, prompt in zip(source_outputs, prompt)} + + for source_output in source_outputs: + engine_input = OmniTokensPrompt( + prompt_token_ids = source_output.outputs[0].token_ids, + multi_modal_data=multi_modal_data[source_output.request_id] if multi_modal_data else None, + ) + engine_inputs.append(engine_input) + return engine_inputs + + else: + engine_input_source = self.engine_input_source + return self.custom_process_input_func(stage_list, engine_input_source, prompt) \ No newline at end of file diff --git a/vllm_omni/entrypoints/utils.py b/vllm_omni/entrypoints/utils.py new file mode 100644 index 000000000..42a98f164 --- /dev/null +++ b/vllm_omni/entrypoints/utils.py @@ -0,0 +1,25 @@ +import os +from pathlib import Path +from omegaconf import OmegaConf +from vllm.transformers_utils.config import get_config + +# Get the project root directory (2 levels up from this file) +PROJECT_ROOT = Path(__file__).parent.parent.parent + + +def load_stage_configs_from_model(model: str): + """Load stage configs from model.""" + hf_config = get_config(model, trust_remote_code=True) + model_type = hf_config.model_type + stage_config_file = f"vllm_omni/model_executor/stage_configs/{model_type}.yaml" + stage_config_path = PROJECT_ROOT / stage_config_file + if not os.path.exists(stage_config_path): + raise FileNotFoundError(f"Stage config file {stage_config_path} not found") + stage_configs = load_stage_configs_from_yaml(config_path=str(stage_config_path)) + return stage_configs + + +def load_stage_configs_from_yaml(config_path: str): + """Load stage configs from yaml file.""" + config_data = OmegaConf.load(config_path) + return config_data.stage_args \ No newline at end of file diff --git a/vllm_omni/inputs/__init__.py b/vllm_omni/inputs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vllm_omni/inputs/data.py b/vllm_omni/inputs/data.py new file mode 100644 index 000000000..b3a4339e4 --- /dev/null +++ b/vllm_omni/inputs/data.py @@ -0,0 +1,12 @@ +from vllm.inputs.data import TokensPrompt +from typing import Any, NotRequired +import torch + + +class OmniTokensPrompt(TokensPrompt): + prompt_embeds: NotRequired[torch.Tensor] + """The embeddings of the prompt.""" + + # New: optional additional information dictionary + # Values may be torch.Tensor or list + additional_information: NotRequired[dict[str, Any]] \ No newline at end of file diff --git a/vllm_omni/model_executor/stage_configs/__init__.py b/vllm_omni/model_executor/stage_configs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml new file mode 100644 index 000000000..05e45d3b2 --- /dev/null +++ b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml @@ -0,0 +1,34 @@ +# stage config for running qwen2.5-omni with architecture of OmniLLM. +stage_args: + - stage_id: 0 + engine_args: + model_stage: thinker + model_arch: Qwen2_5OmniForConditionalGeneration + gpu_memory_utilization: 0.32 + enforce_eager: true # need to discuss + trust_remote_code: true + engine_output_type: latent # change the param name,such as pooling_output + final_output: true + final_output_type: text + - stage_id: 1 + engine_args: + model_stage: talker + model_arch: Qwen2_5OmniForConditionalGeneration + gpu_memory_utilization: 0.32 + enforce_eager: true + trust_remote_code: true + engine_output_type: latent + engine_input_source: [0] + + - stage_id: 2 + engine_args: + model_stage: code2wav + model_arch: Qwen2_5OmniForConditionalGeneration + gpu_memory_utilization: 0.3 + enforce_eager: true + trust_remote_code: true + enable_prefix_caching: false + engine_output_type: audio + engine_input_source: [1] + final_output: true + final_output_type: audio \ No newline at end of file diff --git a/vllm_omni/outputs.py b/vllm_omni/outputs.py new file mode 100644 index 000000000..2425fec48 --- /dev/null +++ b/vllm_omni/outputs.py @@ -0,0 +1,9 @@ +from vllm.outputs import RequestOutput +from dataclasses import dataclass + + +@dataclass +class OmniRequestOutput(RequestOutput): + stage_id: int + final_output_type: str + request_output: RequestOutput \ No newline at end of file From 91c11c3ff40c0a1a41ba605b28a8bde7ab178252 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 15:25:17 +0800 Subject: [PATCH 03/25] Add AR GPU Model Runner and Worker - Introduced ARModelRunner for autoregressive GPU model execution, enabling per-request hidden state outputs and token sampling. - Added ARGPUWorker to initialize GPU settings and manage distributed environment for the AR model runner. - Enhanced memory management and error handling for GPU resources during initialization. --- vllm_omni/worker/AR_gpu_model_runner.py | 578 ++++++++++++++++++++++++ vllm_omni/worker/AR_gpu_worker.py | 67 +++ 2 files changed, 645 insertions(+) create mode 100644 vllm_omni/worker/AR_gpu_model_runner.py create mode 100644 vllm_omni/worker/AR_gpu_worker.py diff --git a/vllm_omni/worker/AR_gpu_model_runner.py b/vllm_omni/worker/AR_gpu_model_runner.py new file mode 100644 index 000000000..6f32a3c03 --- /dev/null +++ b/vllm_omni/worker/AR_gpu_model_runner.py @@ -0,0 +1,578 @@ +"""AR GPU Model Runner for vLLM-omni. + +Exposes per-request hidden representations via ModelRunnerOutput.pooler_output +and also outputs sampled tokens. +""" + +from __future__ import annotations + +from typing import Optional, Union, Any, List +import numpy as np + +import torch + +from vllm import envs +from vllm.v1.worker.gpu_model_runner import ( + EMPTY_MODEL_RUNNER_OUTPUT, + IntermediateTensors, + get_pp_group, + get_tp_group, + has_kv_transfer_group, + set_forward_context, +) +from vllm.v1.core.sched.output import SchedulerOutput +from vllm.multimodal.inputs import MultiModalKwargs +from vllm.v1.attention.backends.utils import CommonAttentionMetadata +from vllm.v1.spec_decode.eagle import EagleProposer + +from vllm_omni.engine import PromptEmbedsPayload, AdditionalInformationPayload +from vllm_omni.outputs import OmniModelRunnerOutput +from vllm_omni.worker.gpu_model_runner import OmniGPUModelRunner + + +class ARModelRunner(OmniGPUModelRunner): + """Autoregressive GPU model runner that returns hidden states per request. + + This runner follows the same preparation and forward path as GPUModelRunner + (inputs assembly, multi-modal handling, TP/PP/DP integration, CUDA graphs), + and additionally performs lightweight sampling so that sampled tokens are + available in outputs. Hidden representations are taken at the same indices + that GPUModelRunner would use for sampling/logits (i.e. `logits_indices`). + """ + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.input_ids = torch.zeros(self.max_num_tokens, + dtype=torch.int64, + device=self.device) + + @torch.inference_mode() + def execute_model( + self, + scheduler_output: "SchedulerOutput", + intermediate_tensors: Optional[IntermediateTensors] = None, + ) -> Union[OmniModelRunnerOutput, IntermediateTensors]: + # Update internal state with the new schedule + self._update_states(scheduler_output) + + # Decode per-request prompt_embeds / additional_hidden_states payloads (if present) into CPU tensors + try: + new_reqs = getattr(scheduler_output, "scheduled_new_reqs", []) + if new_reqs: + import numpy as np + import torch + for nr in new_reqs: + req_id = getattr(nr, "req_id", None) or getattr(nr, "request_id", None) + if req_id is None: + continue + # prompt_embeds + payload_pe = getattr(nr, "prompt_embeds", None) + if payload_pe is not None: + if isinstance(payload_pe, torch.Tensor): + pe_cpu = payload_pe.detach().to("cpu").contiguous() + elif isinstance(payload_pe, PromptEmbedsPayload): + dt = np.dtype(getattr(payload_pe, "dtype", "float32")) + arr = np.frombuffer(payload_pe.data, dtype=dt) + arr = arr.reshape(payload_pe.shape) + pe_cpu = torch.from_numpy(arr) + else: + pe_cpu = None + if pe_cpu is not None and req_id in self.requests: + setattr(self.requests[req_id], "prompt_embeds_cpu", pe_cpu) + # additional_information + payload_info = getattr(nr, "additional_information", None) + if payload_info is not None: + info_dict = {} + if isinstance(payload_info, dict): + # Already decoded + info_dict = payload_info + elif isinstance(payload_info, AdditionalInformationPayload): + for k, entry in payload_info.entries.items(): + if entry.tensor_data is not None: + dt = np.dtype(getattr(entry, "tensor_dtype", "float32")) + arr = np.frombuffer(entry.tensor_data, dtype=dt) + arr = arr.reshape(entry.tensor_shape) + info_dict[k] = torch.from_numpy(arr) + else: + info_dict[k] = entry.list_data + if info_dict and req_id in self.requests: + setattr(self.requests[req_id], "additional_information_cpu", info_dict) + except Exception: + pass + + # If there's no work to do, either return empty output or kv-only path + if not scheduler_output.total_num_scheduled_tokens: + if not has_kv_transfer_group(): + return EMPTY_MODEL_RUNNER_OUTPUT + return self.kv_connector_no_forward(scheduler_output, + self.vllm_config) + + # Prepare decoder inputs and attention metadata + (attn_metadata, attention_cuda_graphs, logits_indices, + spec_decode_metadata, num_scheduled_tokens_np, + spec_decode_common_attn_metadata) = self._prepare_inputs( + scheduler_output) + + # Determine number of input tokens for this iteration + num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens + if (self.use_cuda_graph + and num_scheduled_tokens <= self.cudagraph_batch_sizes[-1]): + num_input_tokens = self.vllm_config.pad_for_cudagraph( + num_scheduled_tokens) + else: + tp_size = self.vllm_config.parallel_config.tensor_parallel_size + if (self.compilation_config.pass_config.enable_sequence_parallelism + and tp_size > 1): + from vllm.utils import round_up # lazy local import + num_input_tokens = round_up(num_scheduled_tokens, tp_size) + else: + num_input_tokens = num_scheduled_tokens + + # DP padding + num_pad, num_tokens_across_dp = self.get_dp_padding(num_input_tokens) + num_input_tokens += num_pad + + # Multimodal handling (encode and gather embeddings if needed) + if self.is_multimodal_model: + self._execute_mm_encoder(scheduler_output) + mm_embeds = self._gather_mm_embeddings(scheduler_output) + else: + mm_embeds = [] + + # Always assemble inputs_embeds on first PP rank; overlay per-request prompt_embeds and collect additional_hidden_states for prefill only + if get_pp_group().is_first_rank: + inputs_embeds_scheduled = self.model.get_input_embeddings( + input_ids=self.input_ids[:num_scheduled_tokens], + multimodal_embeddings=(mm_embeds or None) + if self.is_multimodal_model else None, + ) + + # Copy into persistent buffer to enable CUDA Graph capture + self.inputs_embeds[:num_scheduled_tokens].copy_( + inputs_embeds_scheduled) + + # Reset per-step additional information collector + if hasattr(self, "_forward_additional_information"): + self._forward_additional_information = None + + # Overlay custom prompt_embeds per request for the prompt portion; collect additional_information (tensor/list) for prefill portion only + for req_index, req_id in enumerate(self.input_batch.req_ids): + req_state = self.requests[req_id] + pe_cpu = getattr(req_state, "prompt_embeds_cpu", None) + addi_cpu = getattr(req_state, "additional_information_cpu", None) + num_computed_tokens = int( + self.input_batch.num_computed_tokens_cpu[req_index]) + prompt_len = len(req_state.prompt_token_ids) + prompt_remaining = max(0, prompt_len - num_computed_tokens) + sched_tokens = int(num_scheduled_tokens_np[req_index]) + overlay_len = min(sched_tokens, prompt_remaining) + if overlay_len <= 0: + continue + if pe_cpu is not None: + src = pe_cpu[num_computed_tokens: + num_computed_tokens + overlay_len].to( + dtype=self.dtype, + device=self.device, + non_blocking=True) + start_offset = int(self.query_start_loc_cpu[req_index]) + self.inputs_embeds[start_offset:start_offset + overlay_len] \ + .copy_(src) + # For additional_information: handle arbitrary keys + if addi_cpu is not None and isinstance(addi_cpu, dict): + # Lazy init collector dict + if not hasattr(self, "_forward_additional_information") or \ + self._forward_additional_information is None: + self._forward_additional_information = {} + # Process tensors (slice by scheduled prompt range) and lists (append per-request) + for k, v in addi_cpu.items(): + if isinstance(v, torch.Tensor): + # Slice along token dimension for prefill part + try: + seg = v[num_computed_tokens: + num_computed_tokens + overlay_len].to( + dtype=self.dtype, + device=self.device, + non_blocking=True) + except Exception: + # Fallback: move whole tensor if slicing fails + seg = v.to(dtype=self.dtype, + device=self.device, + non_blocking=True) + prev_val = self._forward_additional_information.get(k) + self._forward_additional_information[k] = ( + torch.cat([prev_val, seg], dim=0) + if isinstance(prev_val, torch.Tensor) else seg.clone()) + elif isinstance(v, list): + prev_val = self._forward_additional_information.get(k) + if prev_val is None: + self._forward_additional_information[k] = [v] + elif isinstance(prev_val, list): + self._forward_additional_information[k].append(v) + else: + # Mixed types: wrap existing into list + self._forward_additional_information[k] = [prev_val, v] + + input_ids = self.input_ids[:num_input_tokens] # preserved for APIs + inputs_embeds = self.inputs_embeds[:num_input_tokens] + model_mm_kwargs = (self._extract_mm_kwargs(scheduler_output) + if self.is_multimodal_model else {}) + else: + input_ids = self.input_ids[:num_input_tokens] + inputs_embeds = None + model_mm_kwargs = {} + + # Positions/mRoPE + if self.uses_mrope: + positions = self.mrope_positions[:, :num_input_tokens] + else: + positions = self.positions[:num_input_tokens] + + # Handle pipeline-parallel intermediate tensors + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_input_tokens, intermediate_tensors, True) + + # Some attention backends only support CUDA Graphs in pure decode. + skip_cuda_graphs = self.full_cuda_graph and not attention_cuda_graphs + + # Forward pass + with set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_input_tokens, + num_tokens_across_dp=num_tokens_across_dp, + skip_cuda_graphs=skip_cuda_graphs, + ), self.maybe_get_kv_connector_output( + scheduler_output) as kv_connector_output: + + model_kwargs_extra = {} + # Only pass additional_information for the prefill part + if hasattr(self, "_forward_additional_information") and \ + self._forward_additional_information is not None and \ + isinstance(self._forward_additional_information, dict): + model_kwargs_extra["additional_information"] = self._forward_additional_information + model_output = self.model( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs( + model_mm_kwargs, + device=self.device, + ), + sampling_metadata=self.input_batch.sampling_metadata, + logits_index=logits_indices, + sampler=self.sampler, + **model_kwargs_extra, + ) + + if self.use_aux_hidden_state_outputs: + hidden_states, _aux_hidden_states = model_output + else: + hidden_states = model_output + + text_hidden_states, multimodal_outputs = ( + self.extract_multimodal_outputs(hidden_states)) + + # Mid PP stages return intermediate tensors unmodified + if not get_pp_group().is_last_rank: + assert isinstance(text_hidden_states, IntermediateTensors) + text_hidden_states.kv_connector_output = kv_connector_output + return text_hidden_states + + # Broadcast PP output for external_launcher (torchrun) + broadcast_pp_output = \ + self.parallel_config.distributed_executor_backend \ + == "external_launcher" and len(get_pp_group().ranks) > 0 + if not get_pp_group().is_last_rank: + assert isinstance(text_hidden_states, IntermediateTensors) + if not broadcast_pp_output: + text_hidden_states.kv_connector_output = kv_connector_output + return text_hidden_states + get_pp_group().send_tensor_dict(text_hidden_states.tensors, + all_gather_group=get_tp_group()) + logits = None + else: + if self.input_batch.pooling_params: + return self._pool(text_hidden_states, num_scheduled_tokens, + num_scheduled_tokens_np, kv_connector_output) + + sample_hidden_states = text_hidden_states[logits_indices] + logits = self.model.compute_logits(sample_hidden_states, None) + if broadcast_pp_output: + model_output_broadcast_data = { + "logits": logits.contiguous(), + } if logits is not None else {} + model_output_broadcast_data = get_pp_group().broadcast_tensor_dict( + model_output_broadcast_data, src=len(get_pp_group().ranks) - 1) + assert model_output_broadcast_data is not None + logits = model_output_broadcast_data["logits"] + + # Apply structured output bitmasks if present + if scheduler_output.grammar_bitmask is not None: + self.apply_grammar_bitmask(scheduler_output, logits) + + # Sample the next token and get logprobs if needed (with spec decode) + sampling_metadata = self.input_batch.sampling_metadata + if spec_decode_metadata is None: + sampler_output = self.sampler( + logits=logits, + sampling_metadata=sampling_metadata, + ) + else: + assert logits is not None + bonus_logits = logits[spec_decode_metadata.bonus_logits_indices] + sampler_output = self.sampler( + logits=bonus_logits, + sampling_metadata=sampling_metadata, + ) + bonus_token_ids = sampler_output.sampled_token_ids + target_logits = logits[spec_decode_metadata.target_logits_indices] + output_token_ids = self.rejection_sampler( + spec_decode_metadata, + None, + target_logits, + bonus_token_ids, + sampling_metadata, + ) + sampler_output.sampled_token_ids = output_token_ids + + num_nans_in_logits = {} + if envs.VLLM_COMPUTE_NANS_IN_LOGITS: + num_nans_in_logits = self._get_nans_in_logits(logits) + + # Handle partial prefill: discard sampled tokens and rewind RNG + discard_sampled_tokens_req_indices = [] + for i, req_id in enumerate(self.input_batch.req_ids): + req_state = self.requests[req_id] + seq_len = (req_state.num_computed_tokens + + scheduler_output.num_scheduled_tokens[req_id]) + if seq_len < req_state.num_tokens: + generator = self.input_batch.generators.get(i) + if generator is not None: + generator.set_offset(generator.get_offset() - 4) + discard_sampled_tokens_req_indices.append(i) + + # Move CPU sync parts + logprobs_tensors = sampler_output.logprobs_tensors + logprobs_lists = logprobs_tensors.tolists() \ + if logprobs_tensors is not None else None + + # Prompt logprobs if needed + prompt_logprobs_dict = self._get_prompt_logprobs_dict( + text_hidden_states[:num_scheduled_tokens], + scheduler_output, + ) + + # Parse valid sampled tokens + sampled_token_ids = sampler_output.sampled_token_ids + max_gen_len = sampled_token_ids.shape[-1] + if max_gen_len == 1: + valid_sampled_token_ids = sampled_token_ids.tolist() + else: + valid_sampled_token_ids = self.rejection_sampler.parse_output( + sampled_token_ids, + self.input_batch.vocab_size, + ) + for i in discard_sampled_tokens_req_indices: + valid_sampled_token_ids[i].clear() + + # Cache sampled tokens + for req_idx, sampled_ids in enumerate(valid_sampled_token_ids): + if not sampled_ids: + continue + start_idx = self.input_batch.num_tokens_no_spec[req_idx] + end_idx = start_idx + len(sampled_ids) + self.input_batch.token_ids_cpu[req_idx, start_idx:end_idx] = sampled_ids + self.input_batch.num_tokens_no_spec[req_idx] = end_idx + self.input_batch.num_tokens[req_idx] = end_idx + req_id = self.input_batch.req_ids[req_idx] + req_state = self.requests[req_id] + req_state.output_token_ids.extend(sampled_ids) + + # Speculative decoding draft tokens if configured + if not self.speculative_config: + spec_token_ids = None + else: + assert spec_decode_common_attn_metadata is not None + spec_token_ids = self.propose_draft_token_ids( + scheduler_output, + valid_sampled_token_ids, + sampling_metadata, + text_hidden_states, + sample_hidden_states, + _aux_hidden_states if '_aux_hidden_states' in locals() else None, + spec_decode_metadata, + spec_decode_common_attn_metadata, + ) + + # Convert to per-request tensors on CPU + pooler_output: list[Optional[torch.Tensor]] = [] + prev_logits_index = 0 + for logits_index in logits_indices: + pooler_output.append(text_hidden_states[prev_logits_index:logits_index+1].detach().to("cpu").contiguous()) + prev_logits_index = logits_index + 1 + + + self.eplb_step() + + return OmniModelRunnerOutput( + req_ids=self.input_batch.req_ids, + req_id_to_index=self.input_batch.req_id_to_index, + sampled_token_ids=valid_sampled_token_ids, + spec_token_ids=spec_token_ids, + logprobs=logprobs_lists, + prompt_logprobs_dict=prompt_logprobs_dict, + pooler_output=pooler_output if self.vllm_config.model_config.engine_output_type != "text" else None, + kv_connector_output=kv_connector_output, + num_nans_in_logits=num_nans_in_logits, + ) + + @torch.inference_mode() + def extract_multimodal_outputs(self, hidden_states: Union[torch.Tensor, List[torch.Tensor]]) -> dict: + if hasattr(self.model, "have_multimodal_outputs") and self.model.have_multimodal_outputs: + text_hidden_states = hidden_states.text_hidden_states + multimodal_outputs = hidden_states.multimodal_outputs + + elif isinstance(hidden_states, torch.Tensor): + text_hidden_states = hidden_states + multimodal_outputs = {} + elif isinstance(hidden_states, List): + text_hidden_states = hidden_states[0] + multimodal_outputs = {} + else: + raise ValueError(f"Invalid hidden states type: {type(hidden_states)}") + return text_hidden_states, multimodal_outputs + + @torch.inference_mode() + def _dummy_run( + self, + num_tokens: int, + capture_attn_cudagraph: bool = False, + skip_eplb: bool = False, + is_profile: bool = False, + ) -> tuple[torch.Tensor, torch.Tensor]: + + # Padding for DP + num_pad, num_tokens_across_dp = self.get_dp_padding(num_tokens) + num_tokens += num_pad + + # Set num_scheduled_tokens based on num_tokens and max_num_seqs + # for dummy run with LoRA so that the num_reqs collectively + # has num_tokens in total. + assert num_tokens <= self.scheduler_config.max_num_batched_tokens + max_num_reqs = self.scheduler_config.max_num_seqs + num_reqs = min(num_tokens, max_num_reqs) + min_tokens_per_req = num_tokens // num_reqs + num_scheduled_tokens_list = [min_tokens_per_req] * num_reqs + num_scheduled_tokens_list[-1] += num_tokens % num_reqs + assert sum(num_scheduled_tokens_list) == num_tokens + assert len(num_scheduled_tokens_list) == num_reqs + num_scheduled_tokens = np.array(num_scheduled_tokens_list, + dtype=np.int32) + + attn_metadata: Optional[dict[str, Any]] = None + if capture_attn_cudagraph: + attn_metadata = {} + + # Make sure max_model_len is used at the graph capture time. + self.seq_lens_np[:num_reqs] = self.max_model_len + self.seq_lens_np[num_reqs:] = 0 + self.seq_lens[:num_reqs].copy_(self.seq_lens_cpu[:num_reqs], + non_blocking=True) + + for kv_cache_group_id, kv_cache_group_spec in enumerate( + self.kv_cache_config.kv_cache_groups): + common_attn_metadata = CommonAttentionMetadata( + query_start_loc=self.query_start_loc[:num_reqs + 1], + query_start_loc_cpu=self.query_start_loc_cpu[:num_reqs + + 1], + seq_lens=self.seq_lens[:num_reqs], + seq_lens_cpu=self.seq_lens_cpu[:num_reqs], + num_computed_tokens_cpu=self.input_batch. + num_computed_tokens_cpu_tensor[:num_reqs], + num_reqs=num_reqs, + num_actual_tokens=num_tokens, + max_query_len=num_tokens, + block_table_tensor=self.input_batch.block_table[ + kv_cache_group_id].get_device_tensor()[:num_reqs], + slot_mapping=self.input_batch. + block_table[kv_cache_group_id].slot_mapping[:num_tokens], + causal=True) + + for attn_group in self.attn_groups[kv_cache_group_id]: + attn_metadata_i = attn_group.metadata_builder\ + .build_for_cudagraph_capture(common_attn_metadata) + for layer_name in kv_cache_group_spec.layer_names: + attn_metadata[layer_name] = attn_metadata_i + + with self.maybe_dummy_run_with_lora(self.lora_config, + num_scheduled_tokens): + if self.is_multimodal_model: + input_ids = None + inputs_embeds = self.inputs_embeds[:num_tokens] + model_mm_kwargs = self._dummy_mm_kwargs(num_reqs) + else: + input_ids = self.input_ids[:num_tokens] + inputs_embeds = None + model_mm_kwargs = {} + + if self.uses_mrope: + positions = self.mrope_positions[:, :num_tokens] + else: + positions = self.positions[:num_tokens] + + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + if self.intermediate_tensors is None: + self.intermediate_tensors = ( + self.model.make_empty_intermediate_tensors( + batch_size=self.max_num_tokens, + dtype=self.model_config.dtype, + device=self.device)) + + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_tokens, None, False) + + with self.maybe_randomize_inputs(input_ids), set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_tokens, + num_tokens_across_dp=num_tokens_across_dp): + outputs = self.model( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs( + model_mm_kwargs, + device=self.device, + ), + ) + + if self.use_aux_hidden_state_outputs: + hidden_states, _ = outputs + else: + hidden_states = outputs + + if self.speculative_config and self.speculative_config.use_eagle(): + assert isinstance(self.drafter, EagleProposer) + self.drafter.dummy_run(num_tokens) + + # This is necessary to avoid blocking DP. + # For dummy runs, we typically skip EPLB since we don't have any real + # requests to process. + # However, in DP settings, there may be cases when some DP ranks do + # not have any requests to process, so they're executing dummy batches. + # In such cases, we still have to trigger EPLB to make sure + # ranks execute the rearrangement in synchronization. + if not skip_eplb: + self.eplb_step(is_dummy=True, is_profile=is_profile) + + logit_indices = np.cumsum(num_scheduled_tokens) - 1 + hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states) + return hidden_states, hidden_states[logit_indices] + diff --git a/vllm_omni/worker/AR_gpu_worker.py b/vllm_omni/worker/AR_gpu_worker.py new file mode 100644 index 000000000..07e080884 --- /dev/null +++ b/vllm_omni/worker/AR_gpu_worker.py @@ -0,0 +1,67 @@ +from vllm.v1.worker.gpu_worker import Worker as GPUWorker +import torch +import os +import gc +from vllm.utils import GiB_bytes, MemorySnapshot +from vllm.platforms import current_platform +from vllm.worker.worker import _check_if_gpu_supports_dtype +from vllm.v1.worker.gpu_worker import init_worker_distributed_environment +from vllm.model_executor import set_random_seed +from vllm.v1.utils import report_usage_stats + +from vllm_omni.worker.AR_gpu_model_runner import ARModelRunner + + +class ARGPUWorker(GPUWorker): + def init_device(self): + if self.device_config.device.type == "cuda": + # torch.distributed.all_reduce does not free the input tensor until + # the synchronization point. This causes the memory usage to grow + # as the number of all_reduce calls increases. This env var disables + # this behavior. + # Related issue: + # https://discuss.pytorch.org/t/cuda-allocation-lifetime-for-inputs-to-distributed-all-reduce/191573 + os.environ["TORCH_NCCL_AVOID_RECORD_STREAMS"] = "1" + + # This env var set by Ray causes exceptions with graph building. + os.environ.pop("NCCL_ASYNC_ERROR_HANDLING", None) + self.device = torch.device(f"cuda:{self.local_rank}") + current_platform.set_device(self.device) + + _check_if_gpu_supports_dtype(self.model_config.dtype) + gc.collect() + torch.cuda.empty_cache() + + # take current memory snapshot + self.init_snapshot = MemorySnapshot() + self.requested_memory = (self.init_snapshot.total_memory * + self.cache_config.gpu_memory_utilization) + if self.init_snapshot.free_memory < self.requested_memory: + GiB = lambda b: round(b / GiB_bytes, 2) + raise ValueError( + f"Free memory on device " + f"({GiB(self.init_snapshot.free_memory)}/" + f"{GiB(self.init_snapshot.total_memory)} GiB) on startup " + f"is less than desired GPU memory utilization " + f"({self.cache_config.gpu_memory_utilization}, " + f"{GiB(self.requested_memory)} GiB). Decrease GPU memory " + f"utilization or reduce GPU memory used by other processes." + ) + else: + raise RuntimeError( + f"Not support device type: {self.device_config.device}") + # Initialize the distributed environment. + init_worker_distributed_environment(self.vllm_config, self.rank, + self.distributed_init_method, + self.local_rank, + current_platform.dist_backend) + # Set random seed. + set_random_seed(self.model_config.seed) + + # Construct the model runner + self.model_runner: ARModelRunner = ARModelRunner( + self.vllm_config, self.device) + + if self.rank == 0: + # If usage stat is enabled, collect relevant info. + report_usage_stats(self.vllm_config) \ No newline at end of file From 9edce9e12a903b940d24e100e775f6a4587b500c Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Wed, 22 Oct 2025 15:26:42 +0800 Subject: [PATCH 04/25] Add end2end example and documentation for qwen2.5-omni --- .../offline_inference/qwen_2_5_omni/README.md | 37 ++ .../qwen_2_5_omni/end2end.py | 130 ++++++ .../qwen_2_5_omni/processing_omni.py | 373 ++++++++++++++++++ .../offline_inference/qwen_2_5_omni/run.sh | 9 + .../offline_inference/qwen_2_5_omni/utils.py | 305 ++++++++++++++ 5 files changed, 854 insertions(+) create mode 100644 examples/offline_inference/qwen_2_5_omni/README.md create mode 100644 examples/offline_inference/qwen_2_5_omni/end2end.py create mode 100644 examples/offline_inference/qwen_2_5_omni/processing_omni.py create mode 100644 examples/offline_inference/qwen_2_5_omni/run.sh create mode 100644 examples/offline_inference/qwen_2_5_omni/utils.py diff --git a/examples/offline_inference/qwen_2_5_omni/README.md b/examples/offline_inference/qwen_2_5_omni/README.md new file mode 100644 index 000000000..d1dfde205 --- /dev/null +++ b/examples/offline_inference/qwen_2_5_omni/README.md @@ -0,0 +1,37 @@ +# Offline Example of vLLM-omni for Qwen2.5-omni + +## Installation + +Set up basic environments +```bash +uv venv --python 3.12 --seed +source .venv/bin/activate +``` +Install certain version of vllm with commitid: 808a7b69df479b6b3a16181711cac7ca28a9b941 + +```bash +git clone https://github.com/vllm-project/vllm.git +cd vllm +git checkout 808a7b69df479b6b3a16181711cac7ca28a9b941 +VLLM_USE_PRECOMPILED=1 uv pip install --editable . +``` + +## Run examples + +Get into the example folder +```bash +cd vllm_omni +cd examples/offline_inference/qwen2_5_omni +``` +Modify PYTHONPATH in run.sh as your path of vllm_omni. Then run. +```bash +bash run.sh +``` +The output audio is saved in ./output_audio + +## To-do list +- [x] Offline inference example for Qwen2.5-omni with single request +- [ ] Adaptation from current vllm branch to stable vllm v0.11.0 +- [ ] Offline inference example for Qwen2.5-omni with streaming multiple requests +- [ ] Online inference support +- [ ] Support for other models \ No newline at end of file diff --git a/examples/offline_inference/qwen_2_5_omni/end2end.py b/examples/offline_inference/qwen_2_5_omni/end2end.py new file mode 100644 index 000000000..030db0b71 --- /dev/null +++ b/examples/offline_inference/qwen_2_5_omni/end2end.py @@ -0,0 +1,130 @@ +import argparse +import os +import soundfile as sf +import random +import numpy as np +import torch + +from vllm.sampling_params import SamplingParams + +import os as _os_env_toggle +_os_env_toggle.environ["VLLM_USE_V1"] = "1" + +from vllm_omni.entrypoints.omni_llm import OmniLLM +from utils import make_omni_prompt + + +SEED = 42 +# Set all random seeds +random.seed(SEED) +np.random.seed(SEED) +torch.manual_seed(SEED) +torch.cuda.manual_seed(SEED) +torch.cuda.manual_seed_all(SEED) + +# Make PyTorch deterministic +torch.backends.cudnn.deterministic = True +torch.backends.cudnn.benchmark = False + +# Set environment variables for deterministic behavior +os.environ["PYTHONHASHSEED"] = str(SEED) +os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--model', required=True, help='Path to merged model directory (will be created if downloading).') + parser.add_argument('--thinker-model', type=str, default=None) + parser.add_argument('--talker-model', type=str, default=None) + parser.add_argument('--code2wav-model', type=str, default=None) + parser.add_argument('--hf-hub-id', default='Qwen/Qwen2.5-Omni-7B', help='Hugging Face repo id to download if needed.') + parser.add_argument('--hf-revision', default=None, help='Optional HF revision (branch/tag/commit).') + parser.add_argument('--prompts', required=True, nargs='+', help='Input text prompts.') + parser.add_argument('--voice-type', default='default', help='Voice type, e.g., m02, f030, default.') + parser.add_argument('--code2wav-dir', default=None, help='Path to code2wav folder (contains spk_dict.pt).') + parser.add_argument('--dit-ckpt', default=None, help='Path to DiT checkpoint file (e.g., dit.pt).') + parser.add_argument('--bigvgan-ckpt', default=None, help='Path to BigVGAN checkpoint file.') + parser.add_argument('--dtype', default='bfloat16', choices=['float16', 'bfloat16', 'float32']) + parser.add_argument('--max-model-len', type=int, default=32768) + + parser.add_argument("--thinker-only", action="store_true") + parser.add_argument("--text-only", action="store_true") + parser.add_argument("--do-wave", action="store_true") + parser.add_argument('--prompt_type', + choices=[ + 'text', 'audio', 'audio-long', 'audio-long-chunks', + 'audio-long-expand-chunks', 'image', 'video', + 'video-frames', 'audio-in-video', 'audio-in-video-v2', + "audio-multi-round", "badcase-vl", "badcase-text", + "badcase-image-early-stop", "badcase-two-audios", + "badcase-two-videos", "badcase-multi-round", + "badcase-voice-type", "badcase-voice-type-v2", + "badcase-audio-tower-1", "badcase-audio-only" + ], + default='text') + parser.add_argument('--use-torchvision', action='store_true') + parser.add_argument('--tokenize', action='store_true') + parser.add_argument('--output-wav', default="output.wav", help='Output wav file path.') + parser.add_argument('--thinker-hidden-states-dir', default="thinker_hidden_states", help='Path to thinker hidden states directory.') + args = parser.parse_args() + return args + + +def main(): + args = parse_args() + model_name = args.model + omni_llm = OmniLLM(model=model_name) + thinker_sampling_params = SamplingParams( + temperature=0.0, # Deterministic - no randomness + top_p=1.0, # Disable nucleus sampling + top_k=-1, # Disable top-k sampling + max_tokens=2048, + seed=SEED, # Fixed seed for sampling + detokenize=True, + repetition_penalty=1.1, + ) + talker_sampling_params = SamplingParams( + temperature=0.0, # Deterministic - no randomness + top_p=1.0, # Disable nucleus sampling + top_k=-1, # Disable top-k sampling + max_tokens=2048, + seed=SEED, # Fixed seed for sampling + detokenize=True, + repetition_penalty=1.1, + stop_token_ids=[8294] + ) + code2wav_sampling_params = SamplingParams( + temperature=0.0, # Deterministic - no randomness + top_p=1.0, # Disable nucleus sampling + top_k=-1, # Disable top-k sampling + max_tokens=2048, + seed=SEED, # Fixed seed for sampling + detokenize=True, + repetition_penalty=1.1, + ) + + sampling_params_list = [thinker_sampling_params, + talker_sampling_params, + code2wav_sampling_params] + + prompt = [make_omni_prompt(args, prompt) for prompt in args.prompts] + omni_outputs = omni_llm.generate(prompt, sampling_params_list) + + os.makedirs(args.output_wav, exist_ok=True) + for stage_outputs in omni_outputs: + if stage_outputs.final_output_type == "text": + for output in stage_outputs.request_output: + request_id = output.request_id + text_output = output.outputs[0].text + print(f"Request ID: {request_id}, Text Output: {text_output}") + elif stage_outputs.final_output_type == "audio": + for output in stage_outputs.request_output: + request_id = output.request_id + audio_tensor = output.multimodal_output["audio"] + output_wav = os.path.join(args.output_wav, f"output_{output.request_id}.wav") + sf.write(output_wav, audio_tensor.detach().cpu().numpy(), samplerate=24000) + print(f"Request ID: {request_id}, Saved audio to {output_wav}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/offline_inference/qwen_2_5_omni/processing_omni.py b/examples/offline_inference/qwen_2_5_omni/processing_omni.py new file mode 100644 index 000000000..bd6f4ae95 --- /dev/null +++ b/examples/offline_inference/qwen_2_5_omni/processing_omni.py @@ -0,0 +1,373 @@ +from __future__ import annotations + +import base64 +import logging +import math +import os +import time +import warnings +from functools import lru_cache +from io import BytesIO +import requests +import torch +import torchvision +from packaging import version +from PIL import Image +from torchvision import io, transforms +from torchvision.transforms import InterpolationMode + + +logger = logging.getLogger(__name__) + +IMAGE_FACTOR = 28 +MIN_PIXELS = 4 * 28 * 28 +MAX_PIXELS = 16384 * 28 * 28 +MAX_RATIO = 200 + +VIDEO_MIN_PIXELS = 128 * 28 * 28 +VIDEO_MAX_PIXELS = 768 * 28 * 28 +VIDEO_TOTAL_PIXELS = 24576 * 28 * 28 +FRAME_FACTOR = 2 +FPS = 2.0 +FPS_MIN_FRAMES = 4 +FPS_MAX_FRAMES = 768 + +temporal_patch_size = 2 +spatial_patch_size = 14 +spatial_merge_size = 2 + + +def round_by_factor(number: int, factor: int) -> int: + """Returns the closest integer to 'number' that is divisible by 'factor'.""" + return round(number / factor) * factor + + +def ceil_by_factor(number: int, factor: int) -> int: + """Returns the smallest integer greater than or equal to 'number' that is divisible by 'factor'.""" + return math.ceil(number / factor) * factor + + +def floor_by_factor(number: int, factor: int) -> int: + """Returns the largest integer less than or equal to 'number' that is divisible by 'factor'.""" + return math.floor(number / factor) * factor + + +def smart_resize(height: int, + width: int, + factor: int = IMAGE_FACTOR, + min_pixels: int = MIN_PIXELS, + max_pixels: int = MAX_PIXELS) -> tuple[int, int]: + """ + Rescales the image so that the following conditions are met: + + 1. Both dimensions (height and width) are divisible by 'factor'. + + 2. The total number of pixels is within the range ['min_pixels', 'max_pixels']. + + 3. The aspect ratio of the image is maintained as closely as possible. + """ + if max(height, width) / min(height, width) > MAX_RATIO: + raise ValueError( + f"absolute aspect ratio must be smaller than {MAX_RATIO}, got {max(height, width) / min(height, width)}" + ) + h_bar = max(factor, round_by_factor(height, factor)) + w_bar = max(factor, round_by_factor(width, factor)) + if h_bar * w_bar > max_pixels: + beta = math.sqrt((height * width) / max_pixels) + h_bar = floor_by_factor(height / beta, factor) + w_bar = floor_by_factor(width / beta, factor) + elif h_bar * w_bar < min_pixels: + beta = math.sqrt(min_pixels / (height * width)) + h_bar = ceil_by_factor(height * beta, factor) + w_bar = ceil_by_factor(width * beta, factor) + return h_bar, w_bar + + +def fetch_image(ele: dict[str, str | Image.Image], + size_factor: int = IMAGE_FACTOR) -> Image.Image: + if "image" in ele: + image = ele["image"] + else: + image = ele["image_url"] + image_obj = None + if isinstance(image, Image.Image): + image_obj = image + elif image.startswith("http://") or image.startswith("https://"): + image_obj = Image.open(requests.get(image, stream=True).raw) + elif image.startswith("file://"): + image_obj = Image.open(image[7:]) + elif image.startswith("data:image"): + if "base64," in image: + _, base64_data = image.split("base64,", 1) + data = base64.b64decode(base64_data) + image_obj = Image.open(BytesIO(data)) + else: + image_obj = Image.open(image) + if image_obj is None: + raise ValueError( + f"Unrecognized image input, support local path, http url, base64 and PIL.Image, got {image}" + ) + image = image_obj.convert("RGB") + ## resize + if "resized_height" in ele and "resized_width" in ele: + resized_height, resized_width = smart_resize( + ele["resized_height"], + ele["resized_width"], + factor=size_factor, + ) + else: + width, height = image.size + min_pixels = ele.get("min_pixels", MIN_PIXELS) + max_pixels = ele.get("max_pixels", MAX_PIXELS) + resized_height, resized_width = smart_resize( + height, + width, + factor=size_factor, + min_pixels=min_pixels, + max_pixels=max_pixels, + ) + image = image.resize((resized_width, resized_height)) + + return image + + +def smart_nframes( + ele: dict, + total_frames: int, + video_fps: int | float, +) -> int: + """calculate the number of frames for video used for model inputs. + + Args: + ele (dict): a dict contains the configuration of video. + support either `fps` or `nframes`: + - nframes: the number of frames to extract for model inputs. + - fps: the fps to extract frames for model inputs. + - min_frames: the minimum number of frames of the video, only used when fps is provided. + - max_frames: the maximum number of frames of the video, only used when fps is provided. + total_frames (int): the original total number of frames of the video. + video_fps (int | float): the original fps of the video. + + Raises: + ValueError: nframes should in interval [FRAME_FACTOR, total_frames]. + + Returns: + int: the number of frames for video used for model inputs. + """ + assert not ("fps" in ele + and "nframes" in ele), "Only accept either `fps` or `nframes`" + if "nframes" in ele: + nframes = round_by_factor(ele["nframes"], FRAME_FACTOR) + else: + fps = ele.get("fps", FPS) + min_frames = ceil_by_factor(ele.get("min_frames", FPS_MIN_FRAMES), + FRAME_FACTOR) + max_frames = floor_by_factor( + ele.get("max_frames", min(FPS_MAX_FRAMES, total_frames)), + FRAME_FACTOR) + nframes = total_frames / video_fps * fps + nframes = min(max(nframes, min_frames), max_frames) + nframes = round_by_factor(nframes, FRAME_FACTOR) + if not (FRAME_FACTOR <= nframes and nframes <= total_frames): + raise ValueError( + f"nframes should in interval [{FRAME_FACTOR}, {total_frames}], but got {nframes}." + ) + return nframes + + +def _read_video_torchvision(ele: dict, ) -> torch.Tensor: + """read video using torchvision.io.read_video + + Args: + ele (dict): a dict contains the configuration of video. + support keys: + - video: the path of video. support "file://", "http://", "https://" and local path. + - video_start: the start time of video. + - video_end: the end time of video. + Returns: + torch.Tensor: the video tensor with shape (T, C, H, W). + """ + video_path = ele["video"] + if version.parse(torchvision.__version__) < version.parse("0.19.0"): + if "http://" in video_path or "https://" in video_path: + warnings.warn( + "torchvision < 0.19.0 does not support http/https video path, please upgrade to 0.19.0." + ) + if "file://" in video_path: + video_path = video_path[7:] + st = time.time() + video, audio, info = io.read_video( + video_path, + start_pts=ele.get("video_start", 0.0), + end_pts=ele.get("video_end", None), + pts_unit="sec", + output_format="TCHW", + ) + total_frames, video_fps = video.size(0), info["video_fps"] + total_duration = round(total_frames / video_fps, 3) + logger.info( + f"torchvision: {video_path=}, {total_frames=}, {video_fps=}, duration={total_duration}s, time={time.time() - st:.3f}s" + ) + nframes = smart_nframes(ele, + total_frames=total_frames, + video_fps=video_fps) + idx = torch.linspace(0, total_frames - 1, nframes).round().long() + video = video[idx] + return video, total_duration, nframes + + +def is_decord_available() -> bool: + import importlib.util + + return importlib.util.find_spec("decord") is not None + + +def _read_video_decord(ele: dict, ) -> torch.Tensor: + """read video using decord.VideoReader + + Args: + ele (dict): a dict contains the configuration of video. + support keys: + - video: the path of video. support "file://", "http://", "https://" and local path. + - video_start: the start time of video. + - video_end: the end time of video. + Returns: + torch.Tensor: the video tensor with shape (T, C, H, W). + """ + import decord + video_path = ele["video"] + st = time.time() + vr = decord.VideoReader(video_path) + # TODO: support start_pts and end_pts + if 'video_start' in ele or 'video_end' in ele: + raise NotImplementedError( + "not support start_pts and end_pts in decord for now.") + total_frames, video_fps = len(vr), vr.get_avg_fps() + total_duration = round(total_frames / video_fps, 3) + logger.info( + f"decord: {video_path=}, {total_frames=}, {video_fps=}, time={time.time() - st:.3f}s" + ) + nframes = smart_nframes(ele, + total_frames=total_frames, + video_fps=video_fps) + idx = torch.linspace(0, total_frames - 1, nframes).round().long().tolist() + video = vr.get_batch(idx).asnumpy() + video = torch.tensor(video).permute(0, 3, 1, 2) # Convert to TCHW format + return video, total_duration, nframes + + +VIDEO_READER_BACKENDS = { + "decord": _read_video_decord, + "torchvision": _read_video_torchvision, +} + +FORCE_QWENVL_VIDEO_READER = os.getenv("FORCE_QWENVL_VIDEO_READER", None) + + +@lru_cache(maxsize=1) +def get_video_reader_backend() -> str: + if FORCE_QWENVL_VIDEO_READER is not None: + video_reader_backend = FORCE_QWENVL_VIDEO_READER + elif is_decord_available(): + video_reader_backend = "decord" + else: + video_reader_backend = "torchvision" + # print(f"qwen-vl-utils using {video_reader_backend} to read video.", file=sys.stderr) + return video_reader_backend + + +def fetch_video( + ele: dict, + image_factor: int = IMAGE_FACTOR) -> torch.Tensor | list[Image.Image]: + if isinstance(ele["video"], str): + video_reader_backend = get_video_reader_backend() + video, total_dur, nframes = VIDEO_READER_BACKENDS[ + video_reader_backend](ele) + frame_timestamps = total_dur * torch.arange(1, nframes + 1) / nframes + grid_timestamps = frame_timestamps[::FRAME_FACTOR] + second_per_grid = grid_timestamps[1] - grid_timestamps[0] + nframes, _, height, width = video.shape + factor = spatial_patch_size * spatial_merge_size + min_pixels = ele.get("min_pixels", VIDEO_MIN_PIXELS) + total_pixels = ele.get("total_pixels", VIDEO_TOTAL_PIXELS) + max_pixels = max( + min(VIDEO_MAX_PIXELS, total_pixels / nframes * FRAME_FACTOR), + int(min_pixels * 1.05)) + max_pixels = ele.get("max_pixels", max_pixels) + # min_pixels = (factor ** 2) * 52 + # max_pixels = (factor ** 2) * min(768, (16384 / nframes * temporal_patch_size)) + if "resized_height" in ele and "resized_width" in ele: + resized_height, resized_width = smart_resize( + ele["resized_height"], + ele["resized_width"], + factor=image_factor, + ) + else: + resized_height, resized_width = smart_resize( + height, + width, + factor=image_factor, + min_pixels=min_pixels, + max_pixels=max_pixels, + ) + video = transforms.functional.resize( + video, + [resized_height, resized_width], + interpolation=InterpolationMode.BICUBIC, + antialias=True, + ).float() + return video, total_dur, nframes, second_per_grid + else: + assert isinstance(ele["video"], (list, tuple)) + process_info = ele.copy() + process_info.pop("type", None) + process_info.pop("video", None) + images = [ + fetch_image({ + "image": video_element, + **process_info + }, + size_factor=image_factor) + for video_element in ele["video"] + ] + nframes = ceil_by_factor(len(images), FRAME_FACTOR) + if len(images) < nframes: + images.extend([images[-1]] * (nframes - len(images))) + return images, None, None, None + + +def extract_vision_info( + conversations: list[dict] | list[list[dict]]) -> list[dict]: + vision_infos = [] + if isinstance(conversations[0], dict): + conversations = [conversations] + for conversation in conversations: + for message in conversation: + if isinstance(message["content"], list): + for ele in message["content"]: + if ("image" in ele or "image_url" in ele or "video" in ele + or ele["type"] in ("image", "image_url", "video")): + vision_infos.append(ele) + return vision_infos + + +def process_vision_info( + conversations: list[dict] | list[list[dict]], +) -> tuple[list[Image.Image] | None, list[torch.Tensor | list[Image.Image]] + | None]: + vision_infos = extract_vision_info(conversations) + ## Read images or videos + image_inputs = [] + video_inputs = [] + for vision_info in vision_infos: + if "image" in vision_info or "image_url" in vision_info: + image_inputs.append(fetch_image(vision_info)) + elif "video" in vision_info: + video_inputs.append(fetch_video(vision_info)) + else: + raise ValueError("image, image_url or video should in content.") + if len(image_inputs) == 0: + image_inputs = None + if len(video_inputs) == 0: + video_inputs = None + return image_inputs, video_inputs \ No newline at end of file diff --git a/examples/offline_inference/qwen_2_5_omni/run.sh b/examples/offline_inference/qwen_2_5_omni/run.sh new file mode 100644 index 000000000..2b5db45cb --- /dev/null +++ b/examples/offline_inference/qwen_2_5_omni/run.sh @@ -0,0 +1,9 @@ +export PYTHONPATH=/path/to/vllm-omni:$PYTHONPATH +export HF_ENDPOINT=https://hf-mirror.com +python end2end.py --model Qwen/Qwen2.5-Omni-7B \ + --prompts "Explain the system architecture for a scalable audio generation pipeline. Answer in 15 words." \ + --voice-type "m02" \ + --dit-ckpt none \ + --bigvgan-ckpt none \ + --output-wav output_audio \ + --prompt_type text \ No newline at end of file diff --git a/examples/offline_inference/qwen_2_5_omni/utils.py b/examples/offline_inference/qwen_2_5_omni/utils.py new file mode 100644 index 000000000..c8cf5392d --- /dev/null +++ b/examples/offline_inference/qwen_2_5_omni/utils.py @@ -0,0 +1,305 @@ +import tempfile +from urllib.request import urlopen +import librosa +import soundfile as sf +import resampy +from typing import Dict, Optional +import torch +import requests +import torchvision.io + +from typing import Union, List +from vllm.inputs import TextPrompt +from vllm_omni.inputs.data import OmniTokensPrompt +from processing_omni import fetch_image, fetch_video + + +def get_system_prompt(): + + return { + 'role': + 'system', + 'content': [{ + 'type': + 'text', + 'text': + 'You are Qwen, a virtual human developed by the Qwen Team, Alibaba Group, capable of perceiving auditory and visual inputs, as well as generating text and speech.' + }] + } + + +def resample_wav_to_16khz(input_filepath): + data, original_sample_rate = sf.read(input_filepath) + # Only use the first channel + if len(data.shape) > 1: + data = data[:, 0] + # resample to 16kHz + data_resampled = resampy.resample(data, + sr_orig=original_sample_rate, + sr_new=16000) + return data_resampled + + +def fetch_and_read_video(args, video_url: str, fps=2): + + def read_video_with_torchvision(video_file_name: str): + video, audio, info = torchvision.io.read_video( + video_file_name, + start_pts=0.0, + end_pts=None, + pts_unit="sec", + output_format="TCHW", + ) + + total_frames, video_fps = video.size(0), info["video_fps"] + total_duration = round(total_frames / video_fps, 3) + nframes = int(total_frames / video_fps * fps) + + frame_timestamps = total_duration * torch.arange(1, + nframes + 1) / nframes + grid_timestamps = frame_timestamps[::2] + second_per_grid = grid_timestamps[1] - grid_timestamps[0] + + idx = torch.linspace(0, video.size(0) - 1, nframes).round().long() + video = video[idx] + + if args.legacy_omni_video: + return [video, total_duration, nframes, second_per_grid.item()] + else: + return video + + def read_video_with_transformers(video_file_name: Union[str, List[str]]): + video, total_duration, nframes, second_per_grid = fetch_video( + {'video': video_file_name}) + if total_duration is None and nframes is None: + nframes = len(video) + total_duration = 0.5 * nframes + second_per_grid = 1.0 + if args.legacy_omni_video: + return [video, total_duration, nframes, second_per_grid] + else: + return video + + def read_video(video_file_name: str): + if args.use_torchvision: + return read_video_with_torchvision(video_file_name) + else: + return read_video_with_transformers(video_file_name) + + if isinstance(video_url, str) and video_url.startswith("http"): + with tempfile.NamedTemporaryFile(delete=True) as temp_video_file: + resp = requests.get(video_url) + assert resp.status_code == requests.codes.ok, f"Failed to fetch video from {video_url}, status_code:{resp.status_code}, resp:{resp}" + + temp_video_file.write(urlopen(video_url).read()) + temp_video_file_path = temp_video_file.name + video_file_name = temp_video_file_path + return read_video(video_file_name) + else: + video_file_name = video_url + return read_video(video_file_name) + + +def make_inputs_qwen2_omni( + args, + messages: List[Dict[str, Union[str, List[Dict[str, str]]]]], + use_audio_in_video: Optional[bool] = False, + tokenize: bool = False, +) -> Union[OmniTokensPrompt, TextPrompt]: + + from transformers import AutoConfig, AutoProcessor, AutoTokenizer + processor = AutoProcessor.from_pretrained(args.model) + tokenizer = AutoTokenizer.from_pretrained(args.model) + + try: + config = AutoConfig.from_pretrained(args.model) + if 'Qwen2_5OmniModel' in config.architectures: + args.legacy_omni_video = False + else: + args.legacy_omni_video = True + except: + args.legacy_omni_video = True + + audios, images, videos = [], [], [] + for message in messages: + if not isinstance(message['content'], list): + message['content'] = [{ + 'type': 'text', + 'text': message['content'], + }] + index, num_contents = 0, len(message['content']) + while index < num_contents: + ele = message['content'][index] + if 'type' not in ele: + if 'text' in ele: + ele['type'] = 'text' + elif 'audio' in ele: + ele['type'] = 'audio' + elif 'audio_url' in ele: + ele['type'] = 'audio_url' + elif 'image' in ele: + ele['type'] = 'image' + elif 'image_url' in ele: + ele['type'] = 'image_url' + elif 'video' in ele: + ele['type'] = 'video' + elif 'video_url' in ele: + ele['type'] = 'video_url' + else: + raise ValueError(f'Unknown ele: {ele}') + + if ele['type'] == 'audio' or ele['type'] == 'audio_url': + if 'audio_url' in ele: + audio_key = 'audio_url' + with tempfile.NamedTemporaryFile( + delete=True) as temp_audio_file: + temp_audio_file.write(urlopen(ele[audio_key]).read()) + temp_audio_file_path = temp_audio_file.name + audios.append( + resample_wav_to_16khz(temp_audio_file_path)) + ele['audio'] = temp_audio_file_path + elif 'audio' in ele: + audio_key = 'audio' + audios.append(resample_wav_to_16khz(ele[audio_key])) + else: + raise ValueError(f'Unknown ele {ele}') + elif use_audio_in_video and (ele['type'] == 'video' + or ele['type'] == 'video_url'): + # use video as audio as well + if 'video_url' in ele: + audio_key = 'video_url' + with tempfile.NamedTemporaryFile( + delete=True) as temp_video_file: + temp_video_file.write(urlopen(ele[audio_key]).read()) + temp_video_file_path = temp_video_file.name + ele[audio_key] = temp_video_file_path + audios.append( + librosa.load(temp_video_file_path, sr=16000)[0]) + videos.append( + fetch_and_read_video(args, temp_video_file_path)) + ele['video'] = temp_video_file_path + elif 'video' in ele: + audio_key = 'video' + audios.append(librosa.load(ele[audio_key], sr=16000)[0]) + videos.append(fetch_and_read_video(args, audio_key)) + else: + raise ValueError("Unknown ele {}".format(ele)) + # insert a audio after the video + message['content'].insert(index + 1, { + "type": "audio", + "audio": ele[audio_key], + }) + # no need to load the added audio again + index += 1 + elif ele['type'] == 'video' or ele['type'] == 'video_url': + if 'video_url' in ele: + video_key = 'video_url' + with tempfile.NamedTemporaryFile( + delete=True) as temp_video_file: + temp_video_file.write(urlopen(ele['video_url']).read()) + temp_video_file_path = temp_video_file.name + videos.append(fetch_and_read_video(args, temp_video_file)) + ele['video'] = temp_video_file_path + else: + video_key = 'video' + videos.append(fetch_and_read_video(args, ele[video_key])) + elif ele['type'] == 'image' or ele['type'] == 'image_url': + images.append(fetch_image(ele)) + + # move to the next content + index += 1 + + prompt = processor.apply_chat_template( + messages, + tokenize=tokenize, + add_generation_prompt=True, + add_vision_id=True, + ) + + audios = audios if len(audios) > 0 else None + images = images if len(images) > 0 else None + videos = videos if len(videos) > 0 else None + + multi_modal_data = {} + if audios: + multi_modal_data["audio"] = audios + if images: + multi_modal_data["image"] = images + if videos: + multi_modal_data["video"] = videos + + if isinstance(prompt, list) and isinstance(prompt[0], (list, str)): + prompt = prompt[0] + + if tokenize: + return OmniTokensPrompt( + prompt_token_ids=prompt, + multi_modal_data=multi_modal_data, + ) + else: + return TextPrompt( + prompt=prompt, + multi_modal_data=multi_modal_data, + ) + + +def make_text_prompt(args, prompt): + messages = [ + get_system_prompt(), + { + "role": "user", + "content": [ + { + "type": "text", + "text": prompt + }, + ] + }, + ] + + prompt = make_inputs_qwen2_omni(args, messages, tokenize=args.tokenize) + return prompt + + +def make_audio_in_video_v2_prompt(args): + messages = [ + { + 'role': + 'system', + 'content': [{ + 'type': + 'text', + 'text': + 'You are Qwen, a virtual human developed by the Qwen Team, Alibaba Group, capable of perceiving auditory and visual inputs, as well as generating text and speech.' + }] + }, + { + "role": + "user", + "content": [ + { + "type": + "video_url", + "video_url": + "https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2.5-Omni/draw_small.mp4" + }, + ] + }, + ] + prompt = make_inputs_qwen2_omni( + args, + messages, + use_audio_in_video=True, + tokenize=args.tokenize, + ) + return prompt + + +def make_omni_prompt(args, prompt = None) -> Union[OmniTokensPrompt, List[OmniTokensPrompt]]: + if args.prompt_type == 'text': + prompt = make_text_prompt(args, prompt) + elif args.prompt_type == 'audio-in-video-v2': + prompt = make_audio_in_video_v2_prompt(args) + else: + raise ValueError(f'Unsupported prompt type: {args.prompt_type}') + return prompt \ No newline at end of file From 6d04ea8450adb2c4d0865042f680fcb47a7d6a16 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 15:28:23 +0800 Subject: [PATCH 05/25] rename to gpu-xx-model-runner style --- .../worker/{AR_gpu_model_runner.py => gpu_ar_model_runner.py} | 0 vllm_omni/worker/{AR_gpu_worker.py => gpu_ar_worker.py} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename vllm_omni/worker/{AR_gpu_model_runner.py => gpu_ar_model_runner.py} (100%) rename vllm_omni/worker/{AR_gpu_worker.py => gpu_ar_worker.py} (100%) diff --git a/vllm_omni/worker/AR_gpu_model_runner.py b/vllm_omni/worker/gpu_ar_model_runner.py similarity index 100% rename from vllm_omni/worker/AR_gpu_model_runner.py rename to vllm_omni/worker/gpu_ar_model_runner.py diff --git a/vllm_omni/worker/AR_gpu_worker.py b/vllm_omni/worker/gpu_ar_worker.py similarity index 100% rename from vllm_omni/worker/AR_gpu_worker.py rename to vllm_omni/worker/gpu_ar_worker.py From 811057ce111a13afbc35d6fdaf6ece253dd546ac Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 15:30:46 +0800 Subject: [PATCH 06/25] Refactor GPU diffusion model runner and worker - Introduced `DiffusionModelRunner` class to handle diffusion processes, extending `OmniGPUModelRunner`. - Updated `DiffusionGPUWorker` to initialize device settings and manage memory more effectively. - Enhanced model execution with support for multimodal inputs and improved tensor management. - Removed legacy code and streamlined the generation process for better performance. --- .../worker/gpu_diffusion_model_runner.py | 463 +++++++++++++----- vllm_omni/worker/gpu_diffusion_worker.py | 111 +++-- 2 files changed, 414 insertions(+), 160 deletions(-) diff --git a/vllm_omni/worker/gpu_diffusion_model_runner.py b/vllm_omni/worker/gpu_diffusion_model_runner.py index e46492294..b9d98515b 100644 --- a/vllm_omni/worker/gpu_diffusion_model_runner.py +++ b/vllm_omni/worker/gpu_diffusion_model_runner.py @@ -1,133 +1,376 @@ -"""Model runner that wraps a diffusers text-to-image pipeline.""" +# SPDX-License-Identifier: Apache-2.0 from __future__ import annotations -from dataclasses import dataclass -from typing import Any, Optional +from typing import Optional, Union, List +import numpy as np +import gc +import logging -@dataclass -class DiffusionRunnerOutput: - """Structured output returned by the diffusion runner.""" +import torch - prompt: str - images: list - request_id: str = "diffusion_request" - finished: bool = True - output_type: str = "image" +from vllm.v1.worker.gpu_model_runner import ( + GPUModelRunner, + EMPTY_MODEL_RUNNER_OUTPUT, + IntermediateTensors, + get_pp_group, + has_kv_transfer_group, + set_forward_context, +) +from vllm.v1.worker.utils import sanity_check_mm_encoder_outputs +from vllm.multimodal.inputs import MultiModalKwargs + +from vllm.v1.core.sched.output import SchedulerOutput +from vllm.v1.attention.backends.utils import CommonAttentionMetadata + +from vllm_omni.outputs import OmniModelRunnerOutput +from vllm_omni.worker.gpu_model_runner import OmniGPUModelRunner -class DiffusionModelRunner: - """Lightweight runner that loads and executes diffusers pipelines.""" +logger = logging.getLogger(__name__) - def __init__( + +class DiffusionModelRunner(OmniGPUModelRunner): + """Diffusion model runner for vLLM-omni (non-autoregressive). + + - Reuses GPUModelRunner preparation, multimodal handling, and TP/PP/DP glue. + - Does not compute logits or perform token sampling. + - Executes diffusion process and returns tensors via `pooler_output`. + """ + + @torch.inference_mode() + def execute_model( self, - model_path: str, - *, - pipeline_name: Optional[str] = None, - device: Optional[str] = None, - dtype: Optional[str] = None, - ) -> None: - self.model_path = model_path - self.pipeline_name = pipeline_name or "auto" - self.device, self.torch_dtype = self._resolve_device_and_dtype(device, dtype) - self._pipeline = self._load_pipeline() + scheduler_output: "SchedulerOutput", + intermediate_tensors: Optional[IntermediateTensors] = None, + ) -> Union[OmniModelRunnerOutput, IntermediateTensors]: + self._update_states(scheduler_output) - def _resolve_device_and_dtype( - self, - device: Optional[str], - dtype: Optional[str], - ): - import torch + if not scheduler_output.total_num_scheduled_tokens: + if not has_kv_transfer_group(): + return EMPTY_MODEL_RUNNER_OUTPUT + return self.kv_connector_no_forward(scheduler_output, + self.vllm_config) - resolved_device = device - if resolved_device is None: - if hasattr(torch.backends, "mps") and torch.backends.mps.is_available(): - resolved_device = "mps" - elif torch.cuda.is_available(): - resolved_device = "cuda" - else: - resolved_device = "cpu" + # Prepare decoder inputs and attention metadata (for batch/order mapping) + (attn_metadata, attention_cuda_graphs, logits_indices, + spec_decode_metadata, num_scheduled_tokens_np, + spec_decode_common_attn_metadata) = self._prepare_inputs( + scheduler_output) - if dtype is not None: - resolved_dtype = getattr(torch, dtype, None) + # Input token count for this iteration (not used by diffusion, but + # retained to keep DP padding/ordering consistent) + num_input_tokens = scheduler_output.total_num_scheduled_tokens + num_pad, num_tokens_across_dp = self.get_dp_padding(num_input_tokens) + num_input_tokens += num_pad + + # Multimodal conditioning (e.g., text/audio/video encoders) + if self.is_multimodal_model: + self._execute_mm_encoder(scheduler_output) + mm_embeds = self._gather_mm_embeddings(scheduler_output) else: - if resolved_device in {"cuda", "mps"}: - resolved_dtype = torch.float16 - else: - resolved_dtype = torch.float32 + mm_embeds = [] - return resolved_device, resolved_dtype - - def _load_pipeline(self): - import diffusers - - if self.pipeline_name != "auto": - PipelineCls = getattr(diffusers, self.pipeline_name) + # Build inputs to mirror AR runner: input_ids/positions/embeds + if self.is_multimodal_model and get_pp_group().is_first_rank: + inputs_embeds_scheduled = self.model.get_input_embeddings( + input_ids=self.input_ids[:scheduler_output.total_num_scheduled_tokens], + multimodal_embeddings=mm_embeds or None, + ) + self.inputs_embeds[:scheduler_output.total_num_scheduled_tokens].copy_( + inputs_embeds_scheduled) + input_ids = self.input_ids[:num_input_tokens] + inputs_embeds = self.inputs_embeds[:num_input_tokens] + model_mm_kwargs = self._extract_mm_kwargs(scheduler_output) else: - PipelineCls = diffusers.AutoPipelineForText2Image + input_ids = self.input_ids[:num_input_tokens] + inputs_embeds = None + model_mm_kwargs = {} - pipeline = PipelineCls.from_pretrained( - self.model_path, - torch_dtype=self.torch_dtype, + # Positions (mrope or standard) + if self.uses_mrope: + positions = self.mrope_positions[:, :num_input_tokens] + else: + positions = self.positions[:num_input_tokens] + + # Intermediate tensors sync for PP (if any) + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_input_tokens, intermediate_tensors, True) + + # Set forward context mainly for resource management and kv connector + skip_cuda_graphs = True # diffusion path does not rely on cuda graphs here + with set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_input_tokens, + num_tokens_across_dp=num_tokens_across_dp, + skip_cuda_graphs=skip_cuda_graphs, + ), self.maybe_get_kv_connector_output( + scheduler_output) as kv_connector_output: + + if not get_pp_group().is_last_rank: + # For non-last PP stages, pass through intermediate tensors. + assert intermediate_tensors is not None + intermediate_tensors.kv_connector_output = kv_connector_output + return intermediate_tensors + + outputs = self._run_diffusion( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + multimodal_kwargs=model_mm_kwargs, + logits_indices=logits_indices, + ) + _, multimodal_outputs = ( + self.extract_multimodal_outputs(outputs)) + + # Ensure one tensor per request, map to CPU for output struct + pooler_output: List[Optional[torch.Tensor]] = [] + if isinstance(multimodal_outputs, torch.Tensor): + # If model returned a single stacked tensor, split by requests + assert outputs.shape[0] == self.input_batch.num_reqs + for i in range(self.input_batch.num_reqs): + pooler_output.append(outputs[i].detach().to("cpu").contiguous()) + elif isinstance(multimodal_outputs, list): + for out in outputs: + pooler_output.append(out.detach().to("cpu").contiguous() if out is not None else None) + elif isinstance(multimodal_outputs, dict): + for out in multimodal_outputs.values(): + pooler_output.append(out.detach().to("cpu").contiguous() if out is not None else None) + else: + raise RuntimeError("Unsupported diffusion output type") + + self.eplb_step() + + return OmniModelRunnerOutput( + req_ids=self.input_batch.req_ids, + req_id_to_index=self.input_batch.req_id_to_index, + sampled_token_ids=[], + spec_token_ids=None, + logprobs=None, + prompt_logprobs_dict={}, + pooler_output=pooler_output, + kv_connector_output=kv_connector_output, + num_nans_in_logits={}, ) - try: - pipeline = pipeline.to(self.device) - except Exception: - pipeline = pipeline.to("cpu") - - self._apply_optimisations(pipeline) - return pipeline - - @staticmethod - def _apply_optimisations(pipeline) -> None: - # Enable attention slicing / VAE tiling where available to reduce memory. - try: - if hasattr(pipeline, "enable_attention_slicing"): - pipeline.enable_attention_slicing() - except Exception: - pass - try: - vae = getattr(pipeline, "vae", None) - if vae and hasattr(vae, "enable_tiling"): - vae.enable_tiling() - except Exception: - pass - - def generate( + def _run_diffusion( self, - prompt: str, *, - height: int = 512, - width: int = 512, - num_inference_steps: int = 30, - guidance_scale: float = 5.0, - negative_prompt: Optional[str] = None, - seed: Optional[int] = None, - image: Optional[Any] = None, - ) -> DiffusionRunnerOutput: - import torch + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors], + inputs_embeds: Optional[torch.Tensor], + multimodal_kwargs: dict, + logits_indices: torch.Tensor, + ) -> Union[torch.Tensor, list[torch.Tensor]]: + """Runs the diffusion process and returns per-request tensors. - generator = None - if seed is not None: - try: - generator = torch.Generator(device=self.device).manual_seed(int(seed)) - except Exception: - # Some backends (e.g., CPU without full support) may not accept the device. - generator = torch.Generator().manual_seed(int(seed)) - - output = self._pipeline( - prompt=prompt, - negative_prompt=negative_prompt, - num_inference_steps=int(num_inference_steps), - guidance_scale=float(guidance_scale), - height=int(height), - width=int(width), - generator=generator, - image=image, + Tries model interfaces in the following order for maximal compatibility: + 1) model.sample(condition=..., **kwargs) + 2) model.forward(condition=..., **kwargs) + 3) model.diffuse(condition=..., **kwargs) + """ + # Keep inputs identical to AR runner + kwargs = dict( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs(multimodal_kwargs, device=self.device), + sampling_metadata=self.input_batch.sampling_metadata, + logits_index=logits_indices, + sampler=self.sampler, ) - return DiffusionRunnerOutput( - prompt=prompt, - images=getattr(output, "images", []) or [], - ) + # For Qwen 2.5 Omni's current implementation, we only support the forward method + if hasattr(self.model, "forward"): + return self.model.forward(**kwargs) + + # if hasattr(self.model, "sample"): + # return self.model.sample(**kwargs) + # if hasattr(self.model, "forward"): + # return self.model.forward(**kwargs) + # if hasattr(self.model, "diffuse"): + # return self.model.diffuse(**kwargs) + + raise RuntimeError( + "The loaded model does not expose diffusion interfaces 'sample', " + "'forward', or 'diffuse'. Please implement one of them or adapt the runner.") + + + + + @torch.inference_mode() + def _dummy_run( + self, + num_tokens: int, + capture_attn_cudagraph: bool = False, + skip_eplb: bool = False, + is_profile: bool = False, + ) -> tuple[torch.Tensor, torch.Tensor]: + # Padding for DP + num_pad, num_tokens_across_dp = self.get_dp_padding(num_tokens) + num_tokens += num_pad + + # Set num_scheduled_tokens based on num_tokens and max_num_seqs + # for dummy run with LoRA so that the num_reqs collectively + # has num_tokens in total. + assert num_tokens <= self.scheduler_config.max_num_batched_tokens + max_num_reqs = self.scheduler_config.max_num_seqs + num_reqs = min(num_tokens, max_num_reqs) + min_tokens_per_req = num_tokens // num_reqs + num_scheduled_tokens_list = [min_tokens_per_req] * num_reqs + num_scheduled_tokens_list[-1] += num_tokens % num_reqs + assert sum(num_scheduled_tokens_list) == num_tokens + assert len(num_scheduled_tokens_list) == num_reqs + num_scheduled_tokens = np.array(num_scheduled_tokens_list, + dtype=np.int32) + + attn_metadata: Optional[dict[str, dict]] = None + if capture_attn_cudagraph: + attn_metadata = {} + + # Make sure max_model_len is used at the graph capture time. + self.seq_lens_np[:num_reqs] = self.max_model_len + self.seq_lens_np[num_reqs:] = 0 + self.seq_lens[:num_reqs].copy_(self.seq_lens_cpu[:num_reqs], + non_blocking=True) + + for kv_cache_group_id, kv_cache_group_spec in enumerate( + self.kv_cache_config.kv_cache_groups): + common_attn_metadata = CommonAttentionMetadata( + query_start_loc=self.query_start_loc[:num_reqs + 1], + query_start_loc_cpu=self.query_start_loc_cpu[:num_reqs + 1], + seq_lens=self.seq_lens[:num_reqs], + seq_lens_cpu=self.seq_lens_cpu[:num_reqs], + num_computed_tokens_cpu=self.input_batch. + num_computed_tokens_cpu_tensor[:num_reqs], + num_reqs=num_reqs, + num_actual_tokens=num_tokens, + max_query_len=num_tokens, + block_table_tensor=self.input_batch.block_table[ + kv_cache_group_id].get_device_tensor()[:num_reqs], + slot_mapping=self.input_batch.block_table[ + kv_cache_group_id].slot_mapping[:num_tokens], + causal=True) + + for attn_group in self.attn_groups[kv_cache_group_id]: + attn_metadata_i = attn_group.metadata_builder \ + .build_for_cudagraph_capture(common_attn_metadata) + for layer_name in kv_cache_group_spec.layer_names: + attn_metadata[layer_name] = attn_metadata_i + + with self.maybe_dummy_run_with_lora(self.lora_config, + num_scheduled_tokens): + if self.is_multimodal_model: + input_ids = None + inputs_embeds = self.inputs_embeds[:num_tokens] + model_mm_kwargs = self._dummy_mm_kwargs(num_reqs) + else: + input_ids = self.input_ids[:num_tokens] + inputs_embeds = None + model_mm_kwargs = {} + + if self.uses_mrope: + positions = self.mrope_positions[:, :num_tokens] + else: + positions = self.positions[:num_tokens] + + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + if self.intermediate_tensors is None: + self.intermediate_tensors = ( + self.model.make_empty_intermediate_tensors( + batch_size=self.max_num_tokens, + dtype=self.model_config.dtype, + device=self.device)) + + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_tokens, None, False) + + # Diffusion path: avoid CUDA graphs; we only use context for resource wiring + with self.maybe_randomize_inputs(input_ids), set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_tokens, + num_tokens_across_dp=num_tokens_across_dp): + outputs = self.model( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs( + model_mm_kwargs, + device=self.device, + ), + sampler=None, + ) + + if self.use_aux_hidden_state_outputs: + hidden_states, _ = outputs + else: + hidden_states = outputs + + # Extract multimodal outputs if present; we ignore them here because + # dummy run returns tensors only. The actual diffusion runner returns + # multimodal outputs via pooler_output in execute_model. + text_hidden_states, _ = self.extract_multimodal_outputs(hidden_states) + + if not skip_eplb: + self.eplb_step(is_dummy=True, is_profile=is_profile) + + logit_indices = np.cumsum(num_scheduled_tokens) - 1 + return text_hidden_states, None + + @torch.inference_mode() + def _dummy_sampler_run(self, hidden_states: torch.Tensor) -> None: + logger.warning("Dummy sampler run is not implemented for diffusion model") + return None + + def profile_run(self) -> None: + # Profile with multimodal encoder & encoder cache, similar to base but + # without any logits/sampler warming. + if self.is_multimodal_model: + mm_budget = self.mm_budget + assert mm_budget is not None + + # TODO: handle encoder-decoder models once supported. + if (encoder_budget := mm_budget.get_encoder_budget()) > 0: + ( + dummy_modality, + max_tokens, + ) = mm_budget.get_modality_with_max_tokens() + ( + max_mm_items_per_prompt, + max_mm_items_per_batch, + ) = mm_budget.get_max_items(dummy_modality, max_tokens) + + batched_dummy_mm_inputs = self._get_mm_dummy_batch( + dummy_modality, + max_mm_items_per_batch, + ) + + dummy_encoder_outputs = self.model.get_multimodal_embeddings( + **batched_dummy_mm_inputs) + + sanity_check_mm_encoder_outputs( + dummy_encoder_outputs, + expected_num_items=max_mm_items_per_batch, + ) + + self.encoder_cache["tmp"] = dict( + enumerate(dummy_encoder_outputs)) + + hidden_states, _ = self._dummy_run(self.max_num_tokens, is_profile=True) + if get_pp_group().is_last_rank: + pass # No sampler/pooler warmup for diffusion + self._sync_device() + del hidden_states + self.encoder_cache.clear() + gc.collect() diff --git a/vllm_omni/worker/gpu_diffusion_worker.py b/vllm_omni/worker/gpu_diffusion_worker.py index 74f2474c4..a9767f013 100644 --- a/vllm_omni/worker/gpu_diffusion_worker.py +++ b/vllm_omni/worker/gpu_diffusion_worker.py @@ -1,56 +1,67 @@ -"""Worker abstraction for executing diffusion model runners.""" +from vllm.v1.worker.gpu_worker import Worker as GPUWorker +import torch +import os +import gc +from vllm.utils import GiB_bytes, MemorySnapshot +from vllm.platforms import current_platform +from vllm.worker.worker import _check_if_gpu_supports_dtype +from vllm.v1.worker.gpu_worker import init_worker_distributed_environment +from vllm.model_executor import set_random_seed +from vllm.v1.utils import report_usage_stats -from __future__ import annotations - -from typing import Any, Optional - -from vllm.v1.worker.worker_base import WorkerBase - -from .gpu_diffusion_model_runner import DiffusionModelRunner, DiffusionRunnerOutput +from vllm_omni.worker.diffusion_model_runner import DiffusionModelRunner -class DiffusionGPUWorker(WorkerBase): - """Minimal worker wrapping DiffusionModelRunner. +class DiffusionGPUWorker(GPUWorker): + def init_device(self): + if self.device_config.device.type == "cuda": + # torch.distributed.all_reduce does not free the input tensor until + # the synchronization point. This causes the memory usage to grow + # as the number of all_reduce calls increases. This env var disables + # this behavior. + # Related issue: + # https://discuss.pytorch.org/t/cuda-allocation-lifetime-for-inputs-to-distributed-all-reduce/191573 + os.environ["TORCH_NCCL_AVOID_RECORD_STREAMS"] = "1" - The worker interface mirrors the shape expected by vLLM executors: it owns - a model runner instance and exposes a `generate` method that can be called - by an executor implementation. - """ + # This env var set by Ray causes exceptions with graph building. + os.environ.pop("NCCL_ASYNC_ERROR_HANDLING", None) + self.device = torch.device(f"cuda:{self.local_rank}") + current_platform.set_device(self.device) - def __init__( - self, - model_path: str, - *, - pipeline_name: Optional[str] = None, - device: Optional[str] = None, - dtype: Optional[str] = None, - ) -> None: - self.model_runner = DiffusionModelRunner( - model_path, - pipeline_name=pipeline_name, - device=device, - dtype=dtype, - ) + _check_if_gpu_supports_dtype(self.model_config.dtype) + gc.collect() + torch.cuda.empty_cache() - def generate( - self, - prompt: str, - *, - height: int = 512, - width: int = 512, - num_inference_steps: int = 30, - guidance_scale: float = 5.0, - negative_prompt: Optional[str] = None, - seed: Optional[int] = None, - image: Optional[Any] = None, - ) -> DiffusionRunnerOutput: - return self.model_runner.generate( - prompt=prompt, - height=height, - width=width, - num_inference_steps=num_inference_steps, - guidance_scale=guidance_scale, - negative_prompt=negative_prompt, - seed=seed, - image=image, - ) + # take current memory snapshot + self.init_snapshot = MemorySnapshot() + self.requested_memory = (self.init_snapshot.total_memory * + self.cache_config.gpu_memory_utilization) + if self.init_snapshot.free_memory < self.requested_memory: + GiB = lambda b: round(b / GiB_bytes, 2) + raise ValueError( + f"Free memory on device " + f"({GiB(self.init_snapshot.free_memory)}/" + f"{GiB(self.init_snapshot.total_memory)} GiB) on startup " + f"is less than desired GPU memory utilization " + f"({self.cache_config.gpu_memory_utilization}, " + f"{GiB(self.requested_memory)} GiB). Decrease GPU memory " + f"utilization or reduce GPU memory used by other processes." + ) + else: + raise RuntimeError( + f"Not support device type: {self.device_config.device}") + # Initialize the distributed environment. + init_worker_distributed_environment(self.vllm_config, self.rank, + self.distributed_init_method, + self.local_rank, + current_platform.dist_backend) + # Set random seed. + set_random_seed(self.model_config.seed) + + # Construct the model runner + self.model_runner: DiffusionModelRunner = DiffusionModelRunner( + self.vllm_config, self.device) + + if self.rank == 0: + # If usage stat is enabled, collect relevant info. + report_usage_stats(self.vllm_config) \ No newline at end of file From 54977bd590704fd99b54b1773eb99dc8c2fb4a34 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 15:33:11 +0800 Subject: [PATCH 07/25] Add OmniGPUModelRunner and OmniModelInputForGPU classes - Introduced OmniGPUModelRunner for enhanced GPU model execution with state management. - Added OmniModelInputForGPU and its builder to support additional information in model inputs. - Implemented methods for updating request states and handling multimodal inputs. - Enhanced input preparation for CUDA graph execution and LoRA integration. --- vllm_omni/worker/gpu_model_runner.py | 758 +++++++++++++++++++++++++++ vllm_omni/worker/model_runner.py | 199 +++++++ 2 files changed, 957 insertions(+) create mode 100644 vllm_omni/worker/gpu_model_runner.py create mode 100644 vllm_omni/worker/model_runner.py diff --git a/vllm_omni/worker/gpu_model_runner.py b/vllm_omni/worker/gpu_model_runner.py new file mode 100644 index 000000000..acbda8648 --- /dev/null +++ b/vllm_omni/worker/gpu_model_runner.py @@ -0,0 +1,758 @@ + +from typing import TYPE_CHECKING, Any, Optional, Union, cast + +import numpy as np +import torch + +import vllm.envs as envs +from vllm.distributed.parallel_state import get_pp_group, get_tp_group +from vllm.forward_context import set_forward_context +from vllm.logger import init_logger +from vllm.model_executor.models.interfaces_base import VllmModelForPooling +from vllm.multimodal.inputs import MultiModalKwargs +from vllm.sequence import IntermediateTensors +from vllm.utils import LazyLoader +from vllm.v1.attention.backends.utils import CommonAttentionMetadata +from vllm.v1.outputs import EMPTY_MODEL_RUNNER_OUTPUT +from vllm.v1.worker.gpu_input_batch import CachedRequestState +from vllm.sampling_params import SamplingType +from vllm.distributed.kv_transfer import has_kv_transfer_group +from vllm.utils import round_up +from vllm.v1.spec_decode.eagle import EagleProposer + +from vllm.v1.worker.gpu_model_runner import GPUModelRunner + +from vllm_omni.outputs import OmniModelRunnerOutput +from vllm_omni.model_executor.layers.mrope import MRotaryEmbedding + +if TYPE_CHECKING: + from vllm.v1.core.sched.output import SchedulerOutput +else: + xgr = LazyLoader("xgr", globals(), "xgrammar") + xgr_torch_compile = LazyLoader( + "xgr_torch_compile", globals(), + "xgrammar.kernels.apply_token_bitmask_inplace_torch_compile") + +logger = init_logger(__name__) + + +class OmniGPUModelRunner(GPUModelRunner): + def _update_states(self, scheduler_output: "SchedulerOutput") -> None: + """Update the cached states and the persistent batch with the scheduler + output. + + The updated states are used by the `_prepare_inputs` function to create + the input GPU tensors for the model. + + The SamplingMetadata is updated and copied to the GPU if there is a + new/resumed/paused/finished request in the batch. + """ + # Remove finished requests from the cached states. + for req_id in scheduler_output.finished_req_ids: + self.requests.pop(req_id, None) + self.encoder_cache.pop(req_id, None) + # Remove the finished requests from the persistent batch. + # NOTE(woosuk): There could be an edge case where finished_req_ids and + # scheduled_req_ids overlap. This happens when a request is aborted and + # then resubmitted with the same ID. In this case, we treat them as two + # distinct requests - clearing the cached states for the first request + # and handling the second as a new request. + for req_id in scheduler_output.finished_req_ids: + self.input_batch.remove_request(req_id) + + # Free the cached encoder outputs. + for req_id, input_id in scheduler_output.free_encoder_input_ids: + encoder_outputs = self.encoder_cache.get(req_id) + if encoder_outputs is not None: + encoder_outputs.pop(input_id, None) + if not encoder_outputs: + self.encoder_cache.pop(req_id, None) + + # Remove the unscheduled requests from the persistent batch. + # NOTE(woosuk): The unscheduled requests are either preempted requests + # or running requests that are not scheduled in this step. We remove + # them from the persistent batch but keep their cached states since + # they will be scheduled again sometime in the future. + scheduled_req_ids = scheduler_output.num_scheduled_tokens.keys() + cached_req_ids = self.input_batch.req_id_to_index.keys() + unscheduled_req_ids = cached_req_ids - scheduled_req_ids + # NOTE(woosuk): The persistent batch optimization assumes that + # consecutive batches contain mostly the same requests. If batches + # have low request overlap (e.g., alternating between two distinct + # sets of requests), this optimization becomes very inefficient. + for req_id in unscheduled_req_ids: + self.input_batch.remove_request(req_id) + + req_ids_to_add: list[str] = [] + # Add new requests to the cached states. + for new_req_data in scheduler_output.scheduled_new_reqs: + req_id = new_req_data.req_id + sampling_params = new_req_data.sampling_params + pooling_params = new_req_data.pooling_params + + if sampling_params and \ + sampling_params.sampling_type == SamplingType.RANDOM_SEED: + generator = torch.Generator(device=self.device) + generator.manual_seed(sampling_params.seed) + else: + generator = None + + if pooling_params: + assert (task := pooling_params.task) is not None, ( + "You did not set `task` in the API") + + model = cast(VllmModelForPooling, self.model) + to_update = model.pooler.get_pooling_updates(task) + to_update.apply(pooling_params) + + self.requests[req_id] = CachedRequestState( + req_id=req_id, + prompt_token_ids=new_req_data.prompt_token_ids, + mm_inputs=new_req_data.mm_inputs, + mm_positions=new_req_data.mm_positions, + sampling_params=sampling_params, + pooling_params=pooling_params, + generator=generator, + block_ids=new_req_data.block_ids, + num_computed_tokens=new_req_data.num_computed_tokens, + output_token_ids=[], + lora_request=new_req_data.lora_request, + ) + + # If prompt embeddings are provided, decode and attach to inter_data + try: + if getattr(new_req_data, "prompt_embeds", None) is not None: + payload = new_req_data.prompt_embeds + import numpy as np + dtype = getattr(np, payload.dtype) + arr = np.frombuffer(payload.data, dtype=dtype) + arr = arr.reshape(payload.shape) + pe_cpu = torch.from_numpy(arr) + # Store temporarily on CPU; later moved to device in builder + setattr(self.requests[req_id], "prompt_embeds_cpu", pe_cpu) + # Also replace payload with Tensor for user visibility in scheduler_output + try: + new_req_data.prompt_embeds = pe_cpu # type: ignore[assignment] + except Exception: + pass + except Exception: + pass + # Decode additional_information payloads (dictionary) + try: + if getattr(new_req_data, "additional_information", None) is not None: + payload_info = new_req_data.additional_information + info_dict = {} + if isinstance(payload_info, dict): + info_dict = payload_info + else: + from vllm.v1.engine import AdditionalInformationPayload + if isinstance(payload_info, AdditionalInformationPayload): + import numpy as np + for k, entry in payload_info.entries.items(): + if entry.tensor_data is not None: + dt = np.dtype(getattr(entry, "tensor_dtype", "float32")) + arr = np.frombuffer(entry.tensor_data, dtype=dt) + arr = arr.reshape(entry.tensor_shape) + info_dict[k] = torch.from_numpy(arr) + else: + info_dict[k] = entry.list_data + if info_dict: + setattr(self.requests[req_id], "additional_information_cpu", info_dict) + except Exception: + pass + + # Only relevant for models using M-RoPE (e.g, Qwen2-VL) + if self.uses_mrope: + image_grid_thw = [] + video_grid_thw = [] + second_per_grid_ts = [] + audio_feature_lengths = [] + use_audio_in_video = False + for mm_input in self.requests[req_id].mm_inputs: + if mm_input.get("image_grid_thw") is not None: + image_grid_thw.extend( + mm_input["image_grid_thw"].tolist()) + if mm_input.get("video_grid_thw") is not None: + video_grid_thw.extend( + mm_input["video_grid_thw"].tolist()) + if mm_input.get("second_per_grid_ts") is not None: + second_per_grid_ts.extend( + mm_input["second_per_grid_ts"]) + if mm_input.get("audio_feature_lengths") is not None: + audio_feature_lengths.extend( + mm_input["audio_feature_lengths"]) + if mm_input.get("use_audio_in_video") is True: + use_audio_in_video = True + + hf_config = self.model_config.hf_config + + self.requests[req_id].mrope_positions, \ + self.requests[req_id].mrope_position_delta = \ + MRotaryEmbedding.get_input_positions_tensor( + self.requests[req_id].prompt_token_ids, + hf_config=hf_config, + image_grid_thw=image_grid_thw, + video_grid_thw=video_grid_thw, + second_per_grid_ts=second_per_grid_ts, + audio_feature_lengths=audio_feature_lengths, + use_audio_in_video=use_audio_in_video, + ) + + req_ids_to_add.append(req_id) + + # Update the states of the running/resumed requests. + is_last_rank = get_pp_group().is_last_rank + req_data = scheduler_output.scheduled_cached_reqs + for i, req_id in enumerate(req_data.req_ids): + req_state = self.requests[req_id] + num_computed_tokens = req_data.num_computed_tokens[i] + new_block_ids = req_data.new_block_ids[i] + resumed_from_preemption = req_data.resumed_from_preemption[i] + + # Update the cached states. + req_state.num_computed_tokens = num_computed_tokens + + if not is_last_rank: + # When using PP, the scheduler sends the sampled tokens back, + # because there's no direct communication between the first- + # stage worker and the last-stage worker. + new_token_ids = req_data.new_token_ids[i] + # Add the sampled token(s) from the previous step (if any). + # This doesn't include "unverified" tokens like spec tokens. + num_new_tokens = (num_computed_tokens + len(new_token_ids) - + req_state.num_tokens) + if num_new_tokens == 1: + # Avoid slicing list in most common case. + req_state.output_token_ids.append(new_token_ids[-1]) + elif num_new_tokens > 0: + req_state.output_token_ids.extend( + new_token_ids[-num_new_tokens:]) + + # Update the block IDs. + if not resumed_from_preemption: + # Append the new blocks to the existing block IDs. + for block_ids, new_ids in zip(req_state.block_ids, + new_block_ids): + block_ids.extend(new_ids) + else: + # The request is resumed from preemption. + # Replace the existing block IDs with the new ones. + req_state.block_ids = new_block_ids + + req_index = self.input_batch.req_id_to_index.get(req_id) + if req_index is None: + # The request is not in the persistent batch. + # The request was either preempted and resumed later, or was not + # scheduled in the previous step and needs to be added again. + req_ids_to_add.append(req_id) + continue + + # Update the persistent batch. + self.input_batch.num_computed_tokens_cpu[req_index] = ( + num_computed_tokens) + self.input_batch.block_table.append_row(new_block_ids, req_index) + + # For the last rank, we don't need to update the token_ids_cpu + # because the sampled tokens are already cached. + if not is_last_rank: + # Add new_token_ids to token_ids_cpu. + start_token_index = num_computed_tokens + end_token_index = num_computed_tokens + len(new_token_ids) + self.input_batch.token_ids_cpu[ + req_index, + start_token_index:end_token_index] = new_token_ids + self.input_batch.num_tokens_no_spec[ + req_index] = end_token_index + self.input_batch.num_tokens[req_index] = end_token_index + + # Add spec_token_ids to token_ids_cpu. + spec_token_ids = ( + scheduler_output.scheduled_spec_decode_tokens.get(req_id, ())) + if spec_token_ids: + num_spec_tokens = len(spec_token_ids) + start_index = self.input_batch.num_tokens_no_spec[req_index] + end_token_index = start_index + num_spec_tokens + self.input_batch.token_ids_cpu[ + req_index, start_index:end_token_index] = spec_token_ids + # NOTE(woosuk): `num_tokens` here may include spec tokens. + self.input_batch.num_tokens[req_index] += num_spec_tokens + + # Add the new or resumed requests to the persistent batch. + # The smaller empty indices are filled first. + for req_id in req_ids_to_add: + req_state = self.requests[req_id] + self.input_batch.add_request(req_state) + + # Condense the batched states if there are gaps left by removed requests + self.input_batch.condense() + # Allow attention backend to reorder the batch, potentially + self._may_reorder_batch(scheduler_output) + # Refresh batch metadata with any pending updates. + self.input_batch.refresh_metadata() + + @torch.inference_mode() + def execute_model( + self, + scheduler_output: "SchedulerOutput", + intermediate_tensors: Optional[IntermediateTensors] = None, + ) -> Union[OmniModelRunnerOutput, IntermediateTensors]: + self._update_states(scheduler_output) + if not scheduler_output.total_num_scheduled_tokens: + if not has_kv_transfer_group(): + # Return empty ModelRunnerOutput if there's no work to do. + return EMPTY_MODEL_RUNNER_OUTPUT + + return self.kv_connector_no_forward(scheduler_output, + self.vllm_config) + + # Prepare the decoder inputs. + (attn_metadata, attention_cuda_graphs, logits_indices, + spec_decode_metadata, num_scheduled_tokens_np, + spec_decode_common_attn_metadata) = ( + self._prepare_inputs(scheduler_output)) + + num_scheduled_tokens = scheduler_output.total_num_scheduled_tokens + if (self.use_cuda_graph + and num_scheduled_tokens <= self.cudagraph_batch_sizes[-1]): + # Use piecewise CUDA graphs. + # Add padding to the batch size. + num_input_tokens = self.vllm_config.pad_for_cudagraph( + num_scheduled_tokens) + else: + # Eager mode. + # Pad tokens to multiple of tensor_parallel_size when + # enabled collective fusion for SP + tp_size = self.vllm_config.parallel_config.tensor_parallel_size + if self.compilation_config.pass_config. \ + enable_sequence_parallelism and tp_size > 1: + num_input_tokens = round_up(num_scheduled_tokens, tp_size) + else: + num_input_tokens = num_scheduled_tokens + + # Padding for DP + num_pad, num_tokens_across_dp = self.get_dp_padding(num_input_tokens) + num_input_tokens += num_pad + + # _prepare_inputs may reorder the batch, so we must gather multi + # modal outputs after that to ensure the correct order + if self.is_multimodal_model: + # Run the multimodal encoder if any. + self._execute_mm_encoder(scheduler_output) + mm_embeds = self._gather_mm_embeddings(scheduler_output) + else: + mm_embeds = [] + + if get_pp_group().is_first_rank: + # Always prepare inputs_embeds on the first PP rank so both + # multimodal and text-only models can consume embeddings. + inputs_embeds_scheduled = self.model.get_input_embeddings( + input_ids=self.input_ids[:num_scheduled_tokens], + multimodal_embeddings=(mm_embeds or None) + if self.is_multimodal_model else None, + ) + + # TODO(woosuk): Avoid the copy. Optimize. + self.inputs_embeds[:num_scheduled_tokens].copy_( + inputs_embeds_scheduled) + + # Overlay per-request custom prompt_embeds for the prompt portion + for req_index, req_id in enumerate(self.input_batch.req_ids): + req_state = self.requests[req_id] + pe_cpu = getattr(req_state, "prompt_embeds_cpu", None) + if pe_cpu is None: + continue + num_computed_tokens = int( + self.input_batch.num_computed_tokens_cpu[req_index]) + prompt_len = len(req_state.prompt_token_ids) + prompt_remaining = max(0, prompt_len - num_computed_tokens) + sched_tokens = int(num_scheduled_tokens_np[req_index]) + overlay_len = min(sched_tokens, prompt_remaining) + if overlay_len <= 0: + continue + src = pe_cpu[num_computed_tokens: + num_computed_tokens + overlay_len].to( + dtype=self.dtype, + device=self.device, + non_blocking=True) + start_offset = int(self.query_start_loc_cpu[req_index]) + self.inputs_embeds[start_offset:start_offset + overlay_len]\ + .copy_(src) + + input_ids = self.input_ids[:num_input_tokens] # preserved for APIs + inputs_embeds = self.inputs_embeds[:num_input_tokens] + model_mm_kwargs = (self._extract_mm_kwargs(scheduler_output) + if self.is_multimodal_model else {}) + else: + # For non-first PP ranks, use token ids as usual; embeddings are + # only consumed on the first rank. + input_ids = self.input_ids[:num_input_tokens] + inputs_embeds = None + model_mm_kwargs = {} + if self.uses_mrope: + positions = self.mrope_positions[:, :num_input_tokens] + else: + positions = self.positions[:num_input_tokens] + + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_input_tokens, intermediate_tensors, True) + + # Some attention backends only support CUDA Graphs in pure decode. + # If attention doesn't support CUDA Graphs for this batch, but we + # compiled with full CUDA graphs, we have to skip them entirely. + skip_cuda_graphs = self.full_cuda_graph and not attention_cuda_graphs + + # Run the model. + # Use persistent buffers for CUDA graphs. + with set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_input_tokens, + num_tokens_across_dp=num_tokens_across_dp, + skip_cuda_graphs=skip_cuda_graphs, + ), self.maybe_get_kv_connector_output( + scheduler_output) as kv_connector_output: + + model_output = self.model( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs( + model_mm_kwargs, + device=self.device, + ), + sampling_metadata=self.input_batch.sampling_metadata, + logits_index=logits_indices, + sampler=self.sampler, + ) + + if self.use_aux_hidden_state_outputs: + hidden_states, aux_hidden_states = model_output + else: + hidden_states = model_output + aux_hidden_states = None + + text_hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states) + + # Broadcast PP output for external_launcher (torchrun) + # to make sure we are synced across pp ranks + # TODO: Support overlapping mirco-batches + # https://github.com/vllm-project/vllm/issues/18019 + broadcast_pp_output = \ + self.parallel_config.distributed_executor_backend \ + == "external_launcher" and len(get_pp_group().ranks) > 0 + if not get_pp_group().is_last_rank: + # For mid-pipeline stages, return the hidden states. + assert isinstance(text_hidden_states, IntermediateTensors) + if not broadcast_pp_output: + text_hidden_states.kv_connector_output = kv_connector_output + return text_hidden_states + get_pp_group().send_tensor_dict(text_hidden_states.tensors, + all_gather_group=get_tp_group()) + logits = None + else: + if self.input_batch.pooling_params: + return self._pool(text_hidden_states, num_scheduled_tokens, + num_scheduled_tokens_np, kv_connector_output) + + sample_hidden_states = text_hidden_states[logits_indices] + logits = self.model.compute_logits(sample_hidden_states, None) + if broadcast_pp_output: + model_output_broadcast_data = { + "logits": logits.contiguous(), + } if logits is not None else {} + model_output_broadcast_data = get_pp_group().broadcast_tensor_dict( + model_output_broadcast_data, src=len(get_pp_group().ranks) - 1) + assert model_output_broadcast_data is not None + logits = model_output_broadcast_data["logits"] + + # Apply structured output bitmasks if present + if scheduler_output.grammar_bitmask is not None: + self.apply_grammar_bitmask(scheduler_output, logits) + + # Sample the next token and get logprobs if needed. + sampling_metadata = self.input_batch.sampling_metadata + if spec_decode_metadata is None: + sampler_output = self.sampler( + logits=logits, + sampling_metadata=sampling_metadata, + ) + else: + # When indexing with a tensor (bonus_logits_indices), PyTorch + # creates a new tensor with separate storage from the original + # logits tensor. This means any in-place operations on bonus_logits + # won't affect the original logits tensor. + assert logits is not None + bonus_logits = logits[spec_decode_metadata.bonus_logits_indices] + sampler_output = self.sampler( + logits=bonus_logits, + sampling_metadata=sampling_metadata, + ) + bonus_token_ids = sampler_output.sampled_token_ids + + # Just like `bonus_logits`, `target_logits` is a new tensor with + # separate storage from the original `logits` tensor. Therefore, + # it is safe to update `target_logits` in place. + target_logits = logits[spec_decode_metadata.target_logits_indices] + output_token_ids = self.rejection_sampler( + spec_decode_metadata, + None, # draft_probs + target_logits, + bonus_token_ids, + sampling_metadata, + ) + sampler_output.sampled_token_ids = output_token_ids + + num_nans_in_logits = {} + if envs.VLLM_COMPUTE_NANS_IN_LOGITS: + num_nans_in_logits = self._get_nans_in_logits(logits) + + # TODO(woosuk): The following loop can be slow since it iterates over + # the requests one by one. Optimize. + discard_sampled_tokens_req_indices = [] + for i, req_id in enumerate(self.input_batch.req_ids): + req_state = self.requests[req_id] + seq_len = (req_state.num_computed_tokens + + scheduler_output.num_scheduled_tokens[req_id]) + if seq_len < req_state.num_tokens: + # Ignore the sampled token for partial prefills. + # Rewind the generator state as if the token was not sampled. + # This relies on cuda-specific torch-internal impl details + generator = self.input_batch.generators.get(i) + if generator is not None: + generator.set_offset(generator.get_offset() - 4) + # Record the index of the request that should not be sampled, + # so that we could clear the sampled tokens before returning. + discard_sampled_tokens_req_indices.append(i) + + # NOTE: GPU -> CPU Sync happens here. + # Move as many CPU operations as possible before this sync point. + logprobs_tensors = sampler_output.logprobs_tensors + logprobs_lists = logprobs_tensors.tolists() \ + if logprobs_tensors is not None else None + + # Compute prompt logprobs if needed. + prompt_logprobs_dict = self._get_prompt_logprobs_dict( + text_hidden_states[:num_scheduled_tokens], + scheduler_output, + ) + + # Get the valid generated tokens. + import os + sampled_token_ids = sampler_output.sampled_token_ids if os.environ.get("model_stage") != "code2wav" else torch.tensor([[8294]]).to(torch.int32).cuda() + max_gen_len = sampled_token_ids.shape[-1] + if max_gen_len == 1: + # No spec decode tokens. + valid_sampled_token_ids = sampled_token_ids.tolist() + else: + # Includes spec decode tokens. + valid_sampled_token_ids = self.rejection_sampler.parse_output( + sampled_token_ids, + self.input_batch.vocab_size, + ) + # Mask out the sampled tokens that should not be sampled. + for i in discard_sampled_tokens_req_indices: + valid_sampled_token_ids[i].clear() + + # Cache the sampled tokens in the model runner, so that the scheduler + # doesn't need to send them back. + # NOTE(woosuk): As an exception, when using PP, the scheduler sends + # the sampled tokens back, because there's no direct communication + # between the first-stage worker and the last-stage worker. + for req_idx, sampled_ids in enumerate(valid_sampled_token_ids): + if not sampled_ids: + continue + + start_idx = self.input_batch.num_tokens_no_spec[req_idx] + end_idx = start_idx + len(sampled_ids) + assert end_idx <= self.max_model_len, ( + "Sampled token IDs exceed the max model length. " + f"Total number of tokens: {end_idx} > max_model_len: " + f"{self.max_model_len}") + + self.input_batch.token_ids_cpu[req_idx, + start_idx:end_idx] = sampled_ids + self.input_batch.num_tokens_no_spec[req_idx] = end_idx + self.input_batch.num_tokens[req_idx] = end_idx + req_id = self.input_batch.req_ids[req_idx] + req_state = self.requests[req_id] + req_state.output_token_ids.extend(sampled_ids) + + if not self.speculative_config: + # Speculative decoding is not enabled. + spec_token_ids = None + else: + assert spec_decode_common_attn_metadata is not None + spec_token_ids = self.propose_draft_token_ids( + scheduler_output, + valid_sampled_token_ids, + sampling_metadata, + text_hidden_states, + sample_hidden_states, + aux_hidden_states, + spec_decode_metadata, + spec_decode_common_attn_metadata, + ) + + self.eplb_step() + + return OmniModelRunnerOutput( + req_ids=self.input_batch.req_ids, + req_id_to_index=self.input_batch.req_id_to_index, + sampled_token_ids=valid_sampled_token_ids, + spec_token_ids=spec_token_ids, + logprobs=logprobs_lists, + prompt_logprobs_dict=prompt_logprobs_dict, + pooler_output=[], + kv_connector_output=kv_connector_output, + num_nans_in_logits=num_nans_in_logits, + multimodal_outputs=multimodal_outputs, + ) + + @torch.inference_mode() + def extract_multimodal_outputs(self, hidden_states: torch.Tensor) -> dict: + if hasattr(self.model, "have_multimodal_outputs") and self.model.have_multimodal_outputs: + text_hidden_states = hidden_states.text_hidden_states + multimodal_outputs = hidden_states.multimodal_outputs + + else: + text_hidden_states = hidden_states + multimodal_outputs = {} + return text_hidden_states, multimodal_outputs + + @torch.inference_mode() + def _dummy_run( + self, + num_tokens: int, + capture_attn_cudagraph: bool = False, + skip_eplb: bool = False, + is_profile: bool = False, + ) -> tuple[torch.Tensor, torch.Tensor]: + + # Padding for DP + num_pad, num_tokens_across_dp = self.get_dp_padding(num_tokens) + num_tokens += num_pad + + # Set num_scheduled_tokens based on num_tokens and max_num_seqs + # for dummy run with LoRA so that the num_reqs collectively + # has num_tokens in total. + assert num_tokens <= self.scheduler_config.max_num_batched_tokens + max_num_reqs = self.scheduler_config.max_num_seqs + num_reqs = min(num_tokens, max_num_reqs) + min_tokens_per_req = num_tokens // num_reqs + num_scheduled_tokens_list = [min_tokens_per_req] * num_reqs + num_scheduled_tokens_list[-1] += num_tokens % num_reqs + assert sum(num_scheduled_tokens_list) == num_tokens + assert len(num_scheduled_tokens_list) == num_reqs + num_scheduled_tokens = np.array(num_scheduled_tokens_list, + dtype=np.int32) + + attn_metadata: Optional[dict[str, Any]] = None + if capture_attn_cudagraph: + attn_metadata = {} + + # Make sure max_model_len is used at the graph capture time. + self.seq_lens_np[:num_reqs] = self.max_model_len + self.seq_lens_np[num_reqs:] = 0 + self.seq_lens[:num_reqs].copy_(self.seq_lens_cpu[:num_reqs], + non_blocking=True) + + for kv_cache_group_id, kv_cache_group_spec in enumerate( + self.kv_cache_config.kv_cache_groups): + common_attn_metadata = CommonAttentionMetadata( + query_start_loc=self.query_start_loc[:num_reqs + 1], + query_start_loc_cpu=self.query_start_loc_cpu[:num_reqs + + 1], + seq_lens=self.seq_lens[:num_reqs], + seq_lens_cpu=self.seq_lens_cpu[:num_reqs], + num_computed_tokens_cpu=self.input_batch. + num_computed_tokens_cpu_tensor[:num_reqs], + num_reqs=num_reqs, + num_actual_tokens=num_tokens, + max_query_len=num_tokens, + block_table_tensor=self.input_batch.block_table[ + kv_cache_group_id].get_device_tensor()[:num_reqs], + slot_mapping=self.input_batch. + block_table[kv_cache_group_id].slot_mapping[:num_tokens], + causal=True) + + for attn_group in self.attn_groups[kv_cache_group_id]: + attn_metadata_i = attn_group.metadata_builder\ + .build_for_cudagraph_capture(common_attn_metadata) + for layer_name in kv_cache_group_spec.layer_names: + attn_metadata[layer_name] = attn_metadata_i + + with self.maybe_dummy_run_with_lora(self.lora_config, + num_scheduled_tokens): + if self.is_multimodal_model: + input_ids = None + inputs_embeds = self.inputs_embeds[:num_tokens] + model_mm_kwargs = self._dummy_mm_kwargs(num_reqs) + else: + input_ids = self.input_ids[:num_tokens] + inputs_embeds = None + model_mm_kwargs = {} + + if self.uses_mrope: + positions = self.mrope_positions[:, :num_tokens] + else: + positions = self.positions[:num_tokens] + + if get_pp_group().is_first_rank: + intermediate_tensors = None + else: + if self.intermediate_tensors is None: + self.intermediate_tensors = ( + self.model.make_empty_intermediate_tensors( + batch_size=self.max_num_tokens, + dtype=self.model_config.dtype, + device=self.device)) + + intermediate_tensors = self.sync_and_slice_intermediate_tensors( + num_tokens, None, False) + + with self.maybe_randomize_inputs(input_ids), set_forward_context( + attn_metadata, + self.vllm_config, + num_tokens=num_tokens, + num_tokens_across_dp=num_tokens_across_dp): + outputs = self.model( + input_ids=input_ids, + positions=positions, + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **MultiModalKwargs.as_kwargs( + model_mm_kwargs, + device=self.device, + ), + sampler = None + ) + + if self.use_aux_hidden_state_outputs: + hidden_states, _ = outputs + else: + hidden_states = outputs + + logger.warning(f"Multimodal outputs are not returned in the dummy run, need to double check the implementation!") + text_hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states) + + + if self.speculative_config and self.speculative_config.use_eagle(): + assert isinstance(self.drafter, EagleProposer) + self.drafter.dummy_run(num_tokens) + + # This is necessary to avoid blocking DP. + # For dummy runs, we typically skip EPLB since we don't have any real + # requests to process. + # However, in DP settings, there may be cases when some DP ranks do + # not have any requests to process, so they're executing dummy batches. + # In such cases, we still have to trigger EPLB to make sure + # ranks execute the rearrangement in synchronization. + if not skip_eplb: + self.eplb_step(is_dummy=True, is_profile=is_profile) + + logit_indices = np.cumsum(num_scheduled_tokens) - 1 + return text_hidden_states, text_hidden_states[logit_indices] \ No newline at end of file diff --git a/vllm_omni/worker/model_runner.py b/vllm_omni/worker/model_runner.py new file mode 100644 index 000000000..ca7b78d98 --- /dev/null +++ b/vllm_omni/worker/model_runner.py @@ -0,0 +1,199 @@ +from dataclasses import dataclass +from typing import Optional, Dict, Any, List +import itertools + +import torch + +from vllm.worker.model_runner import ModelInputForGPU, ModelInputForGPUBuilder + +from vllm.utils import flatten_2d_lists, async_tensor_h2d +from vllm.lora.layers import LoRAMapping +from vllm.sequence import MultiModalKwargs + + +@dataclass(frozen=True) +class OmniModelInputForGPU(ModelInputForGPU): + # New: optional additional information dict for current scheduled tokens + additional_information: Optional[Dict[str, Any]] = None + + +class OmniModelInputForGPUBuilder(ModelInputForGPUBuilder): + def build(self) -> OmniModelInputForGPU: + """Finalize the builder intermediate data and + create on-device tensors. + """ + # Combine and flatten intermediate data. + input_tokens = list[int]() + inputs_embeds_list = list[torch.Tensor]() + token_types = list[int]() + for inter_data in self.inter_data_list: + for cur_input_tokens in inter_data.input_tokens: + input_tokens.extend(cur_input_tokens) + for cur_token_types in inter_data.token_types: + token_types.extend(cur_token_types) + if inter_data.inputs_embeds is not None: + inputs_embeds_list.append( + inter_data.inputs_embeds.to( + dtype=self.runner.model_config.dtype, + device=self.runner.device)) + # Support v1 direct-transfer prompt embeds attached on request state + if inter_data.inputs_embeds is None: + try: + # Locate req_id -> request state to fetch decoded CPU embeds + req_id = inter_data.request_id # type: ignore[attr-defined] + req_state = getattr(self.runner, "requests", {}).get(req_id) + pe_cpu = getattr(req_state, "prompt_embeds_cpu", None) + if pe_cpu is not None: + inter_data.inputs_embeds = pe_cpu.to( + dtype=self.runner.model_config.dtype, + device=self.runner.device) + inputs_embeds_list.append(inter_data.inputs_embeds) + except Exception: + pass + inputs_embeds: Optional[torch.Tensor] + if len(inputs_embeds_list) == 0: + inputs_embeds = None + else: + inputs_embeds = torch.cat(inputs_embeds_list, dim=0).to( + dtype=self.runner.model_config.dtype, + device=self.runner.device) + assert len(inputs_embeds) == len(input_tokens) + + if not input_tokens and inputs_embeds is None: + # This may happen when all prefill requests hit + # prefix caching and there is no decode request. + return self.model_input_cls() + + mrope_input_positions: Optional[List[List[int]]] = None + if any(inter_data.mrope_input_positions is not None + for inter_data in self.inter_data_list): + mrope_input_positions = [[] for _ in range(3)] + for idx in range(3): + for inter_data in self.inter_data_list: + msections = inter_data.mrope_input_positions + if msections is None: + for _seq_input_positions in inter_data.input_positions: + mrope_input_positions[idx].extend( + _seq_input_positions) + else: + for _seq_mrope_input_positions in msections: + mrope_input_positions[idx].extend( + _seq_mrope_input_positions[idx]) + input_positions = None + else: + input_positions = [] + for inter_data in self.inter_data_list: + for cur_input_positions in inter_data.input_positions: + input_positions.extend(cur_input_positions) + + seq_lens = [] + query_lens = [] + max_decode_seq_len = 0 + max_encoder_seq_len = 0 + for inter_data in self.inter_data_list: + seq_lens.extend(inter_data.seq_lens) + query_lens.extend(inter_data.query_lens) + if not inter_data.is_prompt: + max_decode_seq_len = max(max_decode_seq_len, + max(inter_data.seq_lens)) + if self.runner.model_config.is_encoder_decoder: + max_encoder_seq_len = max(max_encoder_seq_len, + inter_data.encoder_seq_len) + + # Mapping from request IDs to sequence IDs. Used for Jamba models + # that manages the cache by itself. + request_ids_to_seq_ids = { + data.request_id: data.seq_ids + for data in self.inter_data_list + } + + cuda_graph_pad_size = self._get_cuda_graph_pad_size( + num_seqs=len(seq_lens), + max_decode_seq_len=max_decode_seq_len, + max_encoder_seq_len=max_encoder_seq_len) + + batch_size = len(input_tokens) + if cuda_graph_pad_size != -1: + # If cuda graph can be used, pad tensors accordingly. + # See `capture_model` API for more details. + # vLLM uses cuda graph only for decoding requests. + batch_size += cuda_graph_pad_size + + # Tokens and positions. + if cuda_graph_pad_size: + input_tokens.extend(itertools.repeat(0, cuda_graph_pad_size)) + assert self.runner.device is not None + input_tokens_tensor = async_tensor_h2d(input_tokens, torch.long, + self.runner.device, + self.runner.pin_memory) + + token_types_tensor = async_tensor_h2d(token_types, torch.long, + self.runner.device, + self.runner.pin_memory) \ + if token_types else None + + if mrope_input_positions is not None: + for idx in range(3): + mrope_input_positions[idx].extend( + itertools.repeat(0, cuda_graph_pad_size)) + input_positions_tensor = async_tensor_h2d(mrope_input_positions, + torch.long, + self.runner.device, + self.runner.pin_memory) + else: + input_positions.extend(itertools.repeat(0, cuda_graph_pad_size)) + input_positions_tensor = async_tensor_h2d(input_positions, + torch.long, + self.runner.device, + self.runner.pin_memory) + # Sequence and query lengths. + if cuda_graph_pad_size: + seq_lens.extend(itertools.repeat(1, cuda_graph_pad_size)) + + # Attention metadata. + attn_metadata = self.attn_metadata_builder.build( + seq_lens, query_lens, cuda_graph_pad_size, batch_size) + + # LoRA data. + lora_requests = set() + lora_mapping = None + if self.enable_lora: + lora_requests = set(r for data in self.inter_data_list + for r in data.lora_requests) + lora_index_mapping = flatten_2d_lists([ + flatten_2d_lists(inter_data.lora_index_mapping) + for inter_data in self.inter_data_list + ]) + if cuda_graph_pad_size: + lora_index_mapping.extend( + itertools.repeat(0, cuda_graph_pad_size)) + lora_prompt_mapping = flatten_2d_lists([ + flatten_2d_lists(inter_data.lora_prompt_mapping) + for inter_data in self.inter_data_list + ]) + + lora_mapping = LoRAMapping( + **dict(index_mapping=lora_index_mapping, + prompt_mapping=lora_prompt_mapping, + is_prefill=not self.decode_only)) + + # Multi-modal data. + multi_modal_kwargs_list = [ + data.multi_modal_kwargs for data in self.inter_data_list + if data.multi_modal_kwargs is not None + ] + multi_modal_kwargs = MultiModalKwargs.batch(multi_modal_kwargs_list) + + return self.model_input_cls( + input_tokens=input_tokens_tensor, + inputs_embeds=inputs_embeds, + input_positions=input_positions_tensor, + token_types=token_types_tensor, + attn_metadata=attn_metadata, + seq_lens=seq_lens, + query_lens=query_lens, + lora_mapping=lora_mapping, + lora_requests=lora_requests, + multi_modal_kwargs=multi_modal_kwargs, + request_ids_to_seq_ids=request_ids_to_seq_ids, + finished_requests_ids=self.finished_requests_ids) \ No newline at end of file From 53b86439fc0dd811c2dc97d8dee00a158ecbf9c6 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 17:16:15 +0800 Subject: [PATCH 08/25] Refactor ARModelRunner to streamline hidden state handling - Removed unnecessary imports of numpy and torch within the request scheduling logic. - Simplified the handling of auxiliary hidden states by initializing it to None when not present. - Optimized CPU tensor conversion for text hidden states to improve performance. --- vllm_omni/worker/gpu_ar_model_runner.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vllm_omni/worker/gpu_ar_model_runner.py b/vllm_omni/worker/gpu_ar_model_runner.py index 6f32a3c03..7e8ec3135 100644 --- a/vllm_omni/worker/gpu_ar_model_runner.py +++ b/vllm_omni/worker/gpu_ar_model_runner.py @@ -59,8 +59,6 @@ class ARModelRunner(OmniGPUModelRunner): try: new_reqs = getattr(scheduler_output, "scheduled_new_reqs", []) if new_reqs: - import numpy as np - import torch for nr in new_reqs: req_id = getattr(nr, "req_id", None) or getattr(nr, "request_id", None) if req_id is None: @@ -272,6 +270,7 @@ class ARModelRunner(OmniGPUModelRunner): hidden_states, _aux_hidden_states = model_output else: hidden_states = model_output + _aux_hidden_states = None text_hidden_states, multimodal_outputs = ( self.extract_multimodal_outputs(hidden_states)) @@ -403,16 +402,17 @@ class ARModelRunner(OmniGPUModelRunner): sampling_metadata, text_hidden_states, sample_hidden_states, - _aux_hidden_states if '_aux_hidden_states' in locals() else None, + _aux_hidden_states, spec_decode_metadata, spec_decode_common_attn_metadata, ) # Convert to per-request tensors on CPU + text_hidden_states_cpu = text_hidden_states.detach().to("cpu").contiguous() pooler_output: list[Optional[torch.Tensor]] = [] prev_logits_index = 0 for logits_index in logits_indices: - pooler_output.append(text_hidden_states[prev_logits_index:logits_index+1].detach().to("cpu").contiguous()) + pooler_output.append(text_hidden_states_cpu[prev_logits_index:logits_index+1]) prev_logits_index = logits_index + 1 From 2cc33bb3ea107ea7c3c3e73484a8334d12462bfc Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 17:38:49 +0800 Subject: [PATCH 09/25] Fix tensor handling in DiffusionModelRunner for multimodal outputs - Updated assertions and tensor management to correctly handle multimodal outputs in the `DiffusionModelRunner` class. - Ensured that the output tensors are processed based on the type of multimodal outputs received, improving robustness and clarity in the code. --- vllm_omni/worker/gpu_diffusion_model_runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vllm_omni/worker/gpu_diffusion_model_runner.py b/vllm_omni/worker/gpu_diffusion_model_runner.py index b9d98515b..d30cf99c5 100644 --- a/vllm_omni/worker/gpu_diffusion_model_runner.py +++ b/vllm_omni/worker/gpu_diffusion_model_runner.py @@ -132,11 +132,11 @@ class DiffusionModelRunner(OmniGPUModelRunner): pooler_output: List[Optional[torch.Tensor]] = [] if isinstance(multimodal_outputs, torch.Tensor): # If model returned a single stacked tensor, split by requests - assert outputs.shape[0] == self.input_batch.num_reqs + assert multimodal_outputs.shape[0] == self.input_batch.num_reqs for i in range(self.input_batch.num_reqs): - pooler_output.append(outputs[i].detach().to("cpu").contiguous()) + pooler_output.append(multimodal_outputs[i].detach().to("cpu").contiguous()) elif isinstance(multimodal_outputs, list): - for out in outputs: + for out in multimodal_outputs: pooler_output.append(out.detach().to("cpu").contiguous() if out is not None else None) elif isinstance(multimodal_outputs, dict): for out in multimodal_outputs.values(): From 4609afa613f14e6324ada7fdb39b330dca86cca3 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Wed, 22 Oct 2025 17:42:29 +0800 Subject: [PATCH 10/25] Refactor sampled_token_ids assignment in OmniGPUModelRunner - Simplified the assignment of sampled_token_ids by removing the conditional check for "model_stage". - Ensured consistent handling of sampled_token_ids for improved clarity and maintainability. --- vllm_omni/worker/gpu_model_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_omni/worker/gpu_model_runner.py b/vllm_omni/worker/gpu_model_runner.py index acbda8648..df021e4a5 100644 --- a/vllm_omni/worker/gpu_model_runner.py +++ b/vllm_omni/worker/gpu_model_runner.py @@ -542,7 +542,7 @@ class OmniGPUModelRunner(GPUModelRunner): # Get the valid generated tokens. import os - sampled_token_ids = sampler_output.sampled_token_ids if os.environ.get("model_stage") != "code2wav" else torch.tensor([[8294]]).to(torch.int32).cuda() + sampled_token_ids = sampler_output.sampled_token_ids max_gen_len = sampled_token_ids.shape[-1] if max_gen_len == 1: # No spec decode tokens. From 6111c288d02a62955dc6532d71ce35bebfe7d2a2 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Wed, 22 Oct 2025 17:46:10 +0800 Subject: [PATCH 11/25] fix some reviews from gemini --- vllm_omni/engine/arg_utils.py | 2 +- vllm_omni/entrypoints/omni_llm.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vllm_omni/engine/arg_utils.py b/vllm_omni/engine/arg_utils.py index 2f744480c..41a74ca47 100644 --- a/vllm_omni/engine/arg_utils.py +++ b/vllm_omni/engine/arg_utils.py @@ -26,7 +26,7 @@ class OmniEngineArgs(EngineArgs): "model_config.engine_output_type for schedulers to use." ), ) - parser.add_argument("--model-stage", type=str, default=EngineArgs.model_stage, + parser.add_argument("--model-stage", type=str, default=OmniEngineArgs.model_stage, help="Declare model stage (e.g., 'thinker', 'talker', 'token2wav'). This will be written into model_config.model_stage for schedulers to use.") return parser diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index aee4fcf15..063d37570 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -39,8 +39,8 @@ class OmniLLM: def initialize_stages(self, model: str): for stage_config in self.stage_configs: stage = Stage(stage_config) - omni_llm = OmniLLM(model=model, **stage_config.engine_args) - stage.set_engine(omni_llm) + stage_llm = StageLLM(model=model, **stage_config.engine_args) + stage.set_engine(stage_llm) self.stage_list.append(stage) def generate( @@ -51,6 +51,7 @@ class OmniLLM: ) -> list[OmniRequestOutput]: """Generate text outputs for the given prompts.""" final_outputs: list[OmniRequestOutput] = [] + assert len(sampling_params_list) == len(self.stage_list), "sampling_params_list must be a list of length equal to the number of stages" for stage_id, stage in enumerate(self.stage_list): if stage_id > 0: engine_inputs = stage.process_engine_inputs(self.stage_list, prompts) From 87d6414a26fdce8e5c11e32a15f54e3400313156 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Wed, 22 Oct 2025 18:14:11 +0800 Subject: [PATCH 12/25] supplement some imports --- vllm_omni/entrypoints/omni_llm.py | 6 ++++ .../stage_configs/qwen2_5_omni.yaml | 7 ++++ .../stage_input_processors/__init__.py | 0 .../stage_input_processors/qwen2_5_omni.py | 32 +++++++++++++++++++ 4 files changed, 45 insertions(+) create mode 100644 vllm_omni/model_executor/stage_input_processors/__init__.py create mode 100644 vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index 063d37570..214060a40 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -17,6 +17,8 @@ import vllm.envs as envs from vllm_omni.entrypoints.utils import load_stage_configs_from_model from vllm_omni.entrypoints.stage_manager import Stage from vllm_omni.engine.arg_utils import OmniEngineArgs +from vllm_omni.engine.output_processor import MultimodalOutputProcessor +from vllm_omni.engine.processor import OmniProcessor from vllm_omni.outputs import OmniRequestOutput @@ -134,6 +136,10 @@ class StageLLM(LLM): # Create the Engine (autoselects V0 vs V1) self.llm_engine = LLMEngine.from_engine_args( engine_args=engine_args, usage_context=UsageContext.LLM_CLASS) + self.llm_engine.output_processor = MultimodalOutputProcessor(tokenizer=self.llm_engine.tokenizer, + log_stats=self.llm_engine.log_stats) + self.llm_engine.processor = OmniProcessor(vllm_config=self.llm_engine.vllm_config, + tokenizer=self.llm_engine.tokenizer) self.engine_class = type(self.llm_engine) self.request_counter = Counter() diff --git a/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml index 05e45d3b2..aa38bae56 100644 --- a/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml +++ b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml @@ -4,6 +4,8 @@ stage_args: engine_args: model_stage: thinker model_arch: Qwen2_5OmniForConditionalGeneration + worker_cls: vllm_omni.worker.AR_gpu_worker.ARGPUWorker + scheduler_cls: vllm_omni.core.sched.scheduler.OmniScheduler gpu_memory_utilization: 0.32 enforce_eager: true # need to discuss trust_remote_code: true @@ -14,16 +16,21 @@ stage_args: engine_args: model_stage: talker model_arch: Qwen2_5OmniForConditionalGeneration + worker_cls: vllm_omni.worker.AR_gpu_worker.ARGPUWorker + scheduler_cls: vllm_omni.core.sched.scheduler.OmniScheduler gpu_memory_utilization: 0.32 enforce_eager: true trust_remote_code: true engine_output_type: latent engine_input_source: [0] + custom_process_input_func: vllm_omni.model_executor.stage_input_processors.qwen2_5_omni.thinker2talker - stage_id: 2 engine_args: model_stage: code2wav model_arch: Qwen2_5OmniForConditionalGeneration + worker_cls: vllm_omni.worker.diffusion_gpu_worker.DiffusionGPUWorker + scheduler_cls: vllm_omni.core.sched.diffusion_scheduler.DiffusionScheduler gpu_memory_utilization: 0.3 enforce_eager: true trust_remote_code: true diff --git a/vllm_omni/model_executor/stage_input_processors/__init__.py b/vllm_omni/model_executor/stage_input_processors/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py new file mode 100644 index 000000000..a854fc562 --- /dev/null +++ b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py @@ -0,0 +1,32 @@ +from vllm.inputs import TextPrompt +from typing import Union +import torch +from vllm_omni.inputs.data import OmniTokensPrompt + +def thinker2talker(stage_list, engine_input_source, prompt: Union[OmniTokensPrompt, TextPrompt] = None): + source_stage_id = engine_input_source[0] + thinker_outputs = stage_list[source_stage_id].engine_outputs + talker_inputs = [] + multi_modal_data = {thinker_output.request_id: + prompt.get('multi_modal_data', None) for thinker_output, prompt in zip(thinker_outputs, prompt)} + + for i, thinker_output in enumerate(thinker_outputs): + output = thinker_output.outputs[0] + prompt_token_ids = thinker_output.prompt_token_ids + thinker_output_ids = output.token_ids + prompt_token_ids_len = len(prompt_token_ids) + thinker_hidden_states = output.multimodal_output["latent"].clone().detach().cuda() + talker_inputs.append( + OmniTokensPrompt( + prompt_token_ids=[0] * (len(prompt_token_ids) + 2), # add 2 for codec pad and start token + additional_information={ + "thinker_result": thinker_hidden_states[prompt_token_ids_len:].to(torch.float32), + "prompt_embeds": thinker_hidden_states[:prompt_token_ids_len].to(torch.float32), + "prompt_token_ids": prompt_token_ids, + "thinker_output_token_ids": thinker_output_ids, + }, + multi_modal_data=multi_modal_data[thinker_output.request_id] if multi_modal_data is not None else None, + mm_processor_kwargs=None, + ) + ) + return talker_inputs \ No newline at end of file From 98a85d42a6083af13caf83b665f79637ddded199 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Thu, 23 Oct 2025 10:26:21 +0800 Subject: [PATCH 13/25] fix for some reviews --- vllm_omni/config/__init__.py | 3 ++- vllm_omni/entrypoints/omni_llm.py | 2 +- vllm_omni/entrypoints/{stage_manager.py => stage.py} | 0 vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml | 7 +++---- .../model_executor/stage_input_processors/qwen2_5_omni.py | 4 +++- 5 files changed, 9 insertions(+), 7 deletions(-) rename vllm_omni/entrypoints/{stage_manager.py => stage.py} (100%) diff --git a/vllm_omni/config/__init__.py b/vllm_omni/config/__init__.py index 2f53543ee..a688faf1b 100644 --- a/vllm_omni/config/__init__.py +++ b/vllm_omni/config/__init__.py @@ -1,10 +1,11 @@ """ Configuration module for vLLM-omni. """ -from vllm.config import ModelConfig from typing import Optional from pydantic.dataclasses import dataclass from pydantic import ConfigDict + +from vllm.config import ModelConfig from vllm.config import config import vllm_omni.model_executor.models as me_models diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index 214060a40..9cac1ee6f 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -15,7 +15,7 @@ from vllm.logger import init_logger import vllm.envs as envs from vllm_omni.entrypoints.utils import load_stage_configs_from_model -from vllm_omni.entrypoints.stage_manager import Stage +from vllm_omni.entrypoints.stage import Stage from vllm_omni.engine.arg_utils import OmniEngineArgs from vllm_omni.engine.output_processor import MultimodalOutputProcessor from vllm_omni.engine.processor import OmniProcessor diff --git a/vllm_omni/entrypoints/stage_manager.py b/vllm_omni/entrypoints/stage.py similarity index 100% rename from vllm_omni/entrypoints/stage_manager.py rename to vllm_omni/entrypoints/stage.py diff --git a/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml index aa38bae56..49a1222cc 100644 --- a/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml +++ b/vllm_omni/model_executor/stage_configs/qwen2_5_omni.yaml @@ -4,7 +4,7 @@ stage_args: engine_args: model_stage: thinker model_arch: Qwen2_5OmniForConditionalGeneration - worker_cls: vllm_omni.worker.AR_gpu_worker.ARGPUWorker + worker_cls: vllm_omni.worker.gpu_ar_worker.GPUARWorker scheduler_cls: vllm_omni.core.sched.scheduler.OmniScheduler gpu_memory_utilization: 0.32 enforce_eager: true # need to discuss @@ -16,7 +16,7 @@ stage_args: engine_args: model_stage: talker model_arch: Qwen2_5OmniForConditionalGeneration - worker_cls: vllm_omni.worker.AR_gpu_worker.ARGPUWorker + worker_cls: vllm_omni.worker.gpu_ar_worker.GPUARWorker scheduler_cls: vllm_omni.core.sched.scheduler.OmniScheduler gpu_memory_utilization: 0.32 enforce_eager: true @@ -24,12 +24,11 @@ stage_args: engine_output_type: latent engine_input_source: [0] custom_process_input_func: vllm_omni.model_executor.stage_input_processors.qwen2_5_omni.thinker2talker - - stage_id: 2 engine_args: model_stage: code2wav model_arch: Qwen2_5OmniForConditionalGeneration - worker_cls: vllm_omni.worker.diffusion_gpu_worker.DiffusionGPUWorker + worker_cls: vllm_omni.worker.gpu_diffusion_worker.GPUDiffusionWorker scheduler_cls: vllm_omni.core.sched.diffusion_scheduler.DiffusionScheduler gpu_memory_utilization: 0.3 enforce_eager: true diff --git a/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py index a854fc562..08585a765 100644 --- a/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py +++ b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py @@ -1,6 +1,8 @@ -from vllm.inputs import TextPrompt from typing import Union import torch + +from vllm.inputs import TextPrompt + from vllm_omni.inputs.data import OmniTokensPrompt def thinker2talker(stage_list, engine_input_source, prompt: Union[OmniTokensPrompt, TextPrompt] = None): From ea16546ae975b29d65c71db55478c4a3e77e90da Mon Sep 17 00:00:00 2001 From: tzhouam Date: Thu, 23 Oct 2025 11:04:10 +0800 Subject: [PATCH 14/25] Refactor GPU diffusion model classes for clarity and consistency - Renamed `DiffusionModelRunner` to `GPUDiffusionModelRunner` for better clarity in class purpose. - Updated `DiffusionGPUWorker` to utilize the renamed `GPUDiffusionModelRunner`. - Simplified output handling in `GPUDiffusionModelRunner` by removing unnecessary checks, enhancing code readability. --- vllm_omni/worker/gpu_diffusion_model_runner.py | 9 +++------ vllm_omni/worker/gpu_diffusion_worker.py | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/vllm_omni/worker/gpu_diffusion_model_runner.py b/vllm_omni/worker/gpu_diffusion_model_runner.py index d30cf99c5..2cf308895 100644 --- a/vllm_omni/worker/gpu_diffusion_model_runner.py +++ b/vllm_omni/worker/gpu_diffusion_model_runner.py @@ -10,7 +10,6 @@ import logging import torch from vllm.v1.worker.gpu_model_runner import ( - GPUModelRunner, EMPTY_MODEL_RUNNER_OUTPUT, IntermediateTensors, get_pp_group, @@ -30,7 +29,7 @@ from vllm_omni.worker.gpu_model_runner import OmniGPUModelRunner logger = logging.getLogger(__name__) -class DiffusionModelRunner(OmniGPUModelRunner): +class GPUDiffusionModelRunner(OmniGPUModelRunner): """Diffusion model runner for vLLM-omni (non-autoregressive). - Reuses GPUModelRunner preparation, multimodal handling, and TP/PP/DP glue. @@ -47,11 +46,9 @@ class DiffusionModelRunner(OmniGPUModelRunner): self._update_states(scheduler_output) if not scheduler_output.total_num_scheduled_tokens: - if not has_kv_transfer_group(): - return EMPTY_MODEL_RUNNER_OUTPUT - return self.kv_connector_no_forward(scheduler_output, - self.vllm_config) + return EMPTY_MODEL_RUNNER_OUTPUT + # Prepare decoder inputs and attention metadata (for batch/order mapping) (attn_metadata, attention_cuda_graphs, logits_indices, spec_decode_metadata, num_scheduled_tokens_np, diff --git a/vllm_omni/worker/gpu_diffusion_worker.py b/vllm_omni/worker/gpu_diffusion_worker.py index a9767f013..be4c2047d 100644 --- a/vllm_omni/worker/gpu_diffusion_worker.py +++ b/vllm_omni/worker/gpu_diffusion_worker.py @@ -9,10 +9,10 @@ from vllm.v1.worker.gpu_worker import init_worker_distributed_environment from vllm.model_executor import set_random_seed from vllm.v1.utils import report_usage_stats -from vllm_omni.worker.diffusion_model_runner import DiffusionModelRunner +from vllm_omni.worker.gpu_diffusion_model_runner import GPUDiffusionModelRunner -class DiffusionGPUWorker(GPUWorker): +class GPUDiffusionWorker(GPUWorker): def init_device(self): if self.device_config.device.type == "cuda": # torch.distributed.all_reduce does not free the input tensor until @@ -59,7 +59,7 @@ class DiffusionGPUWorker(GPUWorker): set_random_seed(self.model_config.seed) # Construct the model runner - self.model_runner: DiffusionModelRunner = DiffusionModelRunner( + self.model_runner: GPUDiffusionModelRunner = GPUDiffusionModelRunner( self.vllm_config, self.device) if self.rank == 0: From a3a77b6cc4cfb8f6c7ad4b3c0b325cdafd9ce9aa Mon Sep 17 00:00:00 2001 From: tzhouam Date: Thu, 23 Oct 2025 14:24:03 +0800 Subject: [PATCH 15/25] Qwen2.5-Omni: squash 5 commits (add components, refactor, load thinker embedding from weights, remove unused code) Squashed commits:\n- 4e2d812 Add Qwen2.5-Omni model components\n- 351f833 load the thinker embedding from the weights, not rely on local .pt files\n- 2c32533 Refactor Qwen2.5-Omni model files\n- 9163a60 remove unused codes\n- 192bdf8 remove --- vllm_omni/model_executor/layers/__init__.py | 0 vllm_omni/model_executor/layers/mrope.py | 699 +++++++ .../model_executor/models/qwen2_5_omni.py | 706 +++++++ .../models/qwen2_5_omni_talker.py | 239 +++ .../models/qwen2_5_omni_thinker.py | 889 +++++++++ .../models/qwen2_5_omni_token2wav.py | 1690 +++++++++++++++++ vllm_omni/model_executor/models/qwen2_old.py | 539 ++++++ vllm_omni/model_executor/models/utils.py | 8 + 8 files changed, 4770 insertions(+) create mode 100644 vllm_omni/model_executor/layers/__init__.py create mode 100644 vllm_omni/model_executor/layers/mrope.py create mode 100644 vllm_omni/model_executor/models/qwen2_5_omni.py create mode 100644 vllm_omni/model_executor/models/qwen2_5_omni_talker.py create mode 100644 vllm_omni/model_executor/models/qwen2_5_omni_thinker.py create mode 100644 vllm_omni/model_executor/models/qwen2_5_omni_token2wav.py create mode 100644 vllm_omni/model_executor/models/qwen2_old.py create mode 100644 vllm_omni/model_executor/models/utils.py diff --git a/vllm_omni/model_executor/layers/__init__.py b/vllm_omni/model_executor/layers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vllm_omni/model_executor/layers/mrope.py b/vllm_omni/model_executor/layers/mrope.py new file mode 100644 index 000000000..e7871cb38 --- /dev/null +++ b/vllm_omni/model_executor/layers/mrope.py @@ -0,0 +1,699 @@ +import itertools +from typing import Optional, Union + +import numpy as np +import torch +from transformers import PretrainedConfig + +from vllm.model_executor.layers.rotary_embedding.base import RotaryEmbedding +from vllm.model_executor.layers.rotary_embedding.common import apply_rotary_emb_dispatch + +def _apply_rotary_emb( + x: torch.Tensor, + cos: torch.Tensor, + sin: torch.Tensor, + is_neox_style: bool, +) -> torch.Tensor: + """ + Args: + x: [num_tokens, num_heads, head_size] + cos: [num_tokens, head_size // 2] + sin: [num_tokens, head_size // 2] + is_neox_style: Whether to use the Neox-style or GPT-J-style rotary + positional embeddings. + """ + cos = cos.unsqueeze(-2).to(x.dtype) + sin = sin.unsqueeze(-2).to(x.dtype) + if is_neox_style: + x1, x2 = torch.chunk(x, 2, dim=-1) + else: + x1 = x[..., ::2] + x2 = x[..., 1::2] + o1 = x1 * cos - x2 * sin + o2 = x2 * cos + x1 * sin + if is_neox_style: + return torch.cat((o1, o2), dim=-1) + else: + return torch.stack((o1, o2), dim=-1).flatten(-2) + +class MRotaryEmbedding(RotaryEmbedding): + """Rotary Embedding with Multimodal Sections.""" + + def __init__( + self, + head_size: int, + rotary_dim: int, + max_position_embeddings: int, + base: float, + is_neox_style: bool, + dtype: torch.dtype, + mrope_section: Optional[list[int]] = None, + ) -> None: + # In Qwen2.5-VL, the maximum index value is related to the duration of + # the input video. We enlarge max_position_embeddings to 4 times to get + # a larger the cos and sin cache. + self.cache_max_position_num = max_position_embeddings * 4 + super().__init__(head_size, rotary_dim, self.cache_max_position_num, + base, is_neox_style, dtype) + + self.mrope_section = mrope_section + if self.mrope_section: + print("Warning: mrope_section check is not disabled in Qwen2.5-Omni, this may cause errors, and should be removed in the future.") + # assert sum(self.mrope_section) == rotary_dim // 2 + + def forward( + self, + positions: torch.Tensor, + query: torch.Tensor, + key: Optional[torch.Tensor] = None, + ) -> tuple[torch.Tensor, Optional[torch.Tensor]]: + """PyTorch-native implementation equivalent to forward(). + + Args: + positions: + [num_tokens,] (text only) or + [3, num_tokens] (T/H/W positions with multimodal inputs) + query: [num_tokens, num_heads * head_size] + key: [num_tokens, num_kv_heads * head_size] + """ + assert positions.ndim == 1 or positions.ndim == 2 + assert key is not None + + num_tokens = positions.shape[-1] + cos_sin = self.cos_sin_cache[positions] + cos, sin = cos_sin.chunk(2, dim=-1) + if positions.ndim == 2: + assert self.mrope_section + + cos = torch.cat([ + m[i] + for i, m in enumerate(cos.split(self.mrope_section, dim=-1)) + ], + dim=-1) + sin = torch.cat([ + m[i] + for i, m in enumerate(sin.split(self.mrope_section, dim=-1)) + ], + dim=-1) + + query_shape = query.shape + query = query.view(num_tokens, -1, self.head_size) + query_rot = query[..., :self.rotary_dim] + query_pass = query[..., self.rotary_dim:] + # query_rot = apply_rotary_emb_dispatch(query_rot, cos, sin, + # self.is_neox_style) + query_rot = _apply_rotary_emb(query_rot, cos, sin, + self.is_neox_style) + query = torch.cat((query_rot, query_pass), dim=-1).reshape(query_shape) + + key_shape = key.shape + key = key.view(num_tokens, -1, self.head_size) + key_rot = key[..., :self.rotary_dim] + key_pass = key[..., self.rotary_dim:] + # key_rot = apply_rotary_emb_dispatch(key_rot, cos, sin, + # self.is_neox_style) + key_rot = _apply_rotary_emb(key_rot, cos, sin, + self.is_neox_style) + key = torch.cat((key_rot, key_pass), dim=-1).reshape(key_shape) + return query, key + + @classmethod + def get_input_positions( + cls, + input_tokens: list[int], + hf_config: PretrainedConfig, + image_grid_thw: Optional[Union[list[list[int]], torch.Tensor]], + video_grid_thw: Optional[Union[list[list[int]], torch.Tensor]], + second_per_grid_ts: Optional[list[float]], + context_len: int = 0, + seq_len: Optional[int] = None, + audio_feature_lengths: Optional[torch.Tensor] = None, + use_audio_in_video: bool = False, + ) -> tuple[list[list[int]], int]: + """Get mrope input positions and delta value.""" + + image_grid_thw = [] if image_grid_thw is None else image_grid_thw + video_grid_thw = [] if video_grid_thw is None else video_grid_thw + second_per_grid_ts = [] if second_per_grid_ts is None else \ + second_per_grid_ts + + llm_positions, mrope_position_delta = \ + cls.get_input_positions_tensor( + input_tokens=input_tokens, + hf_config=hf_config, + image_grid_thw=image_grid_thw, + video_grid_thw=video_grid_thw, + second_per_grid_ts=second_per_grid_ts, + context_len=context_len, + seq_len=seq_len, + audio_feature_lengths=audio_feature_lengths, + use_audio_in_video=use_audio_in_video, + ) + + return llm_positions.tolist(), mrope_position_delta + + @classmethod + def get_input_positions_tensor( + cls, + input_tokens: list[int], + hf_config: PretrainedConfig, + image_grid_thw: Union[list[list[int]], torch.Tensor], + video_grid_thw: Union[list[list[int]], torch.Tensor], + second_per_grid_ts: list[float], + context_len: int = 0, + seq_len: Optional[int] = None, + audio_feature_lengths: Optional[torch.Tensor] = None, + use_audio_in_video: bool = False, + ) -> tuple[torch.Tensor, int]: + from vllm.transformers_utils.config import thinker_uses_mrope + if thinker_uses_mrope(hf_config): + return cls._omni_get_input_positions_tensor( + input_tokens=input_tokens, + hf_config=hf_config, + image_grid_thw=image_grid_thw, + video_grid_thw=video_grid_thw, + second_per_grid_ts=second_per_grid_ts, + context_len=context_len, + seq_len=seq_len, + audio_feature_lengths=audio_feature_lengths, + use_audio_in_video=use_audio_in_video, + ) + elif hf_config.model_type in ["glm4v", "glm4v_moe"]: + return cls._glm4v_get_input_positions_tensor( + input_tokens=input_tokens, + hf_config=hf_config, + image_grid_thw=image_grid_thw, + video_grid_thw=video_grid_thw, + context_len=context_len, + seq_len=seq_len, + ) + else: + return cls._vl_get_input_positions_tensor( + input_tokens=input_tokens, + hf_config=hf_config, + image_grid_thw=image_grid_thw, + video_grid_thw=video_grid_thw, + second_per_grid_ts=second_per_grid_ts, + context_len=context_len, + seq_len=seq_len, + ) + + @classmethod + def _glm4v_get_input_positions_tensor( + cls, + input_tokens: list[int], + hf_config: PretrainedConfig, + image_grid_thw: Union[list[list[int]], torch.Tensor], + video_grid_thw: Union[list[list[int]], torch.Tensor], + context_len: int = 0, + seq_len: Optional[int] = None, + ) -> tuple[torch.Tensor, int]: + """Get mrope input positions and delta value for GLM4V.""" + + image_token_id = hf_config.image_token_id + video_start_token_id = hf_config.video_start_token_id + video_end_token_id = hf_config.video_end_token_id + spatial_merge_size = hf_config.vision_config.spatial_merge_size + llm_pos_ids_list: list = [] + + if not (image_grid_thw is None and video_grid_thw is None): + if isinstance(image_grid_thw, torch.Tensor): + image_grid_thw = image_grid_thw.tolist() + + input_token_type: list[str] = [] + video_check_flg = False + for token in input_tokens: + if token == video_start_token_id: + video_check_flg = True + elif token == video_end_token_id: + video_check_flg = False + + if (token == image_token_id) and (video_check_flg is False): + input_token_type.append("image") + elif (token == image_token_id) and (video_check_flg is True): + input_token_type.append("video") + else: + input_token_type.append("text") + + input_type_group: list[tuple[str, int, int]] = [] + for key, group_iter in itertools.groupby( + enumerate(input_token_type), lambda x: x[1]): + group_list = list(group_iter) + start_index = group_list[0][0] + end_index = group_list[-1][0] + 1 + input_type_group.append((key, start_index, end_index)) + + video_frame_num = 1 + mm_data_idx = 0 + for modality_type, start_idx, end_idx in input_type_group: + st_idx = llm_pos_ids_list[-1].max() + 1 if len( + llm_pos_ids_list) > 0 else 0 + if modality_type == "image": + t, h, w = ( + image_grid_thw[mm_data_idx][0], + image_grid_thw[mm_data_idx][1], + image_grid_thw[mm_data_idx][2], + ) + llm_grid_t, llm_grid_h, llm_grid_w = \ + t, h // spatial_merge_size, w // spatial_merge_size + + t_index = torch.arange(llm_grid_t).view(-1, 1).expand( + -1, llm_grid_h * llm_grid_w).flatten() + h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand( + llm_grid_t, -1, llm_grid_w).flatten() + w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand( + llm_grid_t, llm_grid_h, -1).flatten() + llm_pos_ids_list.append( + torch.stack([t_index, h_index, w_index]) + st_idx) + mm_data_idx += 1 + + elif modality_type == "video": + t, h, w = ( + video_frame_num, + image_grid_thw[mm_data_idx][1], + image_grid_thw[mm_data_idx][2], + ) + llm_grid_t, llm_grid_h, llm_grid_w = \ + t, h // spatial_merge_size, w // spatial_merge_size + + for t_idx in range(llm_grid_t): + t_index = torch.tensor(t_idx).view(-1, 1).expand( + -1, llm_grid_h * llm_grid_w).flatten() + h_index = torch.arange(llm_grid_h).view( + 1, -1, 1).expand(1, -1, llm_grid_w).flatten() + w_index = torch.arange(llm_grid_w).view( + 1, 1, -1).expand(1, llm_grid_h, -1).flatten() + llm_pos_ids_list.append( + torch.stack([t_index, h_index, w_index]) + st_idx) + + mm_data_idx += 1 + video_frame_num += 1 + + else: + text_len = end_idx - start_idx + llm_pos_ids_list.append( + torch.arange(text_len).view(1, -1).expand(3, -1) + + st_idx) + video_frame_num = 1 + + else: + text_len = len(input_tokens) + llm_pos_ids_list.append( + torch.arange(text_len).view(1, -1).expand(3, -1)) + + llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1) + llm_positions = llm_positions[:, context_len:seq_len] + mrope_position_delta = (llm_positions.max() + 1 - + len(input_tokens)).item() + return llm_positions, mrope_position_delta + + @classmethod + def _vl_get_input_positions_tensor( + cls, + input_tokens: list[int], + hf_config: PretrainedConfig, + image_grid_thw: Union[list[list[int]], torch.Tensor], + video_grid_thw: Union[list[list[int]], torch.Tensor], + second_per_grid_ts: list[float], + context_len: int = 0, + seq_len: Optional[int] = None, + ) -> tuple[torch.Tensor, int]: + """Get mrope input positions and delta value.""" + + image_token_id = hf_config.image_token_id + video_token_id = hf_config.video_token_id + vision_start_token_id = hf_config.vision_start_token_id + spatial_merge_size = hf_config.vision_config.spatial_merge_size + tokens_per_second = getattr(hf_config.vision_config, + "tokens_per_second", 1.0) + + input_tokens_tensor = torch.tensor(input_tokens) + vision_start_indices = torch.argwhere( + input_tokens_tensor == vision_start_token_id).squeeze(1) + vision_tokens = input_tokens_tensor[vision_start_indices + 1] + image_nums = (vision_tokens == image_token_id).sum() + video_nums = (vision_tokens == video_token_id).sum() + llm_pos_ids_list: list = [] + + st = 0 + remain_images, remain_videos = image_nums, video_nums + + image_index, video_index = 0, 0 + for _ in range(image_nums + video_nums): + video_second_per_grid_t = 0.0 + if image_token_id in input_tokens and remain_images > 0: + ed_image = input_tokens.index(image_token_id, st) + else: + ed_image = len(input_tokens) + 1 + if video_token_id in input_tokens and remain_videos > 0: + ed_video = input_tokens.index(video_token_id, st) + else: + ed_video = len(input_tokens) + 1 + if ed_image < ed_video: + t, h, w = ( + image_grid_thw[image_index][0], + image_grid_thw[image_index][1], + image_grid_thw[image_index][2], + ) + image_index += 1 + remain_images -= 1 + ed = ed_image + else: + t, h, w = ( + video_grid_thw[video_index][0], + video_grid_thw[video_index][1], + video_grid_thw[video_index][2], + ) + video_second_per_grid_t = 1.0 + if second_per_grid_ts: + video_second_per_grid_t = second_per_grid_ts[video_index] + video_index += 1 + remain_videos -= 1 + ed = ed_video + + llm_grid_t, llm_grid_h, llm_grid_w = \ + t, h // spatial_merge_size, w // spatial_merge_size + text_len = ed - st + + st_idx = llm_pos_ids_list[-1].max() + 1 if len( + llm_pos_ids_list) > 0 else 0 + llm_pos_ids_list.append( + torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx) + + t_index = (torch.arange(llm_grid_t).view(-1, 1).expand( + -1, llm_grid_h * llm_grid_w) * video_second_per_grid_t * + tokens_per_second).long().flatten() + + h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand( + llm_grid_t, -1, llm_grid_w).flatten() + w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand( + llm_grid_t, llm_grid_h, -1).flatten() + llm_pos_ids_list.append( + torch.stack([t_index, h_index, w_index]) + text_len + st_idx) + st = ed + llm_grid_t * llm_grid_h * llm_grid_w + + if st < len(input_tokens): + st_idx = llm_pos_ids_list[-1].max() + 1 if len( + llm_pos_ids_list) > 0 else 0 + text_len = len(input_tokens) - st + llm_pos_ids_list.append( + torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx) + + llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1) + mrope_position_delta = (llm_positions.max() + 1 - + len(input_tokens)).item() + llm_positions = llm_positions[:, context_len:seq_len] + + return llm_positions, mrope_position_delta + + @classmethod + def _omni_get_input_positions_tensor( + cls, + input_tokens: list[int], + hf_config: PretrainedConfig, + image_grid_thw: Union[list[list[int]], torch.Tensor], + video_grid_thw: Union[list[list[int]], torch.Tensor], + second_per_grid_ts: Optional[list[float]] = None, + context_len: int = 0, + seq_len: Optional[int] = None, + audio_feature_lengths: Optional[torch.Tensor] = None, + use_audio_in_video: bool = False, + ) -> tuple[torch.Tensor, int]: + """Get mrope input positions and delta value (Qwen2.5-Omni version). + + Differences from MRotaryEmbedding: + 1. Add audio support (and related `audio_feature_lengths`). + 2. Add `use_audio_in_video` option to read audio from video inputs. + In this case, audio and vision position ids will be split into + chunks and interleaved. + + Example: + + (V_i are vision position ids, A_i are audio position ids) + + |V_1 ... V_n|A_1 ... A_n|V_n+1 ... V_2n|A_n+1 ... A_2n|... + |vision chunk 1|audio chunk 1|vision chunk 2|audio chunk 2 |... + """ + + # TODO(fyabc): refactor and share more code with + # _vl_get_input_positions_tensor. + + thinker_config = hf_config.thinker_config + audio_token_id = thinker_config.audio_token_index + image_token_id = thinker_config.image_token_index + video_token_id = thinker_config.video_token_index + audio_start_token_id = thinker_config.audio_start_token_id + audio_end_token_id = thinker_config.audio_end_token_id + vision_start_token_id = thinker_config.vision_start_token_id + vision_end_token_id = thinker_config.vision_end_token_id + seconds_per_chunk = thinker_config.seconds_per_chunk + spatial_merge_size = thinker_config.vision_config.spatial_merge_size + tokens_per_second = getattr(thinker_config.vision_config, + "tokens_per_second", 25) + + if isinstance(image_grid_thw, list): + image_grid_thw = torch.tensor(image_grid_thw) + if isinstance(video_grid_thw, list): + video_grid_thw = torch.tensor(video_grid_thw) + + src_item = input_tokens + audio_seqlens = audio_feature_lengths + if not second_per_grid_ts: + second_per_grid_ts = [1] * video_grid_thw.shape[0] + audio_idx = 0 + video_idx = 0 + image_idx = 0 + new_src_item: list[int] = [] + llm_pos_ids_list: list[torch.Tensor] = [] + + idx = 0 + while idx < len(src_item): + new_src_item_len = len(new_src_item) + start_idx = llm_pos_ids_list[-1].max() + 1 if len( + llm_pos_ids_list) > 0 else 0 + if src_item[idx] not in [ + audio_token_id, video_token_id, image_token_id + ]: + if use_audio_in_video and idx > 0: + if src_item[idx] == vision_end_token_id and \ + src_item[idx - 1] == audio_end_token_id: + # processing the <|audio_eos|> before <|vision_eos|> + start_idx -= 1 + elif src_item[idx] == audio_start_token_id and \ + src_item[idx - 1] == vision_start_token_id: + # processing the <|audio_bos|> after <|vision_eos|> + start_idx -= 1 + new_src_item.append(src_item[idx]) + llm_pos_ids = torch.tensor([start_idx], + dtype=torch.long).expand(3, -1) + llm_pos_ids_list.append(llm_pos_ids) + elif src_item[idx] == audio_token_id: + assert audio_seqlens is not None + audio_seqlen = audio_seqlens[audio_idx] + place_num = (((audio_seqlen - 1) // 2 + 1 - 2) // 2 + 1) + new_src_item.extend([audio_token_id] * place_num) + llm_pos_ids = torch.arange(place_num).expand(3, -1) + start_idx + llm_pos_ids_list.append(llm_pos_ids) + audio_idx += 1 + elif src_item[idx] == image_token_id: + grid_t = image_grid_thw[image_idx][0] + grid_hs = image_grid_thw[:, 1] + grid_ws = image_grid_thw[:, 2] + t_index = (torch.arange(grid_t) * 1 * tokens_per_second).long() + llm_pos_ids = cls._get_llm_pos_ids_for_vision( + start_idx, image_idx, spatial_merge_size, t_index, grid_hs, + grid_ws) + llm_pos_ids_list.append(llm_pos_ids) + vision_seqlen = image_grid_thw[image_idx].prod() // ( + spatial_merge_size**2) + new_src_item.extend([image_token_id] * vision_seqlen) + image_idx += 1 + elif src_item[idx] == video_token_id and not use_audio_in_video: + grid_t = video_grid_thw[video_idx][0] + grid_hs = video_grid_thw[:, 1] + grid_ws = video_grid_thw[:, 2] + t_index = (torch.arange(grid_t) * + second_per_grid_ts[video_idx] * + tokens_per_second).long() + llm_pos_ids = cls._get_llm_pos_ids_for_vision( + start_idx, video_idx, spatial_merge_size, t_index, grid_hs, + grid_ws) + llm_pos_ids_list.append(llm_pos_ids) + vision_seqlen = video_grid_thw[video_idx].prod() // ( + spatial_merge_size**2) + new_src_item.extend([video_token_id] * vision_seqlen) + video_idx += 1 + else: + # read audio from video + assert audio_seqlens is not None + audio_seqlen = audio_seqlens[audio_idx] + vision_seqlen = video_grid_thw[video_idx].prod() // ( + spatial_merge_size**2) + grid_t = video_grid_thw[video_idx][0] + grid_h = video_grid_thw[video_idx][1] + grid_w = video_grid_thw[video_idx][2] + grid_hs = video_grid_thw[:, 1] + grid_ws = video_grid_thw[:, 2] + t_ntoken_per_chunk = int(tokens_per_second * seconds_per_chunk) + t_index = (torch.arange(grid_t) * + second_per_grid_ts[video_idx] * + tokens_per_second).long() + t_index_split_chunk = cls._split_list_into_ranges( + t_index, t_ntoken_per_chunk) + place_num = (((audio_seqlen - 1) // 2 + 1 - 2) // 2 + 1) + 2 + pure_audio_len = place_num - 2 + added_audio_len = 0 + audio_llm_pos_ids_list: list[torch.Tensor] = [] + for t_chunk in t_index_split_chunk: + vision_ntoken_per_chunk = len( + t_chunk) * grid_h * grid_w // (spatial_merge_size**2) + new_src_item.extend([video_token_id] * + vision_ntoken_per_chunk) + vision_llm_pos_ids_list = cls._get_llm_pos_ids_for_vision( + start_idx, video_idx, spatial_merge_size, t_chunk, + grid_hs, grid_ws).split(1, dim=1) + llm_pos_ids_list.extend(vision_llm_pos_ids_list) + new_src_item.extend( + min(t_ntoken_per_chunk, pure_audio_len - + added_audio_len) * [audio_token_id]) + audio_start_idx = start_idx if len( + audio_llm_pos_ids_list + ) == 0 else audio_llm_pos_ids_list[-1][0].item() + 1 + if min(t_ntoken_per_chunk, + pure_audio_len - added_audio_len) > 0: + audio_llm_pos_ids_list = (torch.arange( + min(t_ntoken_per_chunk, pure_audio_len - + added_audio_len)).expand(3, -1) + + audio_start_idx).split(1, + dim=1) + else: + audio_llm_pos_ids_list = [] + added_audio_len += min(t_ntoken_per_chunk, + pure_audio_len - added_audio_len) + llm_pos_ids_list.extend(audio_llm_pos_ids_list) + if added_audio_len < pure_audio_len: + new_src_item.extend( + (pure_audio_len - added_audio_len) * [audio_token_id]) + audio_llm_pos_ids_list = ( + torch.arange(pure_audio_len - added_audio_len).expand( + 3, -1) + llm_pos_ids_list[-1].max() + 1).split( + 1, dim=1) + llm_pos_ids_list.extend(audio_llm_pos_ids_list) + audio_idx += 1 + video_idx += 1 + # move to the next token + idx += len(new_src_item) - new_src_item_len + + llm_positions = torch.cat(llm_pos_ids_list, dim=1) + mrope_position_delta = torch.cat(llm_pos_ids_list, + dim=1).max() + 1 - len(src_item) + llm_positions = llm_positions[:, context_len:seq_len] + + return llm_positions, mrope_position_delta + + @staticmethod + def _get_llm_pos_ids_for_vision( + start_idx: int, + vision_idx: int, + spatial_merge_size: int, + t_index: list[int], + grid_hs: torch.Tensor, + grid_ws: torch.Tensor, + ) -> torch.Tensor: + llm_pos_ids_list = [] + llm_grid_h = grid_hs[vision_idx] // spatial_merge_size + llm_grid_w = grid_ws[vision_idx] // spatial_merge_size + h_index = (torch.arange(llm_grid_h).view(1, -1, 1).expand( + len(t_index), -1, llm_grid_w).flatten()) + w_index = (torch.arange(llm_grid_w).view(1, 1, -1).expand( + len(t_index), llm_grid_h, -1).flatten()) + t_index_tensor = torch.Tensor(t_index).to(llm_grid_h.device).view( + -1, 1).expand(-1, llm_grid_h * llm_grid_w).long().flatten() + _llm_pos_ids = torch.stack([t_index_tensor, h_index, w_index]) + llm_pos_ids_list.append(_llm_pos_ids + start_idx) + llm_pos_ids = torch.cat(llm_pos_ids_list, dim=1) + return llm_pos_ids + + @staticmethod + def _split_list_into_ranges(lst: torch.Tensor, + interval: int) -> list[list[int]]: + ranges: list[list[int]] = [[] + for _ in range((max(lst) // interval) + 1)] + for num in lst: + index = num // interval + ranges[index].append(num) + return ranges + + @staticmethod + def get_next_input_positions( + mrope_position_delta: int, + context_len: int, + seq_len: int, + ) -> list[list[int]]: + return [ + list( + range(context_len + mrope_position_delta, + seq_len + mrope_position_delta)) for _ in range(3) + ] + + @staticmethod + def get_next_input_positions_tensor(out: np.ndarray, out_offset: int, + mrope_position_delta: int, + context_len: int, num_new_tokens: int): + + values = np.arange(mrope_position_delta + context_len, + mrope_position_delta + context_len + num_new_tokens, + dtype=out.dtype) + out[:, out_offset:out_offset + num_new_tokens] = values + + @classmethod + def omni_get_updates_use_audio_in_video( + cls, + thinker_config: PretrainedConfig, + audio_len: int, + video_grid_thw: Union[list[int], torch.Tensor], + video_second_per_grid_t: float, + ) -> list[int]: + """Get video prompt updates when `use_audio_in_video` is True. + + In this case, audio and vision update ids will be split into + chunks and interleaved (details in `_omni_get_input_positions_tensor`). + + <|video_bos|><|VIDEO|><|video_eos|> => + <|video_bos|><|audio_bos|>(... chunks ...)<|audio_eos|><|video_eos|> + """ + + audio_token_id = thinker_config.audio_token_index + video_token_id = thinker_config.video_token_index + audio_start_token_id = thinker_config.audio_start_token_id + audio_end_token_id = thinker_config.audio_end_token_id + seconds_per_chunk = thinker_config.seconds_per_chunk + spatial_merge_size = thinker_config.vision_config.spatial_merge_size + tokens_per_second = getattr(thinker_config.vision_config, + "tokens_per_second", 25) + + grid_t = video_grid_thw[0] + grid_h = video_grid_thw[1] + grid_w = video_grid_thw[2] + t_ntoken_per_chunk = int(tokens_per_second * seconds_per_chunk) + t_index = (torch.arange(grid_t) * video_second_per_grid_t * + tokens_per_second).long() + t_index_split_chunk = cls._split_list_into_ranges( + t_index, t_ntoken_per_chunk) + + updates = [audio_start_token_id] + added_audio_len = 0 + for t_chunk in t_index_split_chunk: + vision_ntoken_per_chunk = len(t_chunk) * grid_h * grid_w // ( + spatial_merge_size**2) + updates.extend([video_token_id] * vision_ntoken_per_chunk) + + audio_chunk_size = min(t_ntoken_per_chunk, + audio_len - added_audio_len) + updates.extend(audio_chunk_size * [audio_token_id]) + added_audio_len += audio_chunk_size + if added_audio_len < audio_len: + updates.extend((audio_len - added_audio_len) * [audio_token_id]) + updates.extend([audio_end_token_id]) + + return updates diff --git a/vllm_omni/model_executor/models/qwen2_5_omni.py b/vllm_omni/model_executor/models/qwen2_5_omni.py new file mode 100644 index 000000000..b99f2bec3 --- /dev/null +++ b/vllm_omni/model_executor/models/qwen2_5_omni.py @@ -0,0 +1,706 @@ +from functools import cached_property +from typing import Iterable, List, Optional, Set, Tuple, Union, NamedTuple, Dict + +import os +import glob +import numpy as np +import torch +import torch.nn as nn +from transformers.models.qwen2_5_omni.configuration_qwen2_5_omni import ( + Qwen2_5OmniConfig, Qwen2_5OmniThinkerConfig, Qwen2_5OmniTalkerConfig) + +from vllm.config import VllmConfig +from vllm.logger import init_logger +from vllm.model_executor.layers.linear import ColumnParallelLinear +from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler +from vllm.model_executor.sampling_metadata import SamplingMetadata +from vllm.model_executor.models.qwen2_5_omni_thinker import ( + Qwen2_5OmniConditionalGenerationMixin, + Qwen2_5OmniThinkerMultiModalProcessor, + Qwen2_5OmniThinkerProcessingInfo, + Qwen2_5OmniThinkerDummyInputsBuilder) +# from vllm.model_executor.models.qwen2_code2wav_dit import Qwen2Code2wav +from vllm.multimodal import MULTIMODAL_REGISTRY +from vllm.sequence import IntermediateTensors + +from vllm.model_executor.models.interfaces import SupportsMultiModal, SupportsPP +from vllm.model_executor.models.utils import (AutoWeightsLoader, WeightsMapper, + init_vllm_registered_model, + maybe_prefix) +from vllm.model_executor.model_loader.weight_utils import download_weights_from_hf +from vllm_omni.model_executor.models.utils import add_prefix_to_loaded_weights + +class OmniOutput(NamedTuple): + """Output from the merged Omni model containing both text and audio.""" + text_hidden_states: torch.Tensor + multimodal_outputs: dict = {} + intermediate_tensors: Optional[IntermediateTensors] = None + +logger = init_logger(__name__) + +@MULTIMODAL_REGISTRY.register_processor( + Qwen2_5OmniThinkerMultiModalProcessor, + info=Qwen2_5OmniThinkerProcessingInfo, + dummy_inputs=Qwen2_5OmniThinkerDummyInputsBuilder, +) +class Qwen2_5OmniForConditionalGeneration(nn.Module, SupportsMultiModal, + SupportsPP, + Qwen2_5OmniConditionalGenerationMixin): + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + self.have_multimodal_outputs = True + config: Qwen2_5OmniConfig = vllm_config.model_config.hf_config + quant_config = vllm_config.quant_config + multimodal_config = vllm_config.model_config.multimodal_config + # keep vllm_config for later submodule init + self.vllm_config = vllm_config + + # Initialize thinker components + thinker_config: Qwen2_5OmniThinkerConfig = config.thinker_config + self.thinker_config = thinker_config + self.multimodal_config = multimodal_config + + # Initialize talker components + talker_config: Qwen2_5OmniTalkerConfig = config.talker_config + self.talker_config = talker_config + + + + self.model_stage = vllm_config.model_config.model_stage + if self.model_stage=="thinker": + # Initialize thinker model (multimodal processing) + self.thinker = init_vllm_registered_model( + vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "thinker"), + hf_config=thinker_config, + # Use registry architecture key + architectures=["Qwen2_5OmniThinkerModel"], + ) + self.model = self.thinker + self.talker = None + self.token2wav = None + + elif self.model_stage=="talker": + self.thinker = None + # Initialize talker model wrapper (handles projection + LM) + self.talker = init_vllm_registered_model( + vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "talker"), + hf_config=talker_config, + # Use registry architecture key + architectures=["Qwen2_5OmniTalkerModel"], + ) + self.talker.init_multi_modal(thinker_config) + self.model=self.talker + self.token2wav = None + + elif self.model_stage=="code2wav": + self.thinker = None + self.talker = None + # Initialize token2wav (code->mel->wav) like thinker/talker + self.token2wav_config = getattr(config, 'token2wav_config', None) + self.token2wav = None + if self.token2wav_config is not None: + self.token2wav = init_vllm_registered_model( + vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "token2wav"), + hf_config=self.token2wav_config, + architectures=["Qwen2_5OmniToken2WavModel"], + ) + # voice resources (loaded on demand) + self._token2wav_conds: Dict[str, torch.Tensor] = {} + self._token2wav_ref_mels: Dict[str, torch.Tensor] = {} + self.model = self.token2wav + else: + raise ValueError("Invalid model stage") + + # Set up intermediate tensors + self.make_empty_intermediate_tensors = ( + self.thinker.make_empty_intermediate_tensors) if self.model_stage=="thinker" else lambda: None + + # -------------------- Device utilities -------------------- + @staticmethod + def _module_device(module: nn.Module) -> torch.device: + try: + return next(module.parameters()).device + except StopIteration: + # No parameters; fall back to buffers or cpu + for _, buf in module.named_buffers(recurse=True): + return buf.device + return torch.device("cpu") + + def move_submodules_to_devices( + self, + *, + thinker_device: Optional[Union[str, torch.device]] = None, + talker_device: Optional[Union[str, torch.device]] = None, + token2wav_device: Optional[Union[str, torch.device]] = None, + ) -> None: + """Optionally move thinker/talker/token2wav to different devices. + + Example: + model.move_submodules_to_devices( + thinker_device='cuda:0', + talker_device='cuda:1', + token2wav_device='cpu', + ) + """ + if thinker_device is not None and self.thinker is not None: + self.thinker.to(thinker_device) + if talker_device is not None and self.talker is not None: + self.talker.to(talker_device) + if token2wav_device is not None and self.token2wav is not None: + self.token2wav.to(token2wav_device) + + @cached_property + def sampler(self): + if hasattr(self.model, "sampler"): + return self.model.sampler + return get_sampler() + + def get_input_embeddings( + self, + input_ids: torch.Tensor, + multimodal_embeddings=None, + ) -> torch.Tensor: + if self.model_stage == "code2wav": + return torch.zeros_like(input_ids).reshape(-1, 1).repeat(1, self.vllm_config.model_config.get_hidden_size()) + return self.model.get_input_embeddings( + input_ids, multimodal_embeddings) + + + def get_multimodal_embeddings(self, **kwargs): + # Delegate to thinker model for multimodal processing + return self.model.get_multimodal_embeddings(**kwargs) + + def last_index_of(self, list, value): + return len(list) - 1 - list[::-1].index(value) + + def forward( + self, + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors] = None, + inputs_embeds: Optional[torch.Tensor] = None, + generate_audio: bool = True, + voice_type: str = "Chelsie", + codec: Optional[torch.Tensor] = None, + sampling_metadata: Optional[SamplingMetadata] = None, + logits_index: Optional[int] = None, + sampler = None, + additional_information: Optional[dict[str, object]] = None, + **kwargs: object, + ) -> Union[torch.Tensor, IntermediateTensors, OmniOutput]: + """ + Workflow: + 1) Thinker: multimodal understanding → text hidden states. + 2) If audio requested and codec not provided, use talker to derive codec. + 3) If audio requested (or codec provided), use token2wav to synthesize waveform. + 4) Return text hidden states (and audio when applicable). + """ + if self.model_stage=="thinker": + # Normalize to batched inputs if caller provides 1D/2D unbatched tensors + added_batch_dim = False + if input_ids is not None and input_ids.ndim == 1: + input_ids = input_ids.unsqueeze(0) + added_batch_dim = True + if positions is not None and positions.ndim == 1: + positions = positions.unsqueeze(0) + added_batch_dim = True + if inputs_embeds is not None and inputs_embeds.ndim == 2: + inputs_embeds = inputs_embeds.unsqueeze(0) + added_batch_dim = True + thinker_dev = self._module_device(self.thinker) + + #if input_ids is None, set it to a zero tensor, in the length of the same as the embedding seq length + if input_ids is None: + input_ids = torch.zeros(inputs_embeds.shape[1], dtype=torch.long, device=thinker_dev).unsqueeze(0) #(1, 0) + added_batch_dim = True + + # 1) Thinker (ensure inputs on thinker's device) + if input_ids is not None and input_ids.device != thinker_dev: + input_ids = input_ids.to(thinker_dev) + if positions is not None and positions.device != thinker_dev: + positions = positions.to(thinker_dev) + if inputs_embeds is not None and inputs_embeds.device != thinker_dev: + inputs_embeds = inputs_embeds.to(thinker_dev) + # Run thinker + thinker_output = self.thinker( + input_ids=input_ids, + positions=positions[0], + intermediate_tensors=intermediate_tensors, + inputs_embeds=inputs_embeds, + **kwargs, + ) + + if isinstance(thinker_output, tuple): + embeds, text_hidden_states = thinker_output + else: + text_hidden_states = thinker_output + + # Text-only path + return OmniOutput( + text_hidden_states=text_hidden_states.squeeze(0) if added_batch_dim else text_hidden_states, + multimodal_outputs=None + ) + + # 2) Talker (if codec not provided) + if self.model_stage=="talker": + + if input_ids is None and additional_information is None: + input_ids = torch.zeros(inputs_embeds.shape[0], dtype=torch.long, device=inputs_embeds.device) + additional_information = {} + self.thinker_reply_part = torch.zeros_like(inputs_embeds) + is_profile = True + else: + is_profile = False + + if input_ids is not None and additional_information is not None and not is_profile: + # read from additional_information dict + thinker_result = None + if additional_information is not None and isinstance(additional_information, dict): + thinker_result = additional_information.get("thinker_result") + prompt_embeds = additional_information.get("prompt_embeds") + prompt_token_ids = additional_information.get("prompt_token_ids") + thinker_output_token_ids = additional_information.get("thinker_output_token_ids") + else: + thinker_result = torch.zeros_like(inputs_embeds) + prompt_embeds = torch.zeros_like(inputs_embeds) + prompt_token_ids = torch.zeros(inputs_embeds.shape[0], dtype=torch.int64, device=inputs_embeds.device) + thinker_output_token_ids = torch.zeros(inputs_embeds.shape[0], dtype=torch.int64, device=inputs_embeds.device) + + if thinker_result is None: + thinker_result = torch.zeros_like(inputs_embeds) + self.thinker_reply_part = thinker_result.squeeze(0) + if self.thinker_reply_part.shape[1] > 1: + self.thinker_reply_part = self.thinker_reply_part[1:, :] + input_ids, inputs_embeds = self._thinker_to_talker_prefill( + voice_type=voice_type, + output_prompt_embeds=thinker_result, + output_token_ids = thinker_output_token_ids, + thinker_prompt_embeds=prompt_embeds, + prompt_token_ids = prompt_token_ids, + ) + elif not is_profile: + input_ids, inputs_embeds = \ + self._thinker_to_talker_decode_one_step( + output_prompt_embeds=self.thinker_reply_part[:1] if self.thinker_reply_part.shape[0]>=1 \ + else torch.zeros(1, self.thinker_reply_part.shape[1]).to(self._module_device(self.model)).to(torch.bfloat16)+(-1.25*2**(-123)), + output_token_ids=input_ids, + ) + + if self.thinker_reply_part.shape[0] >=1: + self.thinker_reply_part = self.thinker_reply_part[1:, :] + + with torch.inference_mode(): + talker_hidden = self.talker( + input_ids=input_ids, + positions=positions[0], + inputs_embeds=inputs_embeds, + + ) + + return OmniOutput( + text_hidden_states=talker_hidden, + multimodal_outputs=None + ) + + if self.model_stage=="code2wav": + code = input_ids if input_ids is not None else torch.zeros(inputs_embeds.shape[0], dtype=torch.long, device=inputs_embeds.device) + audio_tensor = self.generate_audio(code[:-1] if code[-1]==8294 else code, voice_type) + return OmniOutput( + text_hidden_states = None, + multimodal_outputs = { + "audio": audio_tensor + } + ) + + return OmniOutput( + text_hidden_states=torch.cat( + [ + torch.zeros([inputs_embeds.shape[0],896], dtype=torch.bfloat16).to(self._module_device(self.model)), + self.talker.thinker_to_talker_proj(self.talker.get_input_embeddings(torch.tensor([8294,8293]).to(torch.bfloat16).to(self._module_device(self.model))))[0] + ], + dim=0), + multimodal_outputs=None + ) + + def generate_audio(self, code, voice_type): + token2wav_dev = self._module_device(self.token2wav) + if isinstance(code, torch.Tensor): + code_tensor = code.to(dtype=torch.long, device=token2wav_dev) + else: + code_tensor = torch.as_tensor(code, dtype=torch.long, device=token2wav_dev) + if code_tensor.ndim == 2 and code_tensor.shape[0] == 1: + code_tensor = code_tensor.squeeze(0) + + audio_tensor = self._codec_to_audio(code_tensor, voice_type) + + return audio_tensor + + def _load_talker_embedding( + self, + ) -> torch.nn.Embedding: + return self.talker.language_model.model.embed_tokens + + def _init_special_tokens_embeddings( + self, + ): + # talker embeddings + self.talker_embedding = self._load_talker_embedding() + + # embed_text_bos_token + self.tts_text_spk_token_ids = { + # M02:我是个会说标准普通话、带部分北方口音的男声 + 'm02': 151870, + 'Ethan': 151870, + + # F030:我是你的二次元虚拟女友 + 'f030': 151872, + 'Chelsie': 151872, + } + self.default_tts_text_spk_type = list( + self.tts_text_spk_token_ids.keys())[0] + self.tts_text_spk_token_ids['prefix_caching'] = 151870 + + talker_hf_config = self.talker_config + if hasattr(talker_hf_config, 'talker_config'): + talker_hf_config = talker_hf_config.talker_config + + self.embed_text_bos_token = self.thinker_embedding( + torch.tensor( + [talker_hf_config.tts_text_start_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + self.embed_text_spk_tokens = { + key: + self.thinker_embedding( + torch.tensor( + [value], + dtype=torch.long, + device=self._module_device(self.talker), + )) + for key, value in self.tts_text_spk_token_ids.items() + } + self.embed_text_eos_token = self.thinker_embedding( + torch.tensor( + [talker_hf_config.tts_text_end_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + self.embed_text_pad_token = self.thinker_embedding( + torch.tensor( + [talker_hf_config.tts_text_pad_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + self.embed_codec_bos_token = self.talker_embedding( + torch.tensor( + [talker_hf_config.tts_codec_start_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + self.embed_codec_eos_token = self.talker_embedding( + torch.tensor( + [talker_hf_config.tts_codec_end_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + self.embed_codec_pad_token = self.talker_embedding( + torch.tensor( + [talker_hf_config.tts_codec_pad_token_id], + dtype=torch.long, + device=self._module_device(self.talker), + )) + return set(["thinker_embedding.weight", "talker_embedding.weight"]) + + def _get_embed_text_spk_token(self, voice_type: str): + if voice_type not in self.embed_text_spk_tokens: + return self.embed_text_bos_token + return self.embed_text_spk_tokens[voice_type] + + def _get_text_spk_token_id(self, voice_type: str): + talker_hf_config = self.talker_config + if hasattr(talker_hf_config, 'talker_config'): + talker_hf_config = talker_hf_config.talker_config + + if voice_type not in self.tts_text_spk_token_ids: + return talker_hf_config.tts_text_start_token_id + return self.tts_text_spk_token_ids[voice_type] + + def _thinker_to_talker_prefill( + self, + voice_type: str, + output_prompt_embeds, + output_token_ids, + thinker_prompt_embeds, + prompt_token_ids + ): + + talker_hf_config = self.talker_config + if hasattr(talker_hf_config, 'talker_config'): + talker_hf_config = talker_hf_config.talker_config + + # if len(output.outputs[0].token_ids) == 2: + # issue request + prompt_embeds = torch.cat([ + thinker_prompt_embeds, + self._get_embed_text_spk_token(voice_type) + + self.embed_codec_pad_token, + output_prompt_embeds[:1] + self.embed_codec_bos_token, + ], + dim=0) + + prompt_token_ids_processed = prompt_token_ids + [ + talker_hf_config.tts_codec_pad_token_id, + output_token_ids[0], + + ] + input_tokens_len = len(prompt_token_ids_processed) + # the code below is from model runner in Qwen, may need to further discuss later + if input_tokens_len > 2: + prompt_token_ids_processed = ( + [self.talker_config.tts_codec_mask_token_id] * + (input_tokens_len - 2) + [ + self.talker_config.tts_codec_pad_token_id, + self.talker_config.tts_codec_start_token_id + ]) + else: + prompt_token_ids_processed = [ + self.talker_config.tts_codec_pad_token_id, + self.talker_config.tts_codec_start_token_id, + ][-input_tokens_len:] + if isinstance(prompt_token_ids_processed,list): + prompt_token_ids_processed = torch.Tensor(prompt_token_ids_processed).to(torch.int64).to(self._module_device(self.talker)) + return prompt_token_ids_processed, prompt_embeds + + def _thinker_to_talker_decode_one_step( + self, + output_prompt_embeds, + output_token_ids, + ): + processed_output_token_embeds = output_prompt_embeds + self.talker.get_input_embeddings(output_token_ids) #for decode + return output_token_ids, processed_output_token_embeds + + def compute_logits( + self, + hidden_states: Union[torch.Tensor, OmniOutput], + sampling_metadata: SamplingMetadata, + ) -> Optional[torch.Tensor]: + # Handle OmniOutput type + if isinstance(hidden_states, OmniOutput): + hidden_states = hidden_states.text_hidden_states + + # Use thinker model for logits computation + return self.model.compute_logits(hidden_states, sampling_metadata) + + def sample( + self, + logits: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[SamplerOutput]: + # Use thinker model for sampling + return self.model.sample(logits, sampling_metadata) + + def generate_speech(self, text_tokens: torch.Tensor, voice_type: str = "default"): + """ + Generate speech from text tokens using the talker and token2wav models. + This method is kept for backward compatibility and direct speech generation. + + Args: + text_tokens: Text tokens from thinker model + voice_type: Voice type for speech generation + + Returns: + Audio tensor + """ + # Generate codec tokens using talker model + talker_output = self.talker( + input_ids=None, + positions=None, + inputs_embeds=text_tokens + ) + + # Convert talker output to codec tokens + codec_tokens = self._convert_to_codec_tokens(talker_output) + + # Generate audio using token2wav model + return self._codec_to_audio(codec_tokens, voice_type=voice_type) + + + def _convert_to_codec_tokens(self, talker_output: torch.Tensor, sampling_metadata: SamplingMetadata) -> torch.Tensor: + """ + 参考 HF:使用 talker 的 codec 头得到 logits,抑制 BOS,再贪心选取当前步的下一个 codec token。 + """ + with torch.inference_mode(): + logits = self.talker.compute_logits(talker_output, None) + if logits is None: + return torch.zeros((talker_output.size(0), 0), dtype=torch.long, device=talker_output.device) + + # 仅抑制 codec_bos,与 HF generate 的 suppress_tokens 行为一致 + bos_id = None + if hasattr(self, 'talker_config') and hasattr(self.talker_config, 'tts_codec_start_token_id'): + bos_id = int(getattr(self.talker_config, 'tts_codec_start_token_id')) + if bos_id is not None: + logits[..., bos_id] = -1e9 + + # 取最后一步位置的分布并贪心选取 + next_id = self.talker.sample(logits, sampling_metadata).sampled_token_ids + return next_id.to(dtype=torch.long) + + def _init_token2wav_model(self): + """Initialize speaker resources if provided; model is constructed in __init__.""" + if self.token2wav is None or self.token2wav_config is None: + return + device = 'cuda' if torch.cuda.is_available() else 'cpu' + # optional speaker resources + conds = getattr(self.token2wav_config, 'conds', None) + ref_mels = getattr(self.token2wav_config, 'ref_mels', None) + if isinstance(conds, dict) and isinstance(ref_mels, dict): + self._token2wav_conds = {k: torch.as_tensor(v, device=device) for k, v in conds.items()} + self._token2wav_ref_mels = {k: torch.as_tensor(v, device=device) for k, v in ref_mels.items()} + # legacy: load from directory if provided + model_path = self.vllm_config.model_config.model + if isinstance(model_path, str) and os.path.isdir(model_path): + spk_pt = os.path.join(model_path, 'spk_dict.pt') + if os.path.exists(spk_pt): + data = torch.load(spk_pt, map_location=device) + for key, value in data.items(): + self._token2wav_conds[key] = value["cond"].to(device) + self._token2wav_ref_mels[key] = value["ref_mel"].to(device) + else: + # legacy npy inputs + for f in sorted(glob.glob(os.path.join(model_path, 'inputs', '*spk_emb.npy'))): + key = os.path.basename(f).split('_')[0].lower() + self._token2wav_conds[key] = torch.as_tensor(np.load(f), device=device) + for f in sorted(glob.glob(os.path.join(model_path, 'inputs', '*ref_mel.npy'))): + key = os.path.basename(f).split('_')[0].lower() + self._token2wav_ref_mels[key] = torch.as_tensor(np.load(f), device=device) + + def _codec_to_audio(self, codec_tokens: torch.Tensor, voice_type: str = "default") -> Optional[torch.Tensor]: + if self.token2wav is None: + self._init_token2wav_model() + if self.token2wav is None: + return None + # Normalize voice type + voice = (voice_type or 'default') + # Resolve cond / ref_mel if provided + cond = None + ref_mel = None + if voice in self._token2wav_conds and voice in self._token2wav_ref_mels: + cond = self._token2wav_conds[voice] + ref_mel = self._token2wav_ref_mels[voice] + # Fallback: create dummy cond/ref_mel if not provided + token2wav_dev = self._module_device(self.token2wav) + if cond is None: + cond = torch.zeros((1, self.token2wav_config.dit_config.enc_emb_dim), device=token2wav_dev, dtype=torch.float32) + if ref_mel is None: + ref_mel = torch.zeros((1, 300, self.token2wav_config.dit_config.mel_dim), device=token2wav_dev, dtype=torch.float32) + + # Ensure codec is (1, T) long tensor on correct device + if isinstance(codec_tokens, torch.Tensor): + codec = codec_tokens.to(dtype=torch.long, device=token2wav_dev) + if codec.ndim == 1: + codec = codec.unsqueeze(0) + else: + codec = torch.as_tensor(codec_tokens, dtype=torch.long, device=token2wav_dev).unsqueeze(0) + + # Streaming with chunked process and boundary alignment (rely on token2wav.process_chunk) + factor = getattr(self.token2wav.token2wav.factor, 'factor', 2) + chunk_size = 48 + mel_dim = getattr(self.token2wav.token2wav.code2wav_dit_model, 'mel_dim', self.token2wav_config.dit_config.mel_dim) + total_mel = int(codec.shape[1] * factor) + steps = 10 + + # Prepare initial noise for the whole sequence + y_all = torch.randn((1, total_mel, mel_dim), dtype=ref_mel.dtype, device=token2wav_dev) + + logger.info(f"Currently, we do not use the chunked process, we only use the token2wav.process_chunk for the whole sequence.\ + The stream mode will be implemented in the future.") + + chunk_ends = [] + for i in range(codec.shape[1]): + chunk_code_length = i * 2 - 24 + finished = i==(codec.shape[1]-1) + if (chunk_code_length > 0 and + chunk_code_length % chunk_size == 0) or finished: + chunk_ends.append(i) + + # Number of chunks in mel domain + prev_generated = None + wav_chunks: list = [] + prev_id = 0 + + with torch.inference_mode(): + for n,i in enumerate([0]): + finished = (i == codec.shape[1] - 1) + _, audio_chunk = self.token2wav.process_chunk( + conditioning=cond, + reference_mel=ref_mel, + codec_all=codec, + y_all=y_all, + i=n, + steps=steps, + prev_generated=prev_generated if prev_generated is not None else [], + finished=True, + ) + prev_generated = audio_chunk + wav_chunks.append(audio_chunk.detach().cpu().numpy()) + prev_id = i + + if len(wav_chunks) == 0: + return torch.zeros(0, device=token2wav_dev) + + waveform = np.concatenate(wav_chunks) + return torch.as_tensor(waveform, device=token2wav_dev) + + def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]) -> Set[str]: + """Load weights for all components of the omni model.""" + loaded_weights = set() + thinker_weights = [] + talker_weights = [] + token2wav_weights = [] + for k, v in weights: + if k.startswith('thinker.'): + thinker_weights.append((k, v)) + elif k.startswith('talker.'): + talker_weights.append((k, v)) + elif k.startswith('token2wav.'): + token2wav_weights.append((k, v)) + else: + raise ValueError(f"Unknown weight prefix: {k}") + + # Load thinker weights + if self.thinker: + if thinker_weights: + thinker_loaded = self.thinker.load_weights(thinker_weights) + else: + thinker_loaded = set([k for k,v in thinker_weights]) + thinker_loaded = add_prefix_to_loaded_weights(thinker_loaded, 'thinker') + loaded_weights.update(thinker_loaded) + + + # Load talker weights + if talker_weights and self.talker is not None: + # Map talker weights to appropriate components + if self.thinker is None: + thinker_embedding_weights = [w for n, w in thinker_weights if n == "thinker.model.embed_tokens.weight"] + if thinker_embedding_weights: + self.thinker_embedding = nn.Embedding(thinker_embedding_weights[0].shape[0], thinker_embedding_weights[0].shape[1]) + self.thinker_embedding.weight = nn.Parameter(thinker_embedding_weights[0].to(self._module_device(self.talker))) + talker_loaded = self.talker.load_weights(talker_weights) + talker_loaded = add_prefix_to_loaded_weights(talker_loaded, 'talker') + loaded_weights.update(talker_loaded) + loaded_weights.update(self._init_special_tokens_embeddings()) + + + # Load token2wav weights (if any) + if token2wav_weights and self.token2wav is not None: + self._init_token2wav_model() + t2w_loaded = self.token2wav.load_weights(token2wav_weights, os.path.join(self.vllm_config.model_config.model, "spk_dict.pt")) + t2w_loaded = add_prefix_to_loaded_weights(t2w_loaded, 'token2wav') + loaded_weights.update(t2w_loaded) + + return loaded_weights \ No newline at end of file diff --git a/vllm_omni/model_executor/models/qwen2_5_omni_talker.py b/vllm_omni/model_executor/models/qwen2_5_omni_talker.py new file mode 100644 index 000000000..8e6f95d6f --- /dev/null +++ b/vllm_omni/model_executor/models/qwen2_5_omni_talker.py @@ -0,0 +1,239 @@ +from functools import cached_property +from typing import Iterable, List, Optional, Set, Tuple, Union + +import torch +import torch.nn as nn +from transformers.models.qwen2_5_omni.configuration_qwen2_5_omni import Qwen2_5OmniTalkerConfig +from transformers.models.qwen2_5_omni.modeling_qwen2_5_omni import Qwen2_5OmniAudioEncoder + +from vllm.attention import AttentionMetadata +from vllm.config import VllmConfig +from vllm.forward_context import ForwardContext, get_forward_context +from vllm.model_executor.layers.linear import ColumnParallelLinear +from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler +from vllm.model_executor.sampling_metadata import SamplingMetadata +from vllm.model_executor.models.qwen2_5_omni_thinker import ( + Qwen2_5OmniConditionalGenerationMixin, + Qwen2_5OmniThinkerMultiModalProcessor, + Qwen2_5OmniThinkerProcessingInfo, + Qwen2_5OmniThinkerDummyInputsBuilder) +from vllm.multimodal import MULTIMODAL_REGISTRY +from vllm.logger import init_logger +from vllm.sequence import IntermediateTensors + +from vllm.model_executor.models.interfaces import SupportsMultiModal, SupportsPP, MultiModalEmbeddings +from vllm.model_executor.models.utils import (AutoWeightsLoader, WeightsMapper, + init_vllm_registered_model, + maybe_prefix, + merge_multimodal_embeddings) +from vllm.model_executor.models.qwen2_5_vl import Qwen2_5_VisionTransformer + + +@MULTIMODAL_REGISTRY.register_processor( + Qwen2_5OmniThinkerMultiModalProcessor, + info=Qwen2_5OmniThinkerProcessingInfo, + dummy_inputs=Qwen2_5OmniThinkerDummyInputsBuilder, +) +class Qwen2_5OmniTalkerForConditionalGeneration(nn.Module, SupportsMultiModal, + SupportsPP, + Qwen2_5OmniConditionalGenerationMixin): + logger = init_logger(__name__) + # Align to thinker-style static mapper for clarity + hf_to_vllm_mapper = WeightsMapper( + orig_to_new_prefix={ + # text LM head/body in talker + "talker.codec_head.": "language_model.lm_head.", + "talker.model.": "language_model.model.", + # projection weights + "talker.thinker_to_talker_proj.": "thinker_to_talker_proj.", + # fallback root + "talker.": "", + }) + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + config: Qwen2_5OmniTalkerConfig = vllm_config.model_config.hf_config + quant_config = vllm_config.quant_config + + self.prefix = prefix + self.quant_config = quant_config + + if hasattr(config, "talker_config"): + self.config = config.talker_config + vllm_config.model_config.hf_text_config = \ + vllm_config.model_config.hf_config.talker_config + else: + self.config = config + + self.thinker_to_talker_proj = ColumnParallelLinear( + self.config.embedding_size, + self.config.hidden_size, + bias=True, + gather_output=True, + skip_bias_add=False, + quant_config=quant_config, + ) + self.language_model = init_vllm_registered_model( + vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "language_model"), + hf_config=getattr(self.config, 'text_config', self.config), + architectures=["Qwen2ForCausalLM_old"], + ) + self.make_empty_intermediate_tensors = ( + self.language_model.make_empty_intermediate_tensors) + + def init_multi_modal(self, thinker_config): + self.audio_tower = Qwen2_5OmniAudioEncoder(thinker_config.audio_config) + self.visual = Qwen2_5_VisionTransformer( + vision_config=thinker_config.vision_config, + norm_eps=getattr(thinker_config.text_config, "rms_norm_eps", 1e-6), + quant_config=self.quant_config, + prefix=maybe_prefix(self.prefix, "visual"), + ) + + def get_language_model(self) -> torch.nn.Module: + return self.language_model + + @cached_property + def sampler(self): + if hasattr(self.language_model, "sampler"): + return self.language_model.sampler + + return get_sampler() + + def get_input_embeddings( + self, + input_ids: torch.Tensor, + multimodal_embeddings: Optional[MultiModalEmbeddings] = None, + ) -> torch.Tensor: + inputs_embeds = self.language_model.get_input_embeddings(input_ids) + if multimodal_embeddings is not None \ + and len(multimodal_embeddings) != 0: + + # TODO (ywang96): support overlapping modalitiy embeddings so that + # `use_audio_in_video` will work on V1. + inputs_embeds = merge_multimodal_embeddings( + input_ids, inputs_embeds, multimodal_embeddings, [ + self.config.image_token_index, + self.config.video_token_index, + self.config.audio_token_index + ]) + return inputs_embeds + + def forward( + self, + input_ids: torch.Tensor = None, + positions: torch.Tensor = None, + intermediate_tensors: Optional[IntermediateTensors] = None, + inputs_embeds: Optional[torch.Tensor] = None, + **kwargs: object, + ) -> Union[torch.Tensor, IntermediateTensors]: + assert input_ids is not None or inputs_embeds is not None, "input_ids or inputs_embeds must be provided" + forward_context: ForwardContext = get_forward_context() + attn_metadata: AttentionMetadata = forward_context.attn_metadata + + if intermediate_tensors is not None: + inputs_embeds = None + elif inputs_embeds is None: + # for profile_run: + inputs_embeds = self.get_input_embeddings(input_ids) + + input_ids = None + + # projection + inputs_embeds, _ = self.thinker_to_talker_proj(inputs_embeds) + + hidden_states = self.language_model.model(input_ids, + positions, + intermediate_tensors, + inputs_embeds=inputs_embeds) + return hidden_states + + def compute_logits( + self, + hidden_states: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[torch.Tensor]: + return self.language_model.compute_logits(hidden_states, + sampling_metadata) + + def sample( + self, + logits: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[SamplerOutput]: + return self.language_model.sample(logits, sampling_metadata) + + def load_weights(self, weights: Iterable[Tuple[str, + torch.Tensor]]) -> Set[str]: + loader = AutoWeightsLoader( + self, + skip_prefixes=['thinker.', 'token2wav.'], + ) + loaded = loader.load_weights(weights, mapper=self.hf_to_vllm_mapper) + # Log load summary + try: + total_bytes = 0 + for name, param in self.named_parameters(): + if param is not None and param.data is not None: + total_bytes += param.data.numel() * param.data.element_size() + device = next(self.parameters()).device + self.logger.info( + "[Model Loaded] name=%s, success=%s, size=%.2f MB, device=%s", + self.__class__.__name__, True, total_bytes / (1024**2), str(device)) + except Exception: + pass + multi_model_weights = set() + for name, param in self.visual.named_parameters(): + multi_model_weights.add("visual."+name) + for name, param in self.audio_tower.named_parameters(): + multi_model_weights.add("audio_tower."+name) + loaded.update(multi_model_weights) + return loaded + + def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict: + mm_input_by_modality = {} + + # Preserve the order of modalities if there are multiple of them + # from the order of kwargs. + for input_key in kwargs: + if input_key in ("pixel_values", "image_embeds" + ) and "image" not in mm_input_by_modality: + mm_input_by_modality[ + "image"] = self._parse_and_validate_image_input(**kwargs) + if input_key in ("pixel_values_videos", "video_embeds" + ) and "video" not in mm_input_by_modality: + mm_input_by_modality[ + "video"] = self._parse_and_validate_video_input(**kwargs) + if input_key in ("input_audio_features" + ) and "audio" not in mm_input_by_modality: + mm_input_by_modality[ + "audio"] = self._parse_and_validate_audio_input(**kwargs) + return mm_input_by_modality + + def get_multimodal_embeddings(self, + **kwargs: object) -> MultiModalEmbeddings: + + mm_input_by_modality = self._parse_and_validate_multimodal_inputs( + **kwargs) + if not mm_input_by_modality: + return [] + + # The result multimodal_embeddings is tuple of tensors, with each + # tensor correspoending to a multimodal data item (image or video). + multimodal_embeddings: tuple[torch.Tensor, ...] = () + + # NOTE: It is important to iterate over the keys in this dictionary + # to preserve the order of the modalities. + for modality in mm_input_by_modality: + multimodal_input = mm_input_by_modality[modality] + if modality == "image": + vision_embeddings = self._process_image_input(multimodal_input) + multimodal_embeddings += vision_embeddings + if modality == "video": + video_embeddings = self._process_video_input(multimodal_input) + multimodal_embeddings += video_embeddings + if modality == "audio": + audio_embeddings = self._process_audio_input(multimodal_input) + multimodal_embeddings += audio_embeddings + return multimodal_embeddings \ No newline at end of file diff --git a/vllm_omni/model_executor/models/qwen2_5_omni_thinker.py b/vllm_omni/model_executor/models/qwen2_5_omni_thinker.py new file mode 100644 index 000000000..f1eb2c005 --- /dev/null +++ b/vllm_omni/model_executor/models/qwen2_5_omni_thinker.py @@ -0,0 +1,889 @@ +from collections.abc import Iterable, Mapping, Sequence +from copy import copy +from functools import partial +from typing import Any, Optional, Union + +import torch +import torch.nn as nn +from transformers.feature_extraction_utils import BatchFeature +from transformers.models.qwen2_5_omni.configuration_qwen2_5_omni import ( + Qwen2_5OmniConfig, Qwen2_5OmniThinkerConfig) +from transformers.models.qwen2_5_omni.modeling_qwen2_5_omni import ( + Qwen2_5OmniAudioEncoder) +from transformers.models.qwen2_5_omni.processing_qwen2_5_omni import ( + Qwen2_5OmniProcessor) +from transformers.models.whisper import WhisperFeatureExtractor + +from vllm.config import VllmConfig +from vllm.logger import init_logger + +from vllm.model_executor.models.qwen2_5_vl import ( + Qwen2_5_VisionTransformer, Qwen2_5_VLImageEmbeddingInputs, + Qwen2_5_VLImageInputs, Qwen2_5_VLImagePixelInputs, + Qwen2_5_VLProcessingInfo, Qwen2_5_VLVideoEmbeddingInputs, + Qwen2_5_VLVideoInputs, Qwen2_5_VLVideoPixelInputs) +from vllm.model_executor.models.qwen2_audio import ( + Qwen2AudioInputs, Qwen2AudioProcessingInfo, + _get_feat_extract_output_lengths) +from vllm.model_executor.models.qwen2_vl import Qwen2VLMultiModalDataParser +from vllm.model_executor.sampling_metadata import SamplingMetadata +from vllm.multimodal import MULTIMODAL_REGISTRY +from vllm.multimodal.inputs import (ImageItem, ModalityData, + MultiModalDataDict, MultiModalFieldConfig, + MultiModalKwargs, NestedTensors) +from vllm.multimodal.parse import (AudioProcessorItems, DictEmbeddingItems, + ModalityDataItems, MultiModalDataItems, + MultiModalDataParser) +from vllm.multimodal.processing import (BaseMultiModalProcessor, + PlaceholderFeaturesInfo, + PromptReplacement, PromptUpdate) +from vllm.multimodal.profiling import BaseDummyInputsBuilder +from vllm.sequence import IntermediateTensors +from vllm.transformers_utils.tokenizer import decode_tokens, encode_tokens + +from vllm.model_executor.models.interfaces import MultiModalEmbeddings, SupportsMultiModal, SupportsPP +from vllm.model_executor.models.utils import (AutoWeightsLoader, WeightsMapper, + init_vllm_registered_model, maybe_prefix, + merge_multimodal_embeddings) + +from vllm_omni.model_executor.layers.mrope import MRotaryEmbedding + +try: + import flash_attn +except (ImportError, ModuleNotFoundError): + flash_attn = None + +logger = init_logger(__name__) + + +def _qwen2_5_omni_thinker_field_config(hf_inputs: Mapping[str, torch.Tensor]): + audio_feature_lengths = hf_inputs.get("audio_feature_lengths", + torch.empty((0, ))) + + image_grid_thw = hf_inputs.get("image_grid_thw", torch.empty((0, 3))) + image_grid_sizes = image_grid_thw.prod(-1) + + video_grid_thw = hf_inputs.get("video_grid_thw", torch.empty((0, 3))) + video_grid_sizes = video_grid_thw.prod(-1) + + return dict( + input_audio_features=MultiModalFieldConfig.flat_from_sizes( + "audio", audio_feature_lengths, dim=1), + feature_attention_mask=MultiModalFieldConfig.batched("audio"), + audio_feature_lengths=MultiModalFieldConfig.batched("audio"), + pixel_values=MultiModalFieldConfig.flat_from_sizes( + "image", image_grid_sizes), + image_embeds=MultiModalFieldConfig.flat_from_sizes( + "image", image_grid_sizes), + image_grid_thw=MultiModalFieldConfig.batched("image"), + pixel_values_videos=MultiModalFieldConfig.flat_from_sizes( + "video", video_grid_sizes), + video_embeds=MultiModalFieldConfig.flat_from_sizes( + "video", video_grid_sizes), + video_grid_thw=MultiModalFieldConfig.batched("video"), + second_per_grid_ts=MultiModalFieldConfig.batched("video"), + ) + + +class Qwen2_5OmniThinkerMultiModalDataParser(Qwen2VLMultiModalDataParser): + + def _parse_audio_data( + self, + data: Union[dict[str, torch.Tensor], ModalityData[ImageItem]], + ) -> ModalityDataItems[Any, Any]: + if isinstance(data, dict): + return DictEmbeddingItems( + data, + modality="audio", + required_fields={ + "input_audio_features", "audio_feature_lengths" + }, + fields_factory=_qwen2_5_omni_thinker_field_config, + ) + + return super()._parse_audio_data(data) + + +class Qwen2_5OmniThinkerProcessingInfo(Qwen2AudioProcessingInfo, + Qwen2_5_VLProcessingInfo): + + def get_hf_config(self): + return self.ctx.get_hf_config(Qwen2_5OmniConfig).thinker_config + + def get_hf_processor(self, **kwargs: object) -> Qwen2_5OmniProcessor: + return self.ctx.get_hf_processor( + Qwen2_5OmniProcessor, + use_fast=kwargs.pop("use_fast", True), + **kwargs, + ) + + def get_feature_extractor(self, **kwargs: object): + hf_processor = self.get_hf_processor(**kwargs) + feature_extractor = hf_processor.feature_extractor # type: ignore + assert isinstance(feature_extractor, WhisperFeatureExtractor) + return feature_extractor + + def get_supported_mm_limits(self) -> Mapping[str, Optional[int]]: + return {"audio": None, "image": None, "video": None} + + +class Qwen2_5OmniThinkerDummyInputsBuilder( + BaseDummyInputsBuilder[Qwen2_5OmniThinkerProcessingInfo]): + + def get_dummy_text(self, mm_counts: Mapping[str, int]) -> str: + num_audios = mm_counts.get("audio", 0) + num_images = mm_counts.get("image", 0) + num_videos = mm_counts.get("video", 0) + + hf_processor = self.info.get_hf_processor() + + audio_token: str = hf_processor.audio_token + image_token: str = hf_processor.image_token + video_token: str = hf_processor.video_token + + return (audio_token * num_audios + image_token * num_images + + video_token * num_videos) + + def get_dummy_mm_data( + self, + seq_len: int, + mm_counts: Mapping[str, int], + ) -> MultiModalDataDict: + num_audios = mm_counts.get("audio", 0) + num_images = mm_counts.get("image", 0) + num_videos = mm_counts.get("video", 0) + + feature_extractor = self.info.get_feature_extractor() + + target_audio_length = min( + feature_extractor.chunk_length, + 30, + ) * feature_extractor.sampling_rate + target_width, target_height = \ + self.info.get_image_size_with_most_features() + target_num_frames = \ + self.info.get_num_frames_with_most_features(seq_len, mm_counts) + + mm_data = { + "audio": + self._get_dummy_audios(length=target_audio_length, + num_audios=num_audios), + "image": + self._get_dummy_images(width=target_width, + height=target_height, + num_images=num_images), + "video": + self._get_dummy_videos(width=target_width, + height=target_height, + num_frames=target_num_frames, + num_videos=num_videos), + } + + return mm_data + + +class Qwen2_5OmniThinkerMultiModalProcessor( + BaseMultiModalProcessor[Qwen2_5OmniThinkerProcessingInfo]): + + def _get_data_parser(self) -> MultiModalDataParser: + feature_extractor = self.info.get_feature_extractor() + return Qwen2_5OmniThinkerMultiModalDataParser( + target_sr=feature_extractor.sampling_rate) + + def _call_hf_processor( + self, + prompt: str, + mm_data: Mapping[str, object], + mm_kwargs: Mapping[str, object], + tok_kwargs: Mapping[str, object], + ) -> BatchFeature: + mm_data = dict(mm_data) + audios = mm_data.pop("audios", []) + + # NOTE: WhisperFeatureExtractor cannot handle empty list of audios + if audios: + # NOTE: Qwen2.5-Omni processor accept "audio" + mm_data["audio"] = audios + mm_kwargs = dict(**mm_kwargs, ) + + hf_inputs = super()._call_hf_processor( + prompt=prompt, + mm_data=mm_data, + mm_kwargs=mm_kwargs, + tok_kwargs=tok_kwargs, + ) + + input_features = hf_inputs.pop('input_features', None) + feature_attention_mask = hf_inputs.get('feature_attention_mask', None) + if ('input_audio_features' not in hf_inputs + and input_features is not None): + if feature_attention_mask is not None: + input_features = input_features.permute( + 0, 2, 1)[feature_attention_mask.bool()].permute(1, 0) + hf_inputs['input_audio_features'] = input_features + if ('audio_feature_lengths' not in hf_inputs + and feature_attention_mask is not None): + hf_inputs['audio_feature_lengths'] = feature_attention_mask.sum(-1) + return hf_inputs + + def _get_mm_fields_config( + self, + hf_inputs: BatchFeature, + hf_processor_mm_kwargs: Mapping[str, object], + ) -> Mapping[str, MultiModalFieldConfig]: + return _qwen2_5_omni_thinker_field_config(hf_inputs) + + def _maybe_apply_prompt_updates( + self, + mm_items: MultiModalDataItems, + hf_processor_mm_kwargs: Mapping[str, object], + prompt_ids: list[int], + mm_kwargs: MultiModalKwargs, + is_update_applied: bool, + ) -> tuple[list[int], str, Mapping[str, list[PlaceholderFeaturesInfo]]]: + """ + Qwen2.5-Omni reimplements this function to handle `use_audio_in_video`. + """ + unbound_prompt_updates = self._get_prompt_updates( + mm_items, + hf_processor_mm_kwargs, + mm_kwargs, + ) + mm_prompt_updates = self._bind_and_group_updates( + unbound_prompt_updates) + + mm_item_counts = mm_items.get_all_counts() + self._validate_mm_kwargs(mm_kwargs, mm_item_counts) + + use_audio_in_video = hf_processor_mm_kwargs.get( + "use_audio_in_video", False) + + if is_update_applied: + mm_placeholders = self._find_mm_placeholders( + mm_prompt_updates, + prompt_ids, + mm_item_counts, + ) + self._validate_mm_placeholders( + mm_placeholders, + mm_item_counts, + use_audio_in_video=use_audio_in_video) + + tokenizer = self.info.get_tokenizer() + prompt = decode_tokens(tokenizer, prompt_ids) + else: + ( + prompt_ids, + prompt, + mm_placeholders, + ) = self._apply_prompt_updates( + prompt_ids, + mm_prompt_updates, + mm_item_counts, + ) + self._validate_mm_placeholders( + mm_placeholders, + mm_item_counts, + use_audio_in_video=use_audio_in_video) + + tokenizer = self.info.get_tokenizer() + prompt = decode_tokens(tokenizer, prompt_ids) + + if use_audio_in_video: + mm_kwargs["use_audio_in_video"] = True + + return prompt_ids, prompt, mm_placeholders + + def _get_prompt_updates( + self, + mm_items: MultiModalDataItems, + hf_processor_mm_kwargs: Mapping[str, Any], + out_mm_kwargs: MultiModalKwargs, + ) -> Sequence[PromptUpdate]: + processor = self.info.get_hf_processor(**hf_processor_mm_kwargs) + tokenizer = self.info.get_tokenizer() + image_processor = self.info.get_image_processor( + **hf_processor_mm_kwargs) + vocab = tokenizer.get_vocab() + + audio_token = processor.audio_token + image_token = processor.image_token + video_token = processor.video_token + audio_token_id = vocab[audio_token] + image_token_id = vocab[image_token] + video_token_id = vocab[video_token] + + audio_feature_lengths = out_mm_kwargs.get("audio_feature_lengths") + feature_attention_mask = out_mm_kwargs.get("feature_attention_mask") + if audio_feature_lengths is None and feature_attention_mask is None: + audio_output_lengths = [] + elif audio_feature_lengths is not None: + _, audio_output_lens = _get_feat_extract_output_lengths( + audio_feature_lengths) + audio_output_lengths = audio_output_lens.tolist() + elif feature_attention_mask is not None: + assert isinstance(feature_attention_mask, torch.Tensor) + _, audio_output_lens = _get_feat_extract_output_lengths( + feature_attention_mask.sum(-1)) + audio_output_lengths = audio_output_lens.tolist() + + # number of audios read from video. + audio_in_video_item_idx = 0 + + def get_replacement_qwen2_audio(item_idx: int): + item_idx += audio_in_video_item_idx + + num_features = audio_output_lengths[item_idx] + if num_features == 0: + audios = mm_items.get_items("audio", AudioProcessorItems) + audio = audios.get(item_idx) + raise ValueError( + f"The audio {audio} (len={len(audio)}) is too short " + "to be represented inside the model") + + return [audio_token_id] * num_features + + def get_replacement_qwen2_vision(item_idx: int, modality: str): + grid_thw = out_mm_kwargs[f"{modality}_grid_thw"][item_idx] + assert isinstance(grid_thw, torch.Tensor) + merge_length = image_processor.merge_size**2 + + token_id = image_token_id if modality == "image" else video_token_id + return [token_id] * (int(grid_thw.prod()) // merge_length) + + use_audio_in_video = hf_processor_mm_kwargs.get( + "use_audio_in_video", False) + thinker_config = self.info.get_hf_config() + + def get_replacement_qwen2_use_audio_in_video(item_idx: int): + nonlocal audio_in_video_item_idx + + audio_num_features = audio_output_lengths[audio_in_video_item_idx + + item_idx] + video_grid_thw = out_mm_kwargs["video_grid_thw"][item_idx] + + audio_in_video_item_idx += 1 + + second_per_grid_ts = hf_processor_mm_kwargs.get( + "second_per_grid_ts", None) + if second_per_grid_ts: + video_second_per_grid_t = second_per_grid_ts[item_idx] + else: + video_second_per_grid_t = 1.0 + + return MRotaryEmbedding.omni_get_updates_use_audio_in_video( + thinker_config=thinker_config, + audio_len=audio_num_features, + video_grid_thw=video_grid_thw, + video_second_per_grid_t=video_second_per_grid_t, + ) + + video_replacement_fn = ( + get_replacement_qwen2_use_audio_in_video if use_audio_in_video else + partial(get_replacement_qwen2_vision, modality="video")) + + return [ + PromptReplacement( + modality="audio", + target=audio_token, + replacement=get_replacement_qwen2_audio, + ), + PromptReplacement( + modality="image", + target=image_token, + replacement=partial(get_replacement_qwen2_vision, + modality="image"), + ), + PromptReplacement( + modality="video", + target=video_token, + replacement=video_replacement_fn, + ), + ] + + def _apply_hf_processor_main( + self, + prompt: Union[str, list[int]], + mm_items: MultiModalDataItems, + hf_processor_mm_kwargs: Mapping[str, object], + tokenization_kwargs: Mapping[str, object], + *, + enable_hf_prompt_update: bool, + ) -> tuple[list[int], BatchFeature, bool]: + """ + Qwen2.5-Omni reimplements this function to handle text only. + """ + if isinstance(prompt, str): + if enable_hf_prompt_update: + return self._apply_hf_processor_text_mm( + prompt_text=prompt, + mm_items=mm_items, + hf_processor_mm_kwargs=hf_processor_mm_kwargs, + tokenization_kwargs=tokenization_kwargs, + ) + tokenizer = self.info.get_tokenizer() + prompt_ids = encode_tokens(tokenizer, prompt) + else: + prompt_ids = self._apply_hf_processor_tokens_only(prompt) + + mm_processed_data = self._apply_hf_processor_mm_only( + mm_items=mm_items, + hf_processor_mm_kwargs=hf_processor_mm_kwargs, + tokenization_kwargs=tokenization_kwargs, + ) + + return prompt_ids, mm_processed_data, False + + def _apply_hf_processor_mm_only( + self, + mm_items: MultiModalDataItems, + hf_processor_mm_kwargs: Mapping[str, object], + tokenization_kwargs: Mapping[str, object], + ) -> BatchFeature: + """ + Qwen2.5-Omni reimplements this function to handle `use_audio_in_video`. + """ + mm_counts = mm_items.get_all_counts() + + use_audio_in_video = hf_processor_mm_kwargs.get( + "use_audio_in_video", False) + if use_audio_in_video and "video" in mm_counts: + assert "audio" in mm_counts + mm_counts["audio"] -= mm_counts["video"] + + _, mm_processed_data, _ = self._apply_hf_processor_text_mm( + prompt_text=self.dummy_inputs.get_dummy_text(mm_counts), + mm_items=mm_items, + hf_processor_mm_kwargs=hf_processor_mm_kwargs, + tokenization_kwargs=tokenization_kwargs, + ) + + return mm_processed_data + + def _validate_mm_placeholders( + self, + mm_placeholders: Mapping[str, list[PlaceholderFeaturesInfo]], + mm_item_counts: Mapping[str, int], + use_audio_in_video: bool = False, + ) -> None: + if use_audio_in_video: + mm_item_counts = copy(mm_item_counts) + if "video" in mm_item_counts: + assert "audio" in mm_item_counts + mm_item_counts["audio"] -= mm_item_counts["video"] + super()._validate_mm_placeholders(mm_placeholders, mm_item_counts) + + +class Qwen2_5OmniConditionalGenerationMixin: + + def _validate_and_reshape_mm_tensor(self, + mm_input: object, + name: str, + dim: int = 0) -> torch.Tensor: + if not isinstance(mm_input, (torch.Tensor, list)): + raise ValueError(f"Incorrect type of {name}. " + f"Got type: {type(mm_input)}") + if isinstance(mm_input, torch.Tensor): + return torch.concat(list(mm_input), dim=dim) + else: + return torch.concat(mm_input, dim=dim) + + def _parse_and_validate_audio_input( + self, **kwargs: object) -> Optional[Qwen2AudioInputs]: + input_audio_features = kwargs.pop('input_audio_features', None) + audio_feature_lengths = kwargs.pop('audio_feature_lengths', None) + feature_attention_mask = kwargs.pop('feature_attention_mask', None) + if input_audio_features is None: + return None + input_audio_features = self._validate_and_reshape_mm_tensor( + input_audio_features, 'input_audio_features', dim=1) + if feature_attention_mask is not None: + feature_attention_mask = self._validate_and_reshape_mm_tensor( + feature_attention_mask, 'feature_attention_mask') + if not isinstance(input_audio_features, (torch.Tensor, list)): + raise ValueError("Incorrect type of audio input features. " + f"Got type: {type(input_audio_features)}") + return Qwen2AudioInputs(input_features=input_audio_features, + audio_feature_lengths=audio_feature_lengths, + feature_attention_mask=feature_attention_mask) + + def _parse_and_validate_image_input( + self, + **kwargs: dict[str, Any], + ) -> Optional[Qwen2_5_VLImageInputs]: + pixel_values = kwargs.pop("pixel_values", None) + image_embeds = kwargs.pop("image_embeds", None) + image_grid_thw = kwargs.pop("image_grid_thw", None) + + if pixel_values is None and image_embeds is None: + return None + + if pixel_values is not None: + pixel_values = self._validate_and_reshape_mm_tensor( + pixel_values, "image pixel values") + image_grid_thw = self._validate_and_reshape_mm_tensor( + image_grid_thw, "image grid_thw") + + if not isinstance(pixel_values, (torch.Tensor, list)): + raise ValueError("Incorrect type of image pixel values. " + f"Got type: {type(pixel_values)}") + + return Qwen2_5_VLImagePixelInputs(type="pixel_values", + pixel_values=pixel_values, + image_grid_thw=image_grid_thw) + + if image_embeds is not None: + image_embeds = self._validate_and_reshape_mm_tensor( + image_embeds, "image embeds") + image_grid_thw = self._validate_and_reshape_mm_tensor( + image_grid_thw, "image grid_thw") + + if not isinstance(image_embeds, torch.Tensor): + raise ValueError("Incorrect type of image embeddings. " + f"Got type: {type(image_embeds)}") + return Qwen2_5_VLImageEmbeddingInputs( + type="image_embeds", + image_embeds=image_embeds, + image_grid_thw=image_grid_thw) + + def _parse_and_validate_video_input( + self, + **kwargs: dict[str, Any], + ) -> Optional[Qwen2_5_VLVideoInputs]: + pixel_values_videos = kwargs.pop("pixel_values_videos", None) + video_embeds = kwargs.pop("video_embeds", None) + video_grid_thw = kwargs.pop("video_grid_thw", None) + + if pixel_values_videos is None and video_embeds is None: + return None + + if pixel_values_videos is not None: + pixel_values_videos = self._validate_and_reshape_mm_tensor( + pixel_values_videos, "video pixel values") + video_grid_thw = self._validate_and_reshape_mm_tensor( + video_grid_thw, "video grid_thw") + + return Qwen2_5_VLVideoPixelInputs( + type="pixel_values_videos", + pixel_values_videos=pixel_values_videos, + video_grid_thw=video_grid_thw, + ) + + if video_embeds is not None: + video_embeds = self._validate_and_reshape_mm_tensor( + video_embeds, "video embeds") + video_grid_thw = self._validate_and_reshape_mm_tensor( + video_grid_thw, "video grid_thw") + + if not isinstance(video_embeds, torch.Tensor): + raise ValueError("Incorrect type of video embeddings. " + f"Got type: {type(video_embeds)}") + return Qwen2_5_VLVideoEmbeddingInputs( + type="video_embeds", + video_embeds=video_embeds, + video_grid_thw=video_grid_thw) + + def _process_audio_input( + self, + audio_input: Qwen2AudioInputs, + audio_hashes: list[str] = None, + cached_audio_features: torch.Tensor = None, + ) -> torch.Tensor: + + input_features = audio_input["input_features"] + audio_feature_lengths = audio_input["audio_feature_lengths"] + if input_features.ndim == 3: + assert input_features.shape[0] == 1 + input_features = input_features.squeeze(0) + if audio_feature_lengths.ndim == 2: + assert audio_feature_lengths.shape[ + 0] == 1 or audio_feature_lengths.shape[1] == 1 + if audio_feature_lengths.shape[0] == 1: + audio_feature_lengths = audio_feature_lengths.squeeze(0) + else: + audio_feature_lengths = audio_feature_lengths.squeeze(1) + + audio_feat_lengths, audio_output_lengths = ( + self.audio_tower._get_feat_extract_output_lengths( + audio_feature_lengths)) + + audio_outputs = self.audio_tower( + input_features.to(self.audio_tower.dtype), + feature_lens=audio_feature_lengths, + aftercnn_lens=audio_feat_lengths, + ) + audio_features = audio_outputs.last_hidden_state + return audio_features.split(audio_output_lengths.tolist()) + + def _process_image_input( + self, + image_input: Qwen2_5_VLImageInputs) -> tuple[torch.Tensor, ...]: + if image_input["type"] == "image_embeds": + return image_input["image_embeds"].type(self.visual.dtype) + + grid_thw = image_input["image_grid_thw"] + assert grid_thw.ndim == 2 + + pixel_values = image_input["pixel_values"].type(self.visual.dtype) + image_embeds = self.visual(pixel_values, grid_thw=grid_thw) + # Split concatenated embeddings for each image item. + merge_size = self.visual.spatial_merge_size + sizes = grid_thw.prod(-1) // merge_size // merge_size + + return image_embeds.split(sizes.tolist()) + + def _process_video_input( + self, + video_input: Qwen2_5_VLVideoInputs, + video_hashes: list[str] = None, + cached_video_embeds: torch.Tensor = None) -> torch.Tensor: + if video_input["type"] == "video_embeds": + return video_input["video_embeds"].type(self.visual.dtype) + + grid_thw = video_input["video_grid_thw"] + assert grid_thw.ndim == 2 + + pixel_values_videos = video_input["pixel_values_videos"].type( + self.visual.dtype) + video_embeds = self.visual(pixel_values_videos, grid_thw=grid_thw) + # Split concatenated embeddings for each video item. + merge_size = self.visual.spatial_merge_size + sizes = grid_thw.prod(-1) // merge_size // merge_size + + return video_embeds.split(sizes.tolist()) + + +@MULTIMODAL_REGISTRY.register_processor( + Qwen2_5OmniThinkerMultiModalProcessor, + info=Qwen2_5OmniThinkerProcessingInfo, + dummy_inputs=Qwen2_5OmniThinkerDummyInputsBuilder, +) +class Qwen2_5OmniThinkerForConditionalGeneration( + nn.Module, SupportsMultiModal, SupportsPP, + Qwen2_5OmniConditionalGenerationMixin): + hf_to_vllm_mapper = WeightsMapper( + orig_to_new_prefix={ + "thinker.lm_head.": "language_model.lm_head.", + "thinker.model.": "language_model.model.", + "thinker.": "", + }) + + @classmethod + def get_placeholder_str(cls, modality: str, i: int) -> Optional[str]: + if modality.startswith("image"): + return "<|vision_start|><|IMAGE|><|vision_end|>" + if modality.startswith("video"): + return "<|vision_start|><|VIDEO|><|vision_end|>" + if modality.startswith("audio"): + return f"Audio {i}: <|audio_bos|><|AUDIO|><|audio_eos|>" + + raise ValueError("Only image, video or audio modality is supported") + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + thinker_config: Qwen2_5OmniThinkerConfig = ( + vllm_config.model_config.hf_config) + quant_config = vllm_config.quant_config + multimodal_config = vllm_config.model_config.multimodal_config + self.config = thinker_config + self.multimodal_config = multimodal_config + + # force "use_flash_attention_2=True" to audio tower to align + # the results. + if flash_attn is not None: + audio_config = thinker_config.audio_config + audio_config._attn_implementation_autoset = True + audio_config._attn_implementation = "flash_attention_2" + else: + logger.warning( + "flash_attn is not available, the model may not yield the " + "exactly same result as the transformers implementation " + "in the audio tower part.") + + self.audio_tower = Qwen2_5OmniAudioEncoder(thinker_config.audio_config) + self.visual = Qwen2_5_VisionTransformer( + vision_config=thinker_config.vision_config, + norm_eps=getattr(thinker_config.text_config, "rms_norm_eps", 1e-6), + quant_config=quant_config, + prefix=maybe_prefix(prefix, "visual"), + ) + self.quant_config = quant_config + self.language_model = init_vllm_registered_model( + vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "language_model"), + hf_config=thinker_config.text_config, + architectures=["Qwen2ForCausalLM"], + ) + + self.make_empty_intermediate_tensors = ( + self.language_model.make_empty_intermediate_tensors) + + def _parse_and_validate_multimodal_inputs(self, **kwargs: object) -> dict: + mm_input_by_modality = {} + + # Preserve the order of modalities if there are multiple of them + # from the order of kwargs. + for input_key in kwargs: + if input_key in ("pixel_values", "image_embeds" + ) and "image" not in mm_input_by_modality: + mm_input_by_modality[ + "image"] = self._parse_and_validate_image_input(**kwargs) + if input_key in ("pixel_values_videos", "video_embeds" + ) and "video" not in mm_input_by_modality: + mm_input_by_modality[ + "video"] = self._parse_and_validate_video_input(**kwargs) + if input_key in ("input_audio_features" + ) and "audio" not in mm_input_by_modality: + mm_input_by_modality[ + "audio"] = self._parse_and_validate_audio_input(**kwargs) + return mm_input_by_modality + + def get_language_model(self) -> torch.nn.Module: + return self.language_model + + def get_multimodal_embeddings(self, + **kwargs: object) -> MultiModalEmbeddings: + + mm_input_by_modality = self._parse_and_validate_multimodal_inputs( + **kwargs) + if not mm_input_by_modality: + return [] + + # The result multimodal_embeddings is tuple of tensors, with each + # tensor correspoending to a multimodal data item (image or video). + multimodal_embeddings: tuple[torch.Tensor, ...] = () + + # NOTE: It is important to iterate over the keys in this dictionary + # to preserve the order of the modalities. + for modality in mm_input_by_modality: + multimodal_input = mm_input_by_modality[modality] + if modality == "image": + vision_embeddings = self._process_image_input(multimodal_input) + multimodal_embeddings += vision_embeddings + if modality == "video": + video_embeddings = self._process_video_input(multimodal_input) + multimodal_embeddings += video_embeddings + if modality == "audio": + audio_embeddings = self._process_audio_input(multimodal_input) + multimodal_embeddings += audio_embeddings + return multimodal_embeddings + + def get_input_embeddings( + self, + input_ids: torch.Tensor, + multimodal_embeddings: Optional[MultiModalEmbeddings] = None, + ) -> torch.Tensor: + inputs_embeds = self.language_model.get_input_embeddings(input_ids) + if multimodal_embeddings is not None \ + and len(multimodal_embeddings) != 0: + + # TODO (ywang96): support overlapping modalitiy embeddings so that + # `use_audio_in_video` will work on V1. + inputs_embeds = merge_multimodal_embeddings( + input_ids, inputs_embeds, multimodal_embeddings, [ + self.config.image_token_index, + self.config.video_token_index, + self.config.audio_token_index + ]) + return inputs_embeds + + def get_multimodal_embeddings_v0( + self, **kwargs: object) -> Optional[NestedTensors]: + audio_input = self._parse_and_validate_audio_input(**kwargs) + image_input = self._parse_and_validate_image_input(**kwargs) + video_input = self._parse_and_validate_video_input(**kwargs) + + if audio_input is None and image_input is None and video_input is None: + return None + + multimodal_embeddings: list[tuple[NestedTensors, str]] = [] + + if audio_input is not None: + audio_embeds = self._process_audio_input(audio_input) + multimodal_embeddings.append((audio_embeds, "audio")) + if image_input is not None: + image_embeds = self._process_image_input(image_input) + multimodal_embeddings.append((image_embeds, "image")) + if video_input is not None: + video_embeds = self._process_video_input(video_input) + multimodal_embeddings.append((video_embeds, "video")) + return multimodal_embeddings + + def get_input_embeddings_v0( + self, + input_ids: torch.Tensor, + multimodal_embeddings: Optional[NestedTensors] = None, + ) -> torch.Tensor: + inputs_embeds = self.language_model.get_input_embeddings(input_ids) + if multimodal_embeddings is None or len(multimodal_embeddings) == 0: + return inputs_embeds + + for embeddings, modality in multimodal_embeddings: + if modality == "audio": + placeholder_token_id = self.config.audio_token_index + if modality == "image": + placeholder_token_id = self.config.image_token_index + if modality == "video": + placeholder_token_id = self.config.video_token_index + inputs_embeds = merge_multimodal_embeddings( + input_ids, inputs_embeds, embeddings, placeholder_token_id) + return inputs_embeds + + def forward( + self, + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors] = None, + inputs_embeds: Optional[torch.Tensor] = None, + **kwargs: object, + ) -> Union[torch.Tensor, IntermediateTensors]: + if intermediate_tensors is not None: + inputs_embeds = None + + # NOTE: In v1, inputs_embeds is always generated at model runner, this + # condition is for v0 compatibility. + elif inputs_embeds is None: + multimodal_embeddings = self.get_multimodal_embeddings(**kwargs) + inputs_embeds = self.get_input_embeddings( + input_ids, multimodal_embeddings) + text_inputs_embeds = self.get_input_embeddings(input_ids, [(torch.zeros_like(embeddings), modality) for embeddings, modality in multimodal_embeddings] if multimodal_embeddings is not None else None) + input_ids = None + else: + text_inputs_embeds = inputs_embeds + + hidden_states = self.language_model.model(input_ids, + positions, + intermediate_tensors, + inputs_embeds=inputs_embeds) + return text_inputs_embeds, hidden_states.unsqueeze(0) #(1, S, D) + + def compute_logits( + self, + hidden_states: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[torch.Tensor]: + return self.language_model.compute_logits(hidden_states, + sampling_metadata) + + def load_weights(self, weights: Iterable[tuple[str, + torch.Tensor]]) -> set[str]: + loader = AutoWeightsLoader( + self, + skip_prefixes=["talker.", "token2wav."], + ) + loaded_weights = loader.load_weights(weights, + mapper=self.hf_to_vllm_mapper) + # Log load summary + try: + total_bytes = 0 + for name, param in self.named_parameters(): + if param is not None and param.data is not None: + total_bytes += param.data.numel() * param.data.element_size() + device = next(self.parameters()).device + logger.info( + "[Model Loaded] name=%s, success=%s, size=%.2f MB, device=%s", + self.__class__.__name__, True, total_bytes / (1024**2), str(device)) + except Exception: + pass + return loaded_weights + diff --git a/vllm_omni/model_executor/models/qwen2_5_omni_token2wav.py b/vllm_omni/model_executor/models/qwen2_5_omni_token2wav.py new file mode 100644 index 000000000..3937c2942 --- /dev/null +++ b/vllm_omni/model_executor/models/qwen2_5_omni_token2wav.py @@ -0,0 +1,1690 @@ +############################ +# Start Token2Wav # +############################ + +from typing import Iterable, Optional, Set, Tuple, List, Union + +import torch +import torch.nn as nn +import torch.nn.functional as F +import numpy as np +import math +from torch.nn import Parameter + +from vllm.config import VllmConfig +from vllm.logger import init_logger +from vllm.sequence import IntermediateTensors +from vllm.model_executor.sampling_metadata import SamplingMetadata +from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler +from vllm.model_executor.models.interfaces import SupportsPP +from vllm.model_executor.models.utils import ( + AutoWeightsLoader as _Vllm_AutoWeightsLoader, + WeightsMapper as _Vllm_WeightsMapper, + init_vllm_registered_model as _Vllm_init_vllm_registered_model, + maybe_prefix as _Vllm_maybe_prefix, +) + +# Bring in HF base classes, configs and utilities used below +from transformers.utils import ModelOutput +from transformers.utils.logging import get_logger as _hf_get_logger +from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS +from transformers.models.qwen2_5_omni.modeling_qwen2_5_omni import ( + Qwen2_5OmniPreTrainedModel, + Qwen2_5OmniPreTrainedModelForConditionalGeneration, +) +from transformers.models.qwen2_5_omni.configuration_qwen2_5_omni import ( + Qwen2_5OmniDiTConfig, + Qwen2_5OmniBigVGANConfig, + Qwen2_5OmniToken2WavConfig, +) + +# Provide a no-op auto_docstring decorator to satisfy annotations if missing +def auto_docstring(func=None, **_kwargs): + if func is None: + def wrapper(f): + return f + return wrapper + return func + +# HF logger alias +logger = _hf_get_logger(__name__) + + +# Using custom RoPE, will use LlamaRotaryEmbedding next version +class Qwen2_5OmniDiTRotaryEmbedding(nn.Module): + def __init__(self, dim, base=10000): + super().__init__() + + inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim)) + self.register_buffer("inv_freq", inv_freq) + + def forward(self, x): + batch_size, seq_len = x.shape[0], x.shape[1] + t = torch.arange(seq_len, device=x.device) + device_type = x.device.type + device_type = device_type if device_type != "mps" else "cpu" + with torch.autocast(device_type=device_type, enabled=False): + freqs = t.unsqueeze(1).float() @ self.inv_freq.unsqueeze(0).float() + freqs = torch.stack((freqs, freqs), dim=-1) + freqs = freqs.reshape(*freqs.shape[:-2], -1) + freqs = freqs.repeat(batch_size, *([1] * freqs.dim())) + cos = freqs.cos() + sin = freqs.sin() + + return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype) + + +class TimeDelayNetBlock(nn.Module): + def __init__( + self, + in_channels, + out_channels, + kernel_size, + dilation, + ): + super().__init__() + self.conv = nn.Conv1d( + in_channels=in_channels, + out_channels=out_channels, + kernel_size=kernel_size, + dilation=dilation, + padding="same", + padding_mode="reflect", + ) + self.activation = nn.ReLU() + + def forward(self, hidden_states: torch.Tensor): + return self.activation(self.conv(hidden_states)) + + +class Res2NetBlock(torch.nn.Module): + def __init__(self, in_channels, out_channels, scale=8, kernel_size=3, dilation=1): + super().__init__() + + in_channel = in_channels // scale + hidden_channel = out_channels // scale + + self.blocks = nn.ModuleList( + [ + TimeDelayNetBlock( + in_channel, + hidden_channel, + kernel_size=kernel_size, + dilation=dilation, + ) + for i in range(scale - 1) + ] + ) + self.scale = scale + + def forward(self, hidden_states): + outputs = [] + for i, hidden_part in enumerate(torch.chunk(hidden_states, self.scale, dim=1)): + if i == 0: + output_part = hidden_part + elif i == 1: + output_part = self.blocks[i - 1](hidden_part) + else: + output_part = self.blocks[i - 1](hidden_part + output_part) + outputs.append(output_part) + output = torch.cat(outputs, dim=1) + return output + + +class SqueezeExcitationBlock(nn.Module): + def __init__(self, in_channels, se_channels, out_channels): + super().__init__() + + self.conv1 = nn.Conv1d( + in_channels=in_channels, + out_channels=se_channels, + kernel_size=1, + padding="same", + padding_mode="reflect", + ) + self.relu = nn.ReLU(inplace=True) + self.conv2 = nn.Conv1d( + in_channels=se_channels, + out_channels=out_channels, + kernel_size=1, + padding="same", + padding_mode="reflect", + ) + self.sigmoid = nn.Sigmoid() + + def forward(self, hidden_states): + hidden_states_mean = hidden_states.mean(dim=2, keepdim=True) + + hidden_states_mean = self.relu(self.conv1(hidden_states_mean)) + hidden_states_mean = self.sigmoid(self.conv2(hidden_states_mean)) + + return hidden_states * hidden_states_mean + + +class AttentiveStatisticsPooling(nn.Module): + """This class implements an attentive statistic pooling layer for each channel. + It returns the concatenated mean and std of the input tensor. + """ + + def __init__(self, channels, attention_channels=128): + super().__init__() + + self.eps = 1e-12 + self.tdnn = TimeDelayNetBlock(channels * 3, attention_channels, 1, 1) + self.tanh = nn.Tanh() + self.conv = nn.Conv1d( + in_channels=attention_channels, + out_channels=channels, + kernel_size=1, + padding="same", + padding_mode="reflect", + ) + + def _length_to_mask(self, length, max_len=None, dtype=None, device=None): + """Creates a binary mask for each sequence. + + Reference: https://discuss.pytorch.org/t/how-to-generate-variable-length-mask/23397/3 + + Arguments + --------- + length : torch.LongTensor + Containing the length of each sequence in the batch. Must be 1D. + max_len : int + Max length for the mask, also the size of the second dimension. + dtype : torch.dtype, default: None + The dtype of the generated mask. + device: torch.device, default: None + The device to put the mask variable. + + Returns + ------- + mask : tensor + The binary mask. + """ + + if max_len is None: + max_len = length.max().long().item() # using arange to generate mask + mask = torch.arange(max_len, device=length.device, dtype=length.dtype).expand( + len(length), max_len + ) < length.unsqueeze(1) + + mask = torch.as_tensor(mask, dtype=dtype, device=device) + return mask + + def _compute_statistics(self, x, m, dim=2): + mean = (m * x).sum(dim) + std = torch.sqrt((m * (x - mean.unsqueeze(dim)).pow(2)).sum(dim).clamp(self.eps)) + return mean, std + + def forward(self, hidden_states): + seq_length = hidden_states.shape[-1] + lengths = torch.ones(hidden_states.shape[0], device=hidden_states.device) + + # Make binary mask of shape [N, 1, L] + mask = self._length_to_mask( + lengths * seq_length, max_len=seq_length, dtype=hidden_states.dtype, device=hidden_states.device + ) + mask = mask.unsqueeze(1) + + # Expand the temporal context of the pooling layer by allowing the + # self-attention to look at global properties of the utterance. + total = mask.sum(dim=2, keepdim=True) + + mean, std = self._compute_statistics(hidden_states, mask / total) + mean = mean.unsqueeze(2).repeat(1, 1, seq_length) + std = std.unsqueeze(2).repeat(1, 1, seq_length) + attention = torch.cat([hidden_states, mean, std], dim=1) + + # Apply layers + attention = self.conv(self.tanh(self.tdnn(attention))) + + # Filter out zero-paddings + attention = attention.masked_fill(mask == 0, float("-inf")) + + attention = F.softmax(attention, dim=2) + mean, std = self._compute_statistics(hidden_states, attention) + # Append mean and std of the batch + pooled_stats = torch.cat((mean, std), dim=1) + pooled_stats = pooled_stats.unsqueeze(2) + + return pooled_stats + + +class SqueezeExcitationRes2NetBlock(nn.Module): + """An implementation of building block in ECAPA-TDNN, i.e., + TDNN-Res2Net-TDNN-SqueezeExcitationBlock. + """ + + def __init__( + self, + in_channels, + out_channels, + res2net_scale=8, + se_channels=128, + kernel_size=1, + dilation=1, + ): + super().__init__() + self.out_channels = out_channels + self.tdnn1 = TimeDelayNetBlock( + in_channels, + out_channels, + kernel_size=1, + dilation=1, + ) + self.res2net_block = Res2NetBlock(out_channels, out_channels, res2net_scale, kernel_size, dilation) + self.tdnn2 = TimeDelayNetBlock( + out_channels, + out_channels, + kernel_size=1, + dilation=1, + ) + self.se_block = SqueezeExcitationBlock(out_channels, se_channels, out_channels) + + def forward(self, hidden_state): + residual = hidden_state + + hidden_state = self.tdnn1(hidden_state) + hidden_state = self.res2net_block(hidden_state) + hidden_state = self.tdnn2(hidden_state) + hidden_state = self.se_block(hidden_state) + + return hidden_state + residual + + +class ECAPA_TimeDelayNet(torch.nn.Module): + """An implementation of the speaker embedding model in a paper. + "ECAPA-TDNN: Emphasized Channel Attention, Propagation and Aggregation in + TDNN Based Speaker Verification" (https://huggingface.co/papers/2005.07143). + """ + + def __init__(self, config: Qwen2_5OmniDiTConfig): + super().__init__() + if len(config.enc_channels) != len(config.enc_kernel_sizes) or len(config.enc_channels) != len( + config.enc_dilations + ): + raise ValueError("enc_channels, enc_kernel_sizes and enc_dilations should have same length") + self.channels = config.enc_channels + self.blocks = nn.ModuleList() + + # The initial TDNN layer + self.blocks.append( + TimeDelayNetBlock( + config.mel_dim, + config.enc_channels[0], + config.enc_kernel_sizes[0], + config.enc_dilations[0], + ) + ) + + # SE-Res2Net layers + for i in range(1, len(config.enc_channels) - 1): + self.blocks.append( + SqueezeExcitationRes2NetBlock( + config.enc_channels[i - 1], + config.enc_channels[i], + res2net_scale=config.enc_res2net_scale, + se_channels=config.enc_se_channels, + kernel_size=config.enc_kernel_sizes[i], + dilation=config.enc_dilations[i], + ) + ) + + # Multi-layer feature aggregation + self.mfa = TimeDelayNetBlock( + config.enc_channels[-1], + config.enc_channels[-1], + config.enc_kernel_sizes[-1], + config.enc_dilations[-1], + ) + + # Attentive Statistical Pooling + self.asp = AttentiveStatisticsPooling( + config.enc_channels[-1], + attention_channels=config.enc_attention_channels, + ) + + # Final linear transformation + self.fc = nn.Conv1d( + in_channels=config.enc_channels[-1] * 2, + out_channels=config.enc_dim, + kernel_size=1, + padding="same", + padding_mode="reflect", + ) + + def forward(self, hidden_states): + # Minimize transpose for efficiency + hidden_states = hidden_states.transpose(1, 2) + + hidden_states_list = [] + for layer in self.blocks: + hidden_states = layer(hidden_states) + hidden_states_list.append(hidden_states) + + # Multi-layer feature aggregation + hidden_states = torch.cat(hidden_states_list[1:], dim=1) + hidden_states = self.mfa(hidden_states) + + # Attentive Statistical Pooling + hidden_states = self.asp(hidden_states) + + # Final linear transformation + hidden_states = self.fc(hidden_states) + + hidden_states = hidden_states.squeeze(-1) + return hidden_states + + +class DiTInputEmbedding(nn.Module): + def __init__(self, config: Qwen2_5OmniDiTConfig): + super().__init__() + self.proj = nn.Linear( + config.mel_dim + config.enc_dim + config.enc_emb_dim + config.emb_dim, + config.hidden_size, + ) + self.spk_encoder = ECAPA_TimeDelayNet(config) + + def forward( + self, + hidden_states: torch.Tensor, + speaker_embedding: torch.Tensor, + condition_vector: torch.Tensor, + code_embed: torch.Tensor, + drop_audio_cond: Optional[bool] = False, + code_embed_uncond: Optional[bool] = None, + apply_cfg: Optional[bool] = True, + ): + if apply_cfg: + hidden_states = torch.cat([hidden_states, hidden_states], dim=0) + speaker_embedding = torch.cat([speaker_embedding, torch.zeros_like(speaker_embedding)], dim=0) + condition_vector = torch.cat([condition_vector, torch.zeros_like(condition_vector)], dim=0) + code_embed = torch.cat([code_embed, code_embed_uncond], dim=0) + elif drop_audio_cond: # cfg for cond audio + condition_vector = torch.zeros_like(condition_vector) + speaker_embedding = torch.zeros_like(speaker_embedding) + condition_vector = self.spk_encoder(condition_vector).unsqueeze(1).repeat(1, hidden_states.size(1), 1) + hidden_states = self.proj(torch.cat((hidden_states, condition_vector, code_embed, speaker_embedding), dim=-1)) + + return hidden_states + + +# Transformer backbone using DiT blocks +class DiTCodecEmbedding(nn.Module): + def __init__(self, codec_num_embeds, codec_dim, repeats): + super().__init__() + self.repeats = repeats + self.codec_embed = nn.Embedding(codec_num_embeds + 1, codec_dim) + + def forward(self, code, drop_code=False): + if drop_code: + code = torch.zeros_like(code) + code_embed = self.codec_embed(code) + + code_embed = torch.repeat_interleave(code_embed, repeats=self.repeats, dim=1) + return code_embed + + +# AdaLayerNormZero +# return with modulated x for attn input, and params for later mlp modulation +class Qwen2_5_OmniAdaLayerNormZero(nn.Module): + def __init__(self, dim): + super().__init__() + + self.silu = nn.SiLU() + self.linear = nn.Linear(dim, dim * 6) + + self.norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6) + + def forward(self, hidden_states, emb=None): + emb = self.linear(self.silu(emb)) + shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = torch.chunk(emb, 6, dim=1) + + hidden_states = self.norm(hidden_states) * (1 + scale_msa[:, None]) + shift_msa[:, None] + return hidden_states, gate_msa, shift_mlp, scale_mlp, gate_mlp + + +# AdaLayerNormZero for final layer +# return only with modulated x for attn input, cuz no more mlp modulation +class Qwen2_5_OmniAdaLayerNormZero_Final(nn.Module): + def __init__(self, dim): + super().__init__() + + self.silu = nn.SiLU() + self.linear = nn.Linear(dim, dim * 2) + + self.norm = nn.LayerNorm(dim, elementwise_affine=False, eps=1e-6) + + def forward(self, hidden_states, emb): + emb = self.linear(self.silu(emb)) + scale, shift = torch.chunk(emb, 2, dim=1) + + hidden_states = self.norm(hidden_states) * (1 + scale)[:, None, :] + shift[:, None, :] + return hidden_states + + +# FeedForward +class DiTMLP(nn.Module): + def __init__(self, dim, mult=4, dropout=0.0): + super().__init__() + inner_dim = int(dim * mult) + + self.ff = nn.ModuleList( + [ + nn.Linear(dim, inner_dim), + nn.GELU(approximate="tanh"), + nn.Dropout(dropout), + nn.Linear(inner_dim, dim), + ] + ) + + def forward(self, hidden_states): + for layer in self.ff: + hidden_states = layer(hidden_states) + return hidden_states + + +# Modified from Llama with a different rotate function, will fixed in next release +def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1): + """Applies Rotary Position Embedding to the query and key tensors. + + Args: + q (`torch.Tensor`): The query tensor. + k (`torch.Tensor`): The key tensor. + cos (`torch.Tensor`): The cosine part of the rotary embedding. + sin (`torch.Tensor`): The sine part of the rotary embedding. + position_ids (`torch.Tensor`, *optional*): + Deprecated and unused. + unsqueeze_dim (`int`, *optional*, defaults to 1): + The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and + sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note + that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and + k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes + cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have + the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2. + Returns: + `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding. + """ + + def rotate_half_codec(x): + # x = rearrange(x, "... (d r) -> ... d r", r=2) + x = x.reshape(*x.shape[:-1], -1, 2) + x1, x2 = x.unbind(dim=-1) + x = torch.stack((-x2, x1), dim=-1) + return x.reshape(*x.shape[:-2], -1) + + cos = cos.unsqueeze(unsqueeze_dim) + sin = sin.unsqueeze(unsqueeze_dim) + q_embed = (q * cos) + (rotate_half_codec(q) * sin) + k_embed = (k * cos) + (rotate_half_codec(k) * sin) + return q_embed, k_embed + + +class DiTAttention(nn.Module): + def __init__(self, config: Qwen2_5OmniDiTConfig): + super().__init__() + + self.config = config + self.dim = config.hidden_size + self.heads = config.num_attention_heads + self.inner_dim = config.head_dim * config.num_attention_heads + self.dropout = config.dropout + self.is_causal = False + + self.to_q = nn.Linear(config.hidden_size, self.inner_dim) + self.to_k = nn.Linear(config.hidden_size, self.inner_dim) + self.to_v = nn.Linear(config.hidden_size, self.inner_dim) + + self.to_out = nn.ModuleList([nn.Linear(self.inner_dim, config.hidden_size), nn.Dropout(config.dropout)]) + + def forward( + self, + hidden_states, # noised input x + position_embeddings=None, # rotary position embedding for x + attention_mask=None, + ) -> torch.Tensor: + batch_size = hidden_states.shape[0] + + # `sample` projections. + query = self.to_q(hidden_states) + key = self.to_k(hidden_states) + value = self.to_v(hidden_states) + + # attention + inner_dim = key.shape[-1] + head_dim = inner_dim // self.heads + query = query.view(batch_size, -1, self.heads, head_dim).transpose(1, 2) + key = key.view(batch_size, -1, self.heads, head_dim).transpose(1, 2) + value = value.view(batch_size, -1, self.heads, head_dim).transpose(1, 2) + + # apply rotary position embedding + # Due to training process, only first head is applied with RoPE, will be fixed at next release + cos, sin = position_embeddings + query[:, :1], key[:, :1] = apply_rotary_pos_emb(query[:, :1], key[:, :1], cos, sin) + + attention_interface = ALL_ATTENTION_FUNCTIONS[self.config._attn_implementation] + attention_weights, _ = attention_interface( + self, + query, + key, + value, + attention_mask=attention_mask, + is_causal=False, + ) + + # mask. e.g. inference got a batch with different target durations, mask out the padding + attention_weights = attention_weights.reshape(batch_size, -1, self.heads * head_dim) + attention_weights = attention_weights.to(query.dtype) + + # linear proj + attention_output = self.to_out[0](attention_weights) + attention_output = self.to_out[1](attention_output) + + return attention_output + + +# time step conditioning embedding +class SinusPositionEmbedding(nn.Module): + def __init__(self, dim): + super().__init__() + self.dim = dim + + def forward(self, hidden_states, scale=1000): + device = hidden_states.device + half_dim = self.dim // 2 + emb = math.log(10000) / (half_dim - 1) + emb = torch.exp(torch.arange(half_dim, device=device).float() * -emb) + emb = scale * hidden_states.unsqueeze(1) * emb.unsqueeze(0) + emb = torch.cat((emb.sin(), emb.cos()), dim=-1) + return emb.type_as(hidden_states) + + +class DiTTimestepEmbedding(nn.Module): + def __init__(self, dim, freq_embed_dim=256): + super().__init__() + self.time_embed = SinusPositionEmbedding(freq_embed_dim) + self.time_mlp = nn.ModuleList([nn.Linear(freq_embed_dim, dim), nn.SiLU(), nn.Linear(dim, dim)]) + + def forward(self, timestep): # noqa: F821 + time_hidden = self.time_embed(timestep) + time_hidden = time_hidden.to(timestep.dtype) + for layer in self.time_mlp: + time_hidden = layer(time_hidden) # b d + return time_hidden + + +class DiTDecoderLayer(nn.Module): + def __init__(self, config: Qwen2_5OmniDiTConfig, look_ahead_block=0, look_backward_block=0): + super().__init__() + self.attn_norm = Qwen2_5_OmniAdaLayerNormZero(config.hidden_size) + + self.attn = DiTAttention(config) + self.look_ahead_block = look_ahead_block + self.look_backward_block = look_backward_block + self.ff_norm = nn.LayerNorm(config.hidden_size, elementwise_affine=False, eps=1e-6) + self.ff = DiTMLP(dim=config.hidden_size, mult=config.ff_mult, dropout=config.dropout) + + def forward( + self, hidden_states, timestep, position_embeddings=None, block_diff=None + ): # x: noised input, t: time embedding + # pre-norm & modulation for attention input + norm, gate_msa, shift_mlp, scale_mlp, gate_mlp = self.attn_norm(hidden_states, emb=timestep) + + # attention + attn_output = self.attn( + hidden_states=norm, + position_embeddings=position_embeddings, + attention_mask=(block_diff >= -float(self.look_backward_block)) + & (block_diff <= float(self.look_ahead_block)), + ) + + # process attention output for input x + hidden_states = hidden_states + gate_msa.unsqueeze(1) * attn_output + + norm = self.ff_norm(hidden_states) * (1 + scale_mlp[:, None]) + shift_mlp[:, None] + ff_output = self.ff(norm) + hidden_states = hidden_states + gate_mlp.unsqueeze(1) * ff_output + + return hidden_states + + +class SnakeBeta(nn.Module): + """ + A modified Snake function which uses separate parameters for the magnitude of the periodic components + Shape: + - Input: (B, C, T) + - Output: (B, C, T), same shape as the input + Parameters: + - alpha - trainable parameter that controls frequency + - beta - trainable parameter that controls magnitude + References: + - This activation function is a modified version based on this paper by Liu Ziyin, Tilman Hartwig, Masahito Ueda: + https://huggingface.co/papers/2006.08195 + """ + + def __init__(self, in_features, alpha=1.0): + super().__init__() + self.in_features = in_features + + # initialize alpha + self.alpha = Parameter(torch.zeros(in_features) * alpha) + self.beta = Parameter(torch.zeros(in_features) * alpha) + + self.no_div_by_zero = 0.000000001 + + def forward(self, hidden_states): + """ + Forward pass of the function. + Applies the function to the input elementwise. + SnakeBeta ∶= x + 1/b * sin^2 (xa) + """ + alpha = self.alpha.unsqueeze(0).unsqueeze(-1) # line up with x to [B, C, T] + beta = self.beta.unsqueeze(0).unsqueeze(-1) + alpha = torch.exp(alpha) + beta = torch.exp(beta) + hidden_states = hidden_states + (1.0 / (beta + self.no_div_by_zero)) * torch.pow( + torch.sin(hidden_states * alpha), 2 + ) + + return hidden_states + + +def kaiser_sinc_filter1d(cutoff, half_width, kernel_size): + """Generates a 1D Kaiser-windowed sinc filter. + + Args: + cutoff (float): Normalized cutoff frequency (0 to 0.5). + half_width (float): Transition bandwidth. + kernel_size (int): Number of filter taps. + + Returns: + torch.Tensor: A tensor of shape (1, 1, kernel_size) representing the filter. + """ + is_even = kernel_size % 2 == 0 + half_size = kernel_size // 2 + + # Compute Kaiser window parameters + delta_f = 4 * half_width + attenuation = 2.285 * (half_size - 1) * math.pi * delta_f + 7.95 + + if attenuation > 50.0: + beta = 0.1102 * (attenuation - 8.7) + elif attenuation >= 21.0: + beta = 0.5842 * (attenuation - 21) ** 0.4 + 0.07886 * (attenuation - 21.0) + else: + beta = 0.0 + + kaiser_window = torch.kaiser_window(kernel_size, beta=beta, periodic=False, dtype=torch.float32) + + # Compute time indices + if is_even: + time_indices = torch.arange(-half_size, half_size) + 0.5 + else: + time_indices = torch.arange(kernel_size) - half_size + + # Compute sinc filter + if cutoff == 0: + return torch.zeros((1, 1, kernel_size), dtype=torch.float32) # Ensures correct shape + + sinc_filter = torch.sinc(2 * cutoff * time_indices) + normalized_filter = 2 * cutoff * kaiser_window * sinc_filter + + # Normalize to ensure sum = 1 (avoid leakage of constant component) + normalized_filter /= normalized_filter.sum() + + return normalized_filter.view(1, 1, kernel_size) + + +class UpSample1d(nn.Module): + def __init__(self, ratio=2, kernel_size=None): + super().__init__() + self.ratio = ratio + self.kernel_size = int(6 * ratio // 2) * 2 if kernel_size is None else kernel_size + self.stride = ratio + self.pad = self.kernel_size // ratio - 1 + self.pad_left = self.pad * self.stride + (self.kernel_size - self.stride) // 2 + self.pad_right = self.pad * self.stride + (self.kernel_size - self.stride + 1) // 2 + + filter = kaiser_sinc_filter1d(cutoff=0.5 / ratio, half_width=0.6 / ratio, kernel_size=self.kernel_size) + self.register_buffer("filter", filter, persistent=False) + + def forward(self, hidden_states): + channels = hidden_states.shape[1] + hidden_states_dtype = hidden_states.dtype + hidden_states = F.pad(hidden_states, (self.pad, self.pad), mode="replicate").to(self.filter.dtype) + hidden_states = self.ratio * F.conv_transpose1d( + hidden_states, self.filter.expand(channels, -1, -1), stride=self.stride, groups=channels + ).to(hidden_states_dtype) + hidden_states = hidden_states[..., self.pad_left : -self.pad_right] + + return hidden_states + + +class DownSample1d(nn.Module): + def __init__(self, ratio=2, kernel_size=None): + super().__init__() + cutoff = 0.5 / ratio + half_width = 0.6 / ratio + + if cutoff < 0.0: + raise ValueError("Minimum cutoff must be larger than zero.") + if cutoff > 0.5: + raise ValueError("A cutoff above 0.5 does not make sense.") + + self.even = kernel_size % 2 == 0 + self.pad_left = kernel_size // 2 - int(self.even) + self.pad_right = kernel_size // 2 + self.stride = ratio + filter = kaiser_sinc_filter1d(cutoff, half_width, kernel_size) + self.register_buffer("filter", filter, persistent=False) + + def forward(self, hidden_states): + channels = hidden_states.shape[1] + hidden_states_dtype = hidden_states.dtype + hidden_states = F.pad(hidden_states, (self.pad_left, self.pad_right), mode="replicate").to(self.filter.dtype) + out = F.conv1d(hidden_states, self.filter.expand(channels, -1, -1), stride=self.stride, groups=channels).to(hidden_states_dtype) + return out + + +class TorchActivation1d(nn.Module): + def __init__( + self, + activation, + up_ratio: int = 2, + down_ratio: int = 2, + up_kernel_size: int = 12, + down_kernel_size: int = 12, + ): + super().__init__() + if not callable(activation): + raise TypeError("Activation function must be callable") + self.act = activation + self.upsample = UpSample1d(up_ratio, up_kernel_size) + self.downsample = DownSample1d(down_ratio, down_kernel_size) + + def forward(self, hidden_states): + hidden_states = self.upsample(hidden_states) + hidden_states = self.act(hidden_states) + hidden_states = self.downsample(hidden_states) + + return hidden_states + + +class AMPBlock(torch.nn.Module): + def __init__( + self, + channels, + kernel_size=3, + dilation=(1, 3, 5), + ): + super().__init__() + + self.convs1 = nn.ModuleList( + [ + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[0], + padding=self._get_padding(kernel_size, dilation[0]), + ), + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[1], + padding=self._get_padding(kernel_size, dilation[1]), + ), + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=dilation[2], + padding=self._get_padding(kernel_size, dilation[2]), + ), + ] + ) + + self.convs2 = nn.ModuleList( + [ + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=self._get_padding(kernel_size, 1), + ), + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=self._get_padding(kernel_size, 1), + ), + nn.Conv1d( + channels, + channels, + kernel_size, + 1, + dilation=1, + padding=self._get_padding(kernel_size, 1), + ), + ] + ) + + self.num_layers = len(self.convs1) + len(self.convs2) # total number of conv layers + + self.activations = nn.ModuleList( + [TorchActivation1d(activation=SnakeBeta(channels)) for _ in range(self.num_layers)] + ) + + def _get_padding(self, kernel_size, dilation=1): + return int((kernel_size * dilation - dilation) / 2) + + def forward(self, hidden_states): + acts1, acts2 = self.activations[::2], self.activations[1::2] + for conv1, conv2, act1, act2 in zip(self.convs1, self.convs2, acts1, acts2): + residual = hidden_states + hidden_states = act1(hidden_states) + hidden_states = conv1(hidden_states) + hidden_states = act2(hidden_states) + hidden_states = conv2(hidden_states) + hidden_states = residual + hidden_states + + return hidden_states + + +@auto_docstring( + custom_intro=""" + The full Qwen2.5Omni Token2WavBigVGAN model. Which take mel spectrogram as input and predict waveform. + """ +) +class Qwen2_5OmniToken2WavBigVGANModel(Qwen2_5OmniPreTrainedModel): + config: Qwen2_5OmniBigVGANConfig + + def __init__(self, config: Qwen2_5OmniBigVGANConfig): + super().__init__(config) + self.num_residual_blocks = len(config.resblock_kernel_sizes) + self.num_upsample_layers = len(config.upsample_rates) + + self.conv_pre = nn.Conv1d(config.mel_dim, config.upsample_initial_channel, 7, 1, padding=3) + + # Removing extra ModuleList breaks official state dict + ups = [ + nn.ModuleList( + [ + nn.ConvTranspose1d( + config.upsample_initial_channel // (2**layer_idx), + config.upsample_initial_channel // (2 ** (layer_idx + 1)), + kernel_size, + stride, + padding=(kernel_size - stride) // 2, + ) + ] + ) + for layer_idx, (stride, kernel_size) in enumerate(zip(config.upsample_rates, config.upsample_kernel_sizes)) + ] + self.ups = nn.ModuleList(ups) + + self.resblocks = nn.ModuleList( + [ + AMPBlock(config.upsample_initial_channel // (2 ** (layer_idx + 1)), kernel_size, dilation) + for layer_idx in range(self.num_upsample_layers) + for kernel_size, dilation in zip(config.resblock_kernel_sizes, config.resblock_dilation_sizes) + ] + ) + + self.activation_post = TorchActivation1d( + activation=SnakeBeta(config.upsample_initial_channel // (2**self.num_upsample_layers)) + ) + self.conv_post = nn.Conv1d( + config.upsample_initial_channel // (2**self.num_upsample_layers), 1, 7, 1, padding=3, bias=False + ) + + def normalize_spectrogram(self, spectrogram, max_value, min_db): + return torch.clamp((2 * max_value) * ((spectrogram - min_db) / (-min_db)) - max_value, -max_value, max_value) + + def amplitude_to_db(self, amplitude, min_db_level): + min_level = torch.exp( + torch.tensor(min_db_level / 20.0 * np.log(10), device=amplitude.device, dtype=amplitude.dtype) + ) + return 20 * torch.log10(torch.clamp(amplitude, min=min_level)) + + def process_mel_spectrogram(self, mel_spectrogram): + amplitude_spectrum = torch.exp(mel_spectrogram) + decibel_spectrum = self.amplitude_to_db(amplitude_spectrum, -115) - 20 + return self.normalize_spectrogram(decibel_spectrum, 1, -115) + + def forward(self, mel_spectrogram): + processed_spectrogram = self.process_mel_spectrogram(mel_spectrogram) + hidden_representation = self.conv_pre(processed_spectrogram) + + for layer_index in range(self.num_upsample_layers): + hidden_representation = self.ups[layer_index][0](hidden_representation) + residual_output = sum( + self.resblocks[layer_index * self.num_residual_blocks + block_index](hidden_representation) + for block_index in range(self.num_residual_blocks) + ) + residual_output = residual_output / self.num_residual_blocks + hidden_representation = residual_output + + hidden_representation = self.activation_post(hidden_representation) + output_waveform = self.conv_post(hidden_representation) + return torch.clamp(output_waveform, min=-1.0, max=1.0).squeeze().cpu() + + +class RungeKutta4ODESolver: + def __init__(self, function, initial_value): + self.function = function + self.initial_value = initial_value + + self._one_third = 1 / 3 + self._two_thirds = 2 / 3 + + def _rk4_step(self, function, time_start, time_step, time_end, value_start, function_value_start=None): + k1 = function_value_start if function_value_start is not None else function(time_start, value_start) + k2 = function(time_start + time_step * self._one_third, value_start + time_step * k1 * self._one_third) + k3 = function(time_start + time_step * self._two_thirds, value_start + time_step * (k2 - k1 * self._one_third)) + k4 = function(time_end, value_start + time_step * (k1 - k2 + k3)) + return (k1 + 3 * (k2 + k3) + k4) * time_step / 8 + + def _compute_step(self, function, time_start, time_step, time_end, value_start): + function_value_start = function(time_start, value_start) + return self._rk4_step( + function, time_start, time_step, time_end, value_start, function_value_start=function_value_start + ), function_value_start + + def _linear_interpolation(self, time_start, time_end, value_start, value_end, time_point): + if time_point == time_start: + return value_start + if time_point == time_end: + return value_end + weight = (time_point - time_start) / (time_end - time_start) + return value_start + weight * (value_end - value_start) + + def integrate(self, time_points): + solution = torch.empty( + len(time_points), + *self.initial_value.shape, + dtype=self.initial_value.dtype, + device=self.initial_value.device, + ) + solution[0] = self.initial_value + + current_index = 1 + current_value = self.initial_value + for time_start, time_end in zip(time_points[:-1], time_points[1:]): + time_step = time_end - time_start + delta_value, _ = self._compute_step(self.function, time_start, time_step, time_end, current_value) + next_value = current_value + delta_value + + while current_index < len(time_points) and time_end >= time_points[current_index]: + solution[current_index] = self._linear_interpolation( + time_start, time_end, current_value, next_value, time_points[current_index] + ) + current_index += 1 + + current_value = next_value + + return solution + + +@auto_docstring( + custom_intro=""" + The full Qwen2.5Omni Token2WavDiT model. Which take speech tokens as input and predict mel spectrogram. + """ +) +class Qwen2_5OmniToken2WavDiTModel(Qwen2_5OmniPreTrainedModel): + config: Qwen2_5OmniDiTConfig + _no_split_modules = ["DiTDecoderLayer"] + + def __init__(self, config: Qwen2_5OmniDiTConfig): + super().__init__(config) + self.mel_dim = config.mel_dim + self.repeats = config.repeats + self.time_embed = DiTTimestepEmbedding(config.hidden_size) + + self.text_embed = DiTCodecEmbedding(config.num_embeds, config.emb_dim, config.repeats) + self.input_embed = DiTInputEmbedding(config) + + self.rotary_embed = Qwen2_5OmniDiTRotaryEmbedding(config.head_dim) + + self.hidden_size = config.hidden_size + self.layers = config.num_hidden_layers + self.block_size = config.block_size + self.num_attention_heads = config.num_attention_heads + + self.transformer_blocks = nn.ModuleList() + for i in range(config.num_hidden_layers): + self.transformer_blocks.append( + DiTDecoderLayer( + config, + look_ahead_block=1 if i in config.look_ahead_layers else 0, + look_backward_block=1 if i in config.look_backward_layers else 0, + ) + ) + + self.norm_out = Qwen2_5_OmniAdaLayerNormZero_Final(config.hidden_size) # final modulation + self.proj_out = nn.Linear(config.hidden_size, config.mel_dim) + + def _create_block_diff(self, hidden_states): + batch, seq_len = hidden_states.shape[0], hidden_states.shape[1] + block_indices = torch.arange(seq_len, device=hidden_states.device) // self.block_size # [seq_length] + + block_i = block_indices.unsqueeze(1) # [seq_length, 1] + block_j = block_indices.unsqueeze(0) # [1, seq_length] + block_diff = block_j - block_i # (n, n) + + return block_diff.expand(batch, self.num_attention_heads, seq_len, seq_len) + + def forward( + self, + hidden_states, + condition_vector, + speaker_embedding, + quantized_code, + time_step, + drop_audio_conditioning=False, + drop_code=False, + apply_cfg=True, + ): + batch_size = hidden_states.shape[0] + if time_step.ndim == 0: + time_step = time_step.repeat(batch_size) + + # Compute embeddings + time_embedding = self.time_embed(time_step) + text_embedding = self.text_embed(quantized_code, drop_code=False if apply_cfg else drop_code) + text_embedding_unconditioned = self.text_embed(quantized_code, drop_code=True) if apply_cfg else None + + hidden_states = self.input_embed( + hidden_states, + speaker_embedding, + condition_vector, + text_embedding, + drop_audio_cond=drop_audio_conditioning, + code_embed_uncond=text_embedding_unconditioned, + apply_cfg=apply_cfg, + ) + + # Compute positional encodings + position_embeddings = self.rotary_embed(hidden_states) + blockwise_difference = self._create_block_diff(hidden_states) + + # Transformer blocks + for transformer_block in self.transformer_blocks: + hidden_states = transformer_block( + hidden_states, + time_embedding, + position_embeddings=position_embeddings, + block_diff=blockwise_difference, + ) + + hidden_states = self.norm_out(hidden_states, time_embedding) + output = self.proj_out(hidden_states) + + return output + + @torch.no_grad() + def sample( + self, + conditioning_vector, + reference_mel_spectrogram, + quantized_code, + num_steps=10, + guidance_scale=0.5, + sway_coefficient=-1.0, + ): + noise_initialization = torch.randn([1, 30000, self.mel_dim], dtype=reference_mel_spectrogram.dtype) + maximum_duration = quantized_code.shape[1] * self.repeats + initial_state = noise_initialization[:, :maximum_duration].to(quantized_code.device) + batch_size = reference_mel_spectrogram.shape[0] + conditioning_vector = conditioning_vector.unsqueeze(1).repeat(1, maximum_duration, 1) + + if batch_size != 1: + raise ValueError("Only batch size = 1 is currently supported") + + def ode_function(time_step, hidden_states): + if guidance_scale < 1e-5: + prediction = self( + hidden_states=hidden_states, + speaker_embedding=conditioning_vector, + condition_vector=reference_mel_spectrogram, + quantized_code=quantized_code, + time_step=time_step, + drop_audio_conditioning=False, + drop_code=False, + ) + return prediction + + model_output = self( + hidden_states=hidden_states, + quantized_code=quantized_code, + speaker_embedding=conditioning_vector, + condition_vector=reference_mel_spectrogram, + time_step=time_step, + apply_cfg=True, + ) + guided_prediction, null_prediction = torch.chunk(model_output, 2, dim=0) + return guided_prediction + (guided_prediction - null_prediction) * guidance_scale + + initial_time = 0 + time_embedding = torch.linspace( + initial_time, 1, num_steps, device=quantized_code.device, dtype=conditioning_vector.dtype + ) + + if sway_coefficient is not None: + time_embedding += sway_coefficient * (torch.cos(torch.pi / 2 * time_embedding) - 1 + time_embedding) + + ode_solver = RungeKutta4ODESolver(function=ode_function, initial_value=initial_state) + solution_trajectory = ode_solver.integrate(time_embedding) + + generated_waveform = solution_trajectory[-1] + generated_mel_spectrogram = generated_waveform.permute(0, 2, 1) + return generated_mel_spectrogram + + @torch.no_grad() + def fast_block_sample( + self, + conditioning_vector, + reference_mel_spectrogram, + quantized_code, + y0: torch.Tensor, + num_steps=10, + guidance_scale=0.5, + sway_coefficient: Optional[float] = -1.0, + ): + """ + Block-wise ODE sampling starting from provided initial state y0. + + Args: + conditioning_vector: (B, enc_emb_dim) + reference_mel_spectrogram: (B, T_ref, mel_dim) + quantized_code: (B, T_code) + y0: (B, T_target, mel_dim) initial state for ODE + Returns: + mel: (B, mel_dim, T_target) + """ + initial_state = y0.to(quantized_code.device) + batch_size = reference_mel_spectrogram.shape[0] + conditioning_vector = conditioning_vector.unsqueeze(1).repeat(1, initial_state.shape[1], 1) + + if batch_size != 1: + raise ValueError("Only batch size = 1 is currently supported") + + def ode_function(time_step, hidden_states): + if guidance_scale < 1e-5: + prediction = self( + hidden_states=hidden_states, + speaker_embedding=conditioning_vector, + condition_vector=reference_mel_spectrogram, + quantized_code=quantized_code, + time_step=time_step, + drop_audio_conditioning=False, + drop_code=False, + ) + return prediction + + model_output = self( + hidden_states=hidden_states, + quantized_code=quantized_code, + speaker_embedding=conditioning_vector, + condition_vector=reference_mel_spectrogram, + time_step=time_step, + apply_cfg=True, + ) + guided_prediction, null_prediction = torch.chunk(model_output, 2, dim=0) + return guided_prediction + (guided_prediction - null_prediction) * guidance_scale + + initial_time = 0 + time_embedding = torch.linspace( + initial_time, 1, num_steps, device=quantized_code.device, dtype=conditioning_vector.dtype + ) + + if sway_coefficient is not None: + time_embedding += sway_coefficient * (torch.cos(torch.pi / 2 * time_embedding) - 1 + time_embedding) + + ode_solver = RungeKutta4ODESolver(function=ode_function, initial_value=initial_state) + solution_trajectory = ode_solver.integrate(time_embedding) + + generated_waveform = solution_trajectory[-1] + generated_mel_spectrogram = generated_waveform.permute(0, 2, 1) + return generated_mel_spectrogram + + +@auto_docstring( + custom_intro=""" + The full Qwen2.5Omni Token2Wav model. Consists a DiT model take speech tokens as input and predict mel spectrogram and a BigVGAN vocoder take mel spectrogram as input and predict waveform. + """ +) +class Qwen2_5OmniToken2WavModel(Qwen2_5OmniPreTrainedModel): + config: Qwen2_5OmniToken2WavConfig + base_model_prefix = "model" + _no_split_modules = ["Qwen2_5OmniToken2WavDiTModel", "Qwen2_5OmniToken2WavBigVGANModel"] + + def __init__(self, config: Qwen2_5OmniToken2WavConfig): + super().__init__(config) + attn_impl = config._attn_implementation + if config._attn_implementation == "flash_attention_2": + logger.warning_once( + "Qwen2_5OmniToken2WavModel must inference with fp32, but flash_attention_2 only supports fp16 and bf16, " + "attention implementation of Qwen2_5OmniToken2WavModel will fallback to sdpa." + ) + attn_impl = "sdpa" + elif config._attn_implementation == "eager": + logger.warning_once( + "Qwen2_5OmniToken2WavModel does not support eager attention implementation, fall back to sdpa" + ) + attn_impl = "sdpa" + self.code2wav_dit_model = Qwen2_5OmniToken2WavDiTModel._from_config( + config.dit_config, attn_implementation=attn_impl + ) + self.code2wav_bigvgan_model = Qwen2_5OmniToken2WavBigVGANModel._from_config( + config.bigvgan_config, attn_implementation=attn_impl + ) + + # Streaming-related parameters aligned with Qwen2Code2wav + self.factor = self.code2wav_dit_model.repeats # 50Hz=2, 200Hz=4 + # default bs_mel depends on factor + self.bs_mel = 24 if self.factor == 2 else 32 + self.bs_codec = self.bs_mel // self.factor + self.past_cache_size = self.bs_mel * self.factor + self.future_cache_size = self.bs_mel * 1 + self.batched_chunk = 3 + self.chunk_size = self.bs_mel * self.batched_chunk + self.future_size = 20 if self.factor == 2 else 13 + + # codec embedding size for masking EOS out-of-range + try: + self.codec_embed_size = self.code2wav_dit_model.text_embed.codec_embed.weight.size(0) + except Exception: + self.codec_embed_size = -1 + + # vocoder hop length inferred from upsample rates + try: + ups = self.code2wav_bigvgan_model.config.upsample_rates + hop = 1 + for r in ups: + hop *= int(r) + self.vocoder_hop = int(hop) + except Exception: + # fallback to commonly used value + self.vocoder_hop = 240 + + def forward( + self, + code, + conditioning, + reference_mel, + num_steps=10, + guidance_scale=0.5, + sway_coefficient=-1.0, + **kwargs, + ): + """Generates a waveform from input code and conditioning parameters.""" + + mel_spectrogram = self.code2wav_dit_model.sample( + conditioning, + reference_mel, + code, + num_steps=num_steps, + guidance_scale=guidance_scale, + sway_coefficient=sway_coefficient, + ).to(self.code2wav_bigvgan_model.dtype) + + waveform = self.code2wav_bigvgan_model(mel_spectrogram).to(self.dtype) + + return waveform + + # ============== Chunked processing helpers (compat with qwen2_code2wav_dit) ============== + @torch.inference_mode() + def process_chunk_dit_batch( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + code: torch.Tensor, + y0: torch.Tensor, + steps: int, + ) -> torch.Tensor: + """Block-wise DiT: generate mel from initial state y0 for the given code slice.""" + # prevent codec out-of-range (eos) + if self.codec_embed_size > 0: + code = code.clone() + code[code >= self.codec_embed_size] = 0 + mel = self.code2wav_dit_model.fast_block_sample( + conditioning_vector=conditioning, + reference_mel_spectrogram=reference_mel, + quantized_code=code, + y0=y0, + num_steps=steps, + ) + return mel.to(self.code2wav_bigvgan_model.dtype) + + @torch.inference_mode() + def process_chunk_bigvgan_batch(self, mel_batch: torch.Tensor) -> torch.Tensor: + """Vocoder batch: mel -> waveform.""" + return self.code2wav_bigvgan_model(mel_batch) + + @torch.inference_mode() + def process_little_chunk( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + codec_all: torch.Tensor, + y_all: torch.Tensor, + i: int, + steps: int, + prev_generated: torch.Tensor, + finished: bool = False, + ) -> Tuple[Optional[torch.Tensor], torch.Tensor]: + """Streaming per small chunk: returns (mel_or_None, audio_slice).""" + start_index = max(i * self.chunk_size - self.past_cache_size, 0) + end_index = min((i + 1) * self.chunk_size + self.future_cache_size, + codec_all.shape[1] * self.factor) + + y0 = y_all[:, start_index:end_index].reshape(1, -1, self.code2wav_dit_model.mel_dim).contiguous() + codec = codec_all[:, start_index // self.factor:end_index // self.factor].reshape(1, -1).contiguous() + + # generate mel for current window (B, mel_dim, T) + generated = self.process_chunk_dit_batch( + conditioning=conditioning, + reference_mel=reference_mel, + code=codec, + y0=y0, + steps=steps, + ) + + # splice and vocode with 50Hz-style rules + return self._process_chunk_for_50hz( + i=i, + start_index=start_index, + end_index=end_index, + finished=finished, + prev_generated=prev_generated, + generated=generated, + ) + + @torch.inference_mode() + def process_chunk( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + codec_all: torch.Tensor, + y_all: torch.Tensor, + i: int, + steps: int, + prev_generated: Union[torch.Tensor, List[torch.Tensor]], + finished: bool = False, + ) -> Tuple[Union[torch.Tensor, List[torch.Tensor]], torch.Tensor]: + """High-level chunk API aligning to qwen2_code2wav_dit signature.""" + if not isinstance(prev_generated, torch.Tensor): + prev_generated = prev_generated[0] if len(prev_generated) > 0 else None + _mel, audio = self.process_little_chunk( + conditioning=conditioning, + reference_mel=reference_mel, + codec_all=codec_all, + y_all=y_all, + i=i, + steps=steps, + prev_generated=prev_generated, + finished=finished, + ) + return _mel if _mel is not None else prev_generated, audio + + @torch.inference_mode() + def _process_chunk_for_50hz( + self, + i: int, + start_index: int, + end_index: int, + finished: bool, + prev_generated: Optional[torch.Tensor], + generated: torch.Tensor, + ) -> Tuple[torch.Tensor, torch.Tensor]: + """ + Align mel and audio boundaries for 50Hz-like streaming. + + Shapes: + - generated: (B, mel_dim, T_window) + - prev_generated: (B, mel_dim, T_prev) + Returns: + - mel_chunk: (B, mel_dim, T_chunk) + - audio_slice: (T_audio_chunk,) + """ + # Normalize dtype + generated = generated.to(torch.float32) + if i == 0: + mel = generated[:, :, :self.chunk_size] + elif finished: + mel_trim = generated[:, :, self.past_cache_size:] + mel = torch.cat([prev_generated[:, :, -self.future_size * 2:], mel_trim], dim=2) + else: + if start_index == 0: + mel_trim = generated[:, :, i * self.chunk_size:-self.future_cache_size] + else: + mel_trim = generated[:, :, self.past_cache_size:-self.future_cache_size] + mel = torch.cat([prev_generated[:, :, -self.future_size * 2:], mel_trim], dim=2) + + audio = self.code2wav_bigvgan_model(mel) + if i == 0: + audio_output = audio[:-self.future_size * self.vocoder_hop] + elif finished: + audio_output = audio[self.future_size * self.vocoder_hop:] + else: + audio_output = audio[self.future_size * self.vocoder_hop:-self.future_size * self.vocoder_hop] + return mel, audio_output + + +# ================= vLLM-style wrapper for Token2Wav ================= + +class Qwen2_5OmniToken2WavForConditionalGenerationVLLM(nn.Module, SupportsPP): + logger = init_logger(__name__) + + # Map HF weights -> vLLM module names + hf_to_vllm_mapper = _Vllm_WeightsMapper( + orig_to_new_prefix={ + # HF root is 'model.' + "model.": "token2wav_model.", + }) + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + # Expect hf_config to be Token2Wav config + self.config = vllm_config.model_config.hf_config + + # Initialize underlying HF Token2Wav model via registry + self.token2wav = _Vllm_init_vllm_registered_model( + vllm_config=vllm_config, + prefix=_Vllm_maybe_prefix(prefix, "token2wav_model"), + hf_config=self.config, + architectures=["Qwen2_5OmniToken2WavDiTModel"], + ) + + # Provide placeholder to align with vLLM runner expectations + def _empty_intermediate_tensors(): + return None + self.make_empty_intermediate_tensors = _empty_intermediate_tensors + + def get_language_model(self) -> torch.nn.Module: + return self.token2wav + + @property + def sampler(self): + # Token2Wav does not use sampler; return vLLM default for API parity + return get_sampler() + + def forward( + self, + code: torch.Tensor, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + num_steps: int = 10, + guidance_scale: float = 0.5, + sway_coefficient: float = -1.0, + intermediate_tensors: Optional[IntermediateTensors] = None, + **kwargs, + ) -> torch.Tensor: + # Delegate to HF token2wav model + return self.token2wav( + code=code, + conditioning=conditioning, + reference_mel=reference_mel, + num_steps=num_steps, + guidance_scale=guidance_scale, + sway_coefficient=sway_coefficient, + **kwargs, + ) + + def compute_logits( + self, + hidden_states: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[torch.Tensor]: + # Token2Wav outputs waveform; logits are not applicable + return hidden_states + + def sample( + self, + logits: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[SamplerOutput]: + return None + + def load_weights_without_buffers(self, weights: Iterable[Tuple[str, torch.Tensor]]) -> Set[str]: + loader = _Vllm_AutoWeightsLoader(self) + loaded = loader.load_weights(weights, mapper=self.hf_to_vllm_mapper) + # Log load summary + try: + total_bytes = 0 + for _, param in self.named_parameters(): + if param is not None and param.data is not None: + total_bytes += param.data.numel() * param.data.element_size() + device = next(self.parameters()).device + self.logger.info( + "[Model Loaded] name=%s, success=%s, size=%.2f MB, device=%s", + self.__class__.__name__, True, total_bytes / (1024**2), str(device)) + except Exception: + pass + return loaded + + def find_all_registers(self): + """ + Find all registered buffers in a PyTorch model. + + Args: + Returns: + dict: Dictionary with buffer names as keys and their properties as values + """ + registers = {} + + # Get all named buffers + for name, buf in self.named_buffers(): + if name in self.state_dict(): + registers[name] = { + 'name': name, + 'buffer': buf + } + return registers + + + #remove buffers from the weights and reload them after loading weights + def remove_buffers_from_weights(self, weights: Iterable[Tuple[str, torch.Tensor]], buffers: dict): + weights_to_load = [] + for key, value in weights: + if key in buffers: + buffers[key]['buffer'] = value + continue + weights_to_load.append((key, value)) + return weights_to_load + + def reload_buffers_to_model(self, buffers: dict): + """ + reload stored buffers from weights to model + """ + loaded_buffers = set() + for name, buf_val in self.named_buffers(): + if name in buffers: + buf_val.copy_(buffers[name]['buffer']) + loaded_buffers.add(name) + return loaded_buffers + + def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]], spk_dict_path: str) -> Set[str]: + buffers = self.find_all_registers() + weights_to_load = self.remove_buffers_from_weights(weights, buffers) + loaded = self.load_weights_without_buffers(weights_to_load) + loaded_buffers = self.reload_buffers_to_model(buffers) + #merge loaded and loaded_buffers + loaded.update(loaded_buffers) + self.spk_dict = torch.load(spk_dict_path) + return loaded + + # ============== Optional chunked helpers for API parity ============== + @torch.inference_mode() + def process_chunk_dit_batch( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + code: torch.Tensor, + y0: torch.Tensor, + steps: int, + ) -> torch.Tensor: + return self.token2wav( + code=code, + conditioning=conditioning, + reference_mel=reference_mel, + num_steps=steps, + ) + + @torch.inference_mode() + def process_chunk_bigvgan_batch(self, mel_batch: torch.Tensor) -> Optional[torch.Tensor]: + # BigVGAN is not part of this wrapper; return None for parity. + return None + + @torch.inference_mode() + def process_little_chunk( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + codec_all: torch.Tensor, + y_all: torch.Tensor, + i: int, + steps: int, + prev_generated: torch.Tensor, + finished: bool = False, + ) -> Tuple[Optional[torch.Tensor], torch.Tensor]: + mel = self.token2wav( + code=codec_all, + conditioning=conditioning, + reference_mel=reference_mel, + num_steps=steps, + ) + return None, mel + + @torch.inference_mode() + def process_chunk( + self, + conditioning: torch.Tensor, + reference_mel: torch.Tensor, + codec_all: torch.Tensor, + y_all: torch.Tensor, + i: int, + steps: int, + prev_generated: Union[torch.Tensor, List[torch.Tensor]], + finished: bool = False, + ) -> Tuple[Union[torch.Tensor, List[torch.Tensor]], torch.Tensor]: + _mel, out = self.process_little_chunk( + conditioning=conditioning, + reference_mel=reference_mel, + codec_all=codec_all, + y_all=y_all, + i=i, + steps=steps, + prev_generated=prev_generated if isinstance(prev_generated, torch.Tensor) else None, + finished=finished, + ) + return _mel if _mel is not None else prev_generated, out \ No newline at end of file diff --git a/vllm_omni/model_executor/models/qwen2_old.py b/vllm_omni/model_executor/models/qwen2_old.py new file mode 100644 index 000000000..6528bca78 --- /dev/null +++ b/vllm_omni/model_executor/models/qwen2_old.py @@ -0,0 +1,539 @@ +from typing import Iterable, Optional, Set, Tuple, Union + +import torch +from torch import nn +from transformers import Qwen2Config + +from vllm.attention import Attention, AttentionType +from vllm.compilation.decorators import support_torch_compile +from vllm.config import CacheConfig, VllmConfig +from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size +from vllm.logger import init_logger +from vllm.model_executor.layers.activation import SiluAndMul +from vllm.model_executor.layers.layernorm import RMSNorm +from vllm.model_executor.layers.linear import (MergedColumnParallelLinear, + QKVParallelLinear, + RowParallelLinear) +from vllm.model_executor.layers.logits_processor import LogitsProcessor +from vllm.model_executor.layers.pooler import Pooler, PoolingType +from vllm.model_executor.layers.quantization import QuantizationConfig +from vllm.model_executor.layers.rotary_embedding import get_rope +from vllm.model_executor.layers.sampler import SamplerOutput, get_sampler +from vllm.model_executor.layers.vocab_parallel_embedding import ( + ParallelLMHead, VocabParallelEmbedding) +from vllm.model_executor.model_loader.weight_utils import ( + default_weight_loader, maybe_remap_kv_scale_name) +from vllm.v1.pool.metadata import PoolingMetadata +from vllm.model_executor.sampling_metadata import SamplingMetadata +from vllm.sequence import IntermediateTensors, PoolerOutput + +from vllm.model_executor.models.interfaces import SupportsLoRA, SupportsPP +from vllm.model_executor.models.utils import (AutoWeightsLoader, PPMissingLayer, WeightsMapper, + is_pp_missing_parameter, + make_empty_intermediate_tensors_factory, make_layers, + maybe_prefix) + +logger = init_logger(__name__) + + +class Qwen2MLP(nn.Module): + + def __init__( + self, + hidden_size: int, + intermediate_size: int, + hidden_act: str, + quant_config: Optional[QuantizationConfig] = None, + prefix: str = "", + ) -> None: + super().__init__() + self.gate_up_proj = MergedColumnParallelLinear( + hidden_size, + [intermediate_size] * 2, + bias=False, + quant_config=quant_config, + prefix=f"{prefix}.gate_up_proj", + ) + self.down_proj = RowParallelLinear( + intermediate_size, + hidden_size, + bias=False, + quant_config=quant_config, + prefix=f"{prefix}.down_proj", + ) + if hidden_act != "silu": + raise ValueError(f"Unsupported activation: {hidden_act}. " + "Only silu is supported for now.") + self.act_fn = SiluAndMul() + + def forward(self, x): + gate_up, _ = self.gate_up_proj(x) + x = self.act_fn(gate_up) + x, _ = self.down_proj(x) + return x + + +class Qwen2Attention(nn.Module): + + def __init__(self, + hidden_size: int, + num_heads: int, + num_kv_heads: int, + max_position: int = 4096 * 32, + rope_theta: float = 10000, + head_dim: Optional[int] = None, + cache_config: Optional[CacheConfig] = None, + quant_config: Optional[QuantizationConfig] = None, + rope_scaling: Optional[Tuple] = None, + prefix: str = "", + attn_type: str = AttentionType.DECODER) -> None: + super().__init__() + self.hidden_size = hidden_size + tp_size = get_tensor_model_parallel_world_size() + self.total_num_heads = num_heads + assert self.total_num_heads % tp_size == 0 + self.num_heads = self.total_num_heads // tp_size + self.total_num_kv_heads = num_kv_heads + if self.total_num_kv_heads >= tp_size: + # Number of KV heads is greater than TP size, so we partition + # the KV heads across multiple tensor parallel GPUs. + assert self.total_num_kv_heads % tp_size == 0 + else: + # Number of KV heads is less than TP size, so we replicate + # the KV heads across multiple tensor parallel GPUs. + assert tp_size % self.total_num_kv_heads == 0 + self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size) + self.head_dim = head_dim or (hidden_size // self.total_num_heads) + self.q_size = self.num_heads * self.head_dim + self.kv_size = self.num_kv_heads * self.head_dim + self.scaling = self.head_dim**-0.5 + self.rope_theta = rope_theta + + self.qkv_proj = QKVParallelLinear( + hidden_size, + self.head_dim, + self.total_num_heads, + self.total_num_kv_heads, + bias=True, + quant_config=quant_config, + prefix=f"{prefix}.qkv_proj", + ) + self.o_proj = RowParallelLinear( + self.total_num_heads * self.head_dim, + hidden_size, + bias=False, + quant_config=quant_config, + prefix=f"{prefix}.o_proj", + ) + + self.rotary_emb = get_rope( + self.head_dim, + rotary_dim=self.head_dim, + max_position=max_position, + base=self.rope_theta, + rope_scaling=rope_scaling, + ) + self.attn = Attention(self.num_heads, + self.head_dim, + self.scaling, + num_kv_heads=self.num_kv_heads, + cache_config=cache_config, + quant_config=quant_config, + prefix=f"{prefix}.attn", + attn_type=attn_type) + + def forward( + self, + positions: torch.Tensor, + hidden_states: torch.Tensor, + ) -> torch.Tensor: + qkv, _ = self.qkv_proj(hidden_states) + q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1) + q, k = self.rotary_emb(positions, q, k) + attn_output = self.attn(q, k, v) + output, _ = self.o_proj(attn_output) + return output + + +class Qwen2DecoderLayer(nn.Module): + + def __init__( + self, + config: Qwen2Config, + cache_config: Optional[CacheConfig] = None, + quant_config: Optional[QuantizationConfig] = None, + prefix: str = "", + ) -> None: + super().__init__() + self.hidden_size = config.hidden_size + # Requires transformers > 4.32.0 + rope_theta = getattr(config, "rope_theta", 1000000) + rope_scaling = getattr(config, "rope_scaling", None) + + # By default, Qwen2 uses causal attention as it is a decoder-only model. + # You can override the HF config with `is_causal=False` to enable + # bidirectional attention, which is used in some embedding models + # (e.g. Alibaba-NLP/gte-Qwen2-7B-instruct) + if getattr(config, "is_causal", True): + attn_type = AttentionType.DECODER + else: + attn_type = AttentionType.ENCODER_ONLY + + self.self_attn = Qwen2Attention( + hidden_size=self.hidden_size, + num_heads=config.num_attention_heads, + max_position=config.max_position_embeddings, + num_kv_heads=config.num_key_value_heads, + rope_theta=rope_theta, + cache_config=cache_config, + quant_config=quant_config, + rope_scaling=rope_scaling, + head_dim=getattr(config, "head_dim", None), + prefix=f"{prefix}.self_attn", + attn_type=attn_type, + ) + self.mlp = Qwen2MLP( + hidden_size=self.hidden_size, + intermediate_size=config.intermediate_size, + hidden_act=config.hidden_act, + quant_config=quant_config, + prefix=f"{prefix}.mlp", + ) + self.input_layernorm = RMSNorm(config.hidden_size, + eps=config.rms_norm_eps) + self.post_attention_layernorm = RMSNorm(config.hidden_size, + eps=config.rms_norm_eps) + + def forward( + self, + positions: torch.Tensor, + hidden_states: torch.Tensor, + residual: Optional[torch.Tensor], + ) -> Tuple[torch.Tensor, torch.Tensor]: + # Self Attention + if residual is None: + residual = hidden_states + hidden_states = self.input_layernorm(hidden_states) + else: + hidden_states, residual = self.input_layernorm( + hidden_states, residual) + hidden_states = self.self_attn( + positions=positions, + hidden_states=hidden_states, + ) + + # Fully Connected + hidden_states, residual = self.post_attention_layernorm( + hidden_states, residual) + hidden_states = self.mlp(hidden_states) + return hidden_states, residual + + +@support_torch_compile( + dynamic_arg_dims={ + "input_ids": 0, + # positions is of shape (3, seq_len) if mrope is enabled for qwen2-vl, + # otherwise (seq_len, ). + "positions": -1, + "intermediate_tensors": 0, + "inputs_embeds": 0, + }) +class Qwen2Model(nn.Module): + + def __init__(self, + *, + vllm_config: VllmConfig, + prefix: str = "", + decoder_layer_type: type[nn.Module] = Qwen2DecoderLayer): + super().__init__() + + config = vllm_config.model_config.hf_config + cache_config = vllm_config.cache_config + quant_config = vllm_config.quant_config + + # TODO (@robertgshaw2): see if this can be moved out + if (cache_config.sliding_window is not None + and hasattr(config, "max_window_layers")): + raise ValueError("Sliding window for some but all layers is not " + "supported. This model uses sliding window " + "but `max_window_layers` = {} is less than " + "`num_hidden_layers` = {}. Please open an issue " + "to discuss this feature.".format( + config.max_window_layers, + config.num_hidden_layers, + )) + + self.config = config + self.quant_config = quant_config + self.vocab_size = config.vocab_size + + if get_pp_group().is_first_rank or (config.tie_word_embeddings + and get_pp_group().is_last_rank): + self.embed_tokens = VocabParallelEmbedding( + config.vocab_size, + getattr(config, "embedding_size", config.hidden_size), + quant_config=quant_config, + prefix=f"{prefix}.embed_tokens", + ) + else: + self.embed_tokens = PPMissingLayer() + + # Use the provided decoder layer type or default to Qwen2DecoderLayer + decoder_layer_type = decoder_layer_type or Qwen2DecoderLayer + self.start_layer, self.end_layer, self.layers = make_layers( + config.num_hidden_layers, + lambda prefix: decoder_layer_type(config=config, + cache_config=cache_config, + quant_config=quant_config, + prefix=prefix), + prefix=f"{prefix}.layers", + ) + + self.make_empty_intermediate_tensors = ( + make_empty_intermediate_tensors_factory( + ["hidden_states", "residual"], config.hidden_size)) + if get_pp_group().is_last_rank: + self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps) + else: + self.norm = PPMissingLayer() + + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor: + return self.embed_tokens(input_ids) + + def forward( + self, + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors] = None, + inputs_embeds: Optional[torch.Tensor] = None, + ) -> Union[torch.Tensor, IntermediateTensors]: + if get_pp_group().is_first_rank: + if inputs_embeds is not None: + hidden_states = inputs_embeds + else: + hidden_states = self.get_input_embeddings(input_ids) + residual = None + else: + assert intermediate_tensors is not None + hidden_states = intermediate_tensors["hidden_states"] + residual = intermediate_tensors["residual"] + for layer in self.layers[self.start_layer:self.end_layer]: + hidden_states, residual = layer( + positions, + hidden_states, + residual, + ) + if not get_pp_group().is_last_rank: + return IntermediateTensors({ + "hidden_states": hidden_states, + "residual": residual + }) + hidden_states, _ = self.norm(hidden_states, residual) + return hidden_states + + def load_weights(self, weights: Iterable[Tuple[str, + torch.Tensor]]) -> Set[str]: + stacked_params_mapping = [ + # (param_name, shard_name, shard_id) + ("qkv_proj", "q_proj", "q"), + ("qkv_proj", "k_proj", "k"), + ("qkv_proj", "v_proj", "v"), + ("gate_up_proj", "gate_proj", 0), + ("gate_up_proj", "up_proj", 1), + ] + params_dict = dict(self.named_parameters(remove_duplicate=False)) + loaded_params: Set[str] = set() + for name, loaded_weight in weights: + if "rotary_emb.inv_freq" in name: + continue + if (self.quant_config is not None and + (scale_name := self.quant_config.get_cache_scale(name))): + # Loading kv cache quantization scales + param = params_dict[scale_name] + weight_loader = getattr(param, "weight_loader", + default_weight_loader) + loaded_weight = (loaded_weight if loaded_weight.dim() == 0 else + loaded_weight[0]) + weight_loader(param, loaded_weight) + loaded_params.add(scale_name) + continue + for (param_name, weight_name, shard_id) in stacked_params_mapping: + if weight_name not in name: + continue + name = name.replace(weight_name, param_name) + # Skip loading extra bias for GPTQ models. + if name.endswith(".bias") and name not in params_dict: + continue + if is_pp_missing_parameter(name, self): + continue + param = params_dict[name] + weight_loader = param.weight_loader + weight_loader(param, loaded_weight, shard_id) + break + else: + # Skip loading extra bias for GPTQ models. + if name.endswith(".bias") and name not in params_dict: + continue + # Remapping the name of FP8 kv-scale. + name = maybe_remap_kv_scale_name(name, params_dict) + if name is None: + continue + if is_pp_missing_parameter(name, self): + continue + param = params_dict[name] + weight_loader = getattr(param, "weight_loader", + default_weight_loader) + weight_loader(param, loaded_weight) + loaded_params.add(name) + return loaded_params + + +class Qwen2ForCausalLM(nn.Module, SupportsLoRA, SupportsPP): + packed_modules_mapping = { + "qkv_proj": [ + "q_proj", + "k_proj", + "v_proj", + ], + "gate_up_proj": [ + "gate_proj", + "up_proj", + ], + } + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + config = vllm_config.model_config.hf_config + quant_config = vllm_config.quant_config + lora_config = vllm_config.lora_config + + self.config = config + self.lora_config = lora_config + + self.quant_config = quant_config + self.model = Qwen2Model(vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "model")) + + if get_pp_group().is_last_rank: + if config.tie_word_embeddings: + self.lm_head = self.model.embed_tokens + else: + self.lm_head = ParallelLMHead(config.vocab_size, + config.hidden_size, + quant_config=quant_config, + prefix=maybe_prefix( + prefix, "lm_head")) + else: + self.lm_head = PPMissingLayer() + + self.logits_processor = LogitsProcessor(config.vocab_size) + self.sampler = get_sampler() + + self.make_empty_intermediate_tensors = ( + self.model.make_empty_intermediate_tensors) + + def get_input_embeddings(self, input_ids: torch.Tensor) -> torch.Tensor: + return self.model.get_input_embeddings(input_ids) + + def forward( + self, + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors] = None, + inputs_embeds: Optional[torch.Tensor] = None, + ) -> Union[torch.Tensor, IntermediateTensors]: + hidden_states = self.model(input_ids, positions, intermediate_tensors, + inputs_embeds) + return hidden_states + + def compute_logits( + self, + hidden_states: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[torch.Tensor]: + logits = self.logits_processor(self.lm_head, hidden_states, + sampling_metadata) + return logits + + def sample( + self, + logits: torch.Tensor, + sampling_metadata: SamplingMetadata, + ) -> Optional[SamplerOutput]: + next_tokens = self.sampler(logits, sampling_metadata) + return next_tokens + + def load_weights(self, weights: Iterable[Tuple[str, + torch.Tensor]]) -> Set[str]: + loader = AutoWeightsLoader( + self, + skip_prefixes=(["lm_head."] + if self.config.tie_word_embeddings else None), + ) + return loader.load_weights(weights) + + +class Qwen2EmbeddingModel(nn.Module, SupportsLoRA, SupportsPP): + packed_modules_mapping = { + "qkv_proj": [ + "q_proj", + "k_proj", + "v_proj", + ], + "gate_up_proj": [ + "gate_proj", + "up_proj", + ], + } + + hf_to_vllm_mapper = WeightsMapper(orig_to_new_prefix={"model.": ""}) + + def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""): + super().__init__() + config = vllm_config.model_config.hf_config + quant_config = vllm_config.quant_config + lora_config = vllm_config.lora_config + pooler_config = vllm_config.model_config.pooler_config + + self.config = config + self.lora_config = lora_config + + self.quant_config = quant_config + self.model = Qwen2Model(vllm_config=vllm_config, + prefix=maybe_prefix(prefix, "model")) + + # TODO: Replace this model class with as_embedding_model( + # Qwen2ForCausalLM) after changing the default pooling method + if pooler_config.pooling_type is None: + logger.warning( + "This embedding model will default to last-token pooling in " + "an upcoming version. To avoid breaking changes, you should " + "pass `--override-pooler-config '{\"pooling_type\": \"MEAN\"}'`" + " explicitly.") + + self._pooler = Pooler.from_config_with_defaults( + pooler_config, + pooling_type=PoolingType.MEAN, + normalize=True, + softmax=False) + + def forward( + self, + input_ids: torch.Tensor, + positions: torch.Tensor, + intermediate_tensors: Optional[IntermediateTensors] = None, + ) -> torch.Tensor: + return self.model(input_ids, positions, intermediate_tensors) + + def pooler( + self, + hidden_states: torch.Tensor, + pooling_metadata: PoolingMetadata, + ) -> Optional[PoolerOutput]: + return self._pooler(hidden_states, pooling_metadata) + + def load_weights(self, weights: Iterable[Tuple[str, torch.Tensor]]): + weights = self.hf_to_vllm_mapper.apply(weights) + weights = ((name, data) for name, data in weights + if not name.startswith("lm_head.")) + self.model.load_weights(weights) \ No newline at end of file diff --git a/vllm_omni/model_executor/models/utils.py b/vllm_omni/model_executor/models/utils.py new file mode 100644 index 000000000..7e915d937 --- /dev/null +++ b/vllm_omni/model_executor/models/utils.py @@ -0,0 +1,8 @@ +from vllm.model_executor.models.utils import maybe_prefix + + +def add_prefix_to_loaded_weights(weights: set[str], prefix: str) -> set[str]: + """ + Add a prefix to the names of the loaded weights. + """ + return {maybe_prefix(prefix, name) for name in weights} \ No newline at end of file From 1714777c8d10cf19904dbfe359503dca9bef0fdb Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Thu, 23 Oct 2025 14:33:33 +0800 Subject: [PATCH 16/25] Add example of audio file --- .../qwen_2_5_omni/output_audio/output_0.wav | Bin 0 -> 791084 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/offline_inference/qwen_2_5_omni/output_audio/output_0.wav diff --git a/examples/offline_inference/qwen_2_5_omni/output_audio/output_0.wav b/examples/offline_inference/qwen_2_5_omni/output_audio/output_0.wav new file mode 100644 index 0000000000000000000000000000000000000000..45caf1d0e233995a367f28f593ef7c6763f9fc55 GIT binary patch literal 791084 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 z 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

    JlC6ckA{w@P%6v7etnpXP#fbSsGi`mz$vCA_ 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 Date: Thu, 23 Oct 2025 14:35:23 +0800 Subject: [PATCH 17/25] match runner, worker name with file name --- vllm_omni/worker/gpu_ar_model_runner.py | 2 +- vllm_omni/worker/gpu_ar_worker.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/vllm_omni/worker/gpu_ar_model_runner.py b/vllm_omni/worker/gpu_ar_model_runner.py index 7e8ec3135..73e509bd0 100644 --- a/vllm_omni/worker/gpu_ar_model_runner.py +++ b/vllm_omni/worker/gpu_ar_model_runner.py @@ -30,7 +30,7 @@ from vllm_omni.outputs import OmniModelRunnerOutput from vllm_omni.worker.gpu_model_runner import OmniGPUModelRunner -class ARModelRunner(OmniGPUModelRunner): +class GPUARModelRunner(OmniGPUModelRunner): """Autoregressive GPU model runner that returns hidden states per request. This runner follows the same preparation and forward path as GPUModelRunner diff --git a/vllm_omni/worker/gpu_ar_worker.py b/vllm_omni/worker/gpu_ar_worker.py index 07e080884..fb22ce15f 100644 --- a/vllm_omni/worker/gpu_ar_worker.py +++ b/vllm_omni/worker/gpu_ar_worker.py @@ -9,10 +9,10 @@ from vllm.v1.worker.gpu_worker import init_worker_distributed_environment from vllm.model_executor import set_random_seed from vllm.v1.utils import report_usage_stats -from vllm_omni.worker.AR_gpu_model_runner import ARModelRunner +from vllm_omni.worker.gpu_ar_model_runner import GPUARModelRunner -class ARGPUWorker(GPUWorker): +class GPUARWorker(GPUWorker): def init_device(self): if self.device_config.device.type == "cuda": # torch.distributed.all_reduce does not free the input tensor until @@ -59,7 +59,7 @@ class ARGPUWorker(GPUWorker): set_random_seed(self.model_config.seed) # Construct the model runner - self.model_runner: ARModelRunner = ARModelRunner( + self.model_runner: GPUARModelRunner = GPUARModelRunner( self.vllm_config, self.device) if self.rank == 0: From f5c519c44a749d840d01a968e8ba449ab8269c46 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Thu, 23 Oct 2025 15:18:13 +0800 Subject: [PATCH 18/25] update README --- README.md | 125 ++++++++++++------------------------------------------ 1 file changed, 27 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 66c8725ee..20abbb608 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Traditional vLLM systems are limited to text-based, autoregressive generation. v - **Multi-modal Models**: Text, image, video, audio, and sensor data processing - **Non-autoregressive Architectures**: Diffusion Transformers (DiT) and other parallel generation models -- **Heterogeneous Outputs**: Beyond traditional text generation to structured, binary, and streaming outputs +- **Heterogeneous Outputs**: Beyond traditional text generation to multimodal outputs ## 🏗️ Architecture @@ -28,119 +28,48 @@ vLLM-omni is built on a modular architecture that extends vLLM's core functional - **Text**: Advanced tokenization and embedding generation - **Image**: Vision encoder integration (CLIP, etc.) - **Audio**: Speech processing and audio embedding -- **Video**: Frame-by-frame and temporal processing -- **Sensor**: IoT and sensor data interpretation - -### Output Formats - -- **Structured Data**: JSON, XML, and custom formats -- **Binary Outputs**: Images, audio, and video generation -- **Streaming**: Real-time progressive generation -- **Multipart**: Combined multi-modal responses ## 📋 Supported Models ### AR + Diffusion Transformer (DiT) Models -- Qwen-Image (Image generation and editing) - Qwen-omni (Thinker-Talker-Codec structure) -- Custom DiT and hiybrid architectures +- HunyunaImage 3.0 (Ongoing) +- Qwen-Image (Ongoing) ## 🛠️ Installation -### Quick Start - -#### Option 1: Docker (Recommended for macOS) +Set up basic environments +```bash +uv venv --python 3.12 --seed +source .venv/bin/activate +``` +Install certain version of vllm with commitid: 808a7b69df479b6b3a16181711cac7ca28a9b941 ```bash -# Clone the repository -git clone https://github.com/hsliuustc0106/vllm-omni.git -cd vllm-omni - -# Run the automated Docker setup -./scripts/docker-setup-macos.sh +git clone https://github.com/vllm-project/vllm.git +cd vllm +git checkout 808a7b69df479b6b3a16181711cac7ca28a9b941 +VLLM_USE_PRECOMPILED=1 uv pip install --editable . ``` -#### Option 2: Local Installation +## Run examples (Qwen2.5-omni) +Get into the example folder ```bash -# Clone the repository -git clone https://github.com/hsliuustc0106/vllm-omni.git -cd vllm-omni - -# Run the installation script -./install.sh +cd vllm_omni +cd examples/offline_inference/qwen2_5_omni ``` - -### Prerequisites - -- Python 3.11+ (recommended) -- Conda or Miniconda -- Git -- CUDA 11.8+ (for GPU acceleration) or CPU-only installation - -### Installation Methods - -#### Method 1: Automated Installation (Recommended) +Modify PYTHONPATH in run.sh as your path of vllm_omni. Then run. ```bash -# Using shell script -./install.sh - -# Or using Python script -python install.py +bash run.sh ``` +The output audio is saved in ./output_audio -#### Method 2: Manual Installation -```bash -# Create conda environment -conda create -n vllm_omni python=3.11 -y -conda activate vllm_omni +## To-do list +- [x] Offline inference example for Qwen2.5-omni with single request +- [ ] Adaptation from current vllm branch to stable vllm v0.11.0 +- [ ] Offline inference example for Qwen2.5-omni with streaming multiple requests +- [ ] Online inference support +- [ ] Support for other models -# Install PyTorch (CPU or GPU) -pip install torch>=2.7 --index-url https://download.pytorch.org/whl/cpu # CPU -# pip install torch>=2.7 --index-url https://download.pytorch.org/whl/cu121 # GPU - -# Install dependencies -pip install -r requirements.txt -pip install "vllm>=0.10.2" - -# Install vLLM-omni -pip install -e . -``` - -### Verify Installation - -```bash -# Test the installation -python test_installation.py - -# Test basic functionality -python -c "import vllm_omni; print('Ready!')" - -# Test CLI -vllm --help -``` - -For detailed installation instructions, see [INSTALL.md](INSTALL.md). - -## 📥 Model Download - -Models are automatically downloaded when first used, or you can pre-download them: - -```bash -# Check downloaded models -python scripts/download_models.py --check-cache - -# Download all default models -python scripts/download_models.py --all - -# Download specific models -python scripts/download_models.py --ar-models Qwen/Qwen3-0.6B -python scripts/download_models.py --dit-models stabilityai/stable-diffusion-2-1 -``` - -**Model Storage Location:** -- Default: `~/.cache/huggingface/hub/` -- AR models: 100MB - 1GB each -- DiT models: 2GB - 7GB each - -For detailed model management, see [MODEL_DOWNLOAD_GUIDE.md](docs/MODEL_DOWNLOAD_GUIDE.md). +For detailed model management, see [vllm_omni_design.md](docs/architecture/vllm_omni_design.md) and [high_level_arch_design.md](docs/architecture/high_level_arch_design.md). \ No newline at end of file From 8a6e298243bd7466bd8c3570f23bb59f1fb72aaa Mon Sep 17 00:00:00 2001 From: Zhou Taichang Date: Fri, 24 Oct 2025 09:43:45 +0800 Subject: [PATCH 19/25] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Zhou Taichang --- vllm_omni/model_executor/layers/mrope.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_omni/model_executor/layers/mrope.py b/vllm_omni/model_executor/layers/mrope.py index e7871cb38..eb266d641 100644 --- a/vllm_omni/model_executor/layers/mrope.py +++ b/vllm_omni/model_executor/layers/mrope.py @@ -58,7 +58,7 @@ class MRotaryEmbedding(RotaryEmbedding): self.mrope_section = mrope_section if self.mrope_section: - print("Warning: mrope_section check is not disabled in Qwen2.5-Omni, this may cause errors, and should be removed in the future.") + print("Warning: mrope_section check is disabled in Qwen2.5-Omni, this may cause errors, and should be restored in the future.") # assert sum(self.mrope_section) == rotary_dim // 2 def forward( From 52905a0d172e69364e8a0feb5e7ac881bc3d4fc6 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Fri, 24 Oct 2025 10:02:51 +0800 Subject: [PATCH 20/25] Enhance Qwen2.5-Omni model by defining TALKER_CODEC_EOS_TOKEN_ID and TALKER_CODEC_BOS_TOKEN_ID constants. Update audio generation logic to use these constants for improved clarity and maintainability. Refactor tensor initialization to utilize the talker's hidden size. Update comments for better understanding and consistency. --- .../model_executor/models/qwen2_5_omni.py | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/vllm_omni/model_executor/models/qwen2_5_omni.py b/vllm_omni/model_executor/models/qwen2_5_omni.py index b99f2bec3..2a2ba700e 100644 --- a/vllm_omni/model_executor/models/qwen2_5_omni.py +++ b/vllm_omni/model_executor/models/qwen2_5_omni.py @@ -30,6 +30,9 @@ from vllm.model_executor.models.utils import (AutoWeightsLoader, WeightsMapper, from vllm.model_executor.model_loader.weight_utils import download_weights_from_hf from vllm_omni.model_executor.models.utils import add_prefix_to_loaded_weights +TALKER_CODEC_EOS_TOKEN_ID = 8294 +TALKER_CODEC_BOS_TOKEN_ID = 8293 + class OmniOutput(NamedTuple): """Output from the merged Omni model containing both text and audio.""" text_hidden_states: torch.Tensor @@ -308,7 +311,7 @@ class Qwen2_5OmniForConditionalGeneration(nn.Module, SupportsMultiModal, if self.model_stage=="code2wav": code = input_ids if input_ids is not None else torch.zeros(inputs_embeds.shape[0], dtype=torch.long, device=inputs_embeds.device) - audio_tensor = self.generate_audio(code[:-1] if code[-1]==8294 else code, voice_type) + audio_tensor = self.generate_audio(code[:-1] if code[-1]==TALKER_CODEC_EOS_TOKEN_ID else code, voice_type) return OmniOutput( text_hidden_states = None, multimodal_outputs = { @@ -319,8 +322,8 @@ class Qwen2_5OmniForConditionalGeneration(nn.Module, SupportsMultiModal, return OmniOutput( text_hidden_states=torch.cat( [ - torch.zeros([inputs_embeds.shape[0],896], dtype=torch.bfloat16).to(self._module_device(self.model)), - self.talker.thinker_to_talker_proj(self.talker.get_input_embeddings(torch.tensor([8294,8293]).to(torch.bfloat16).to(self._module_device(self.model))))[0] + torch.zeros([inputs_embeds.shape[0],self.talker.config.hidden_size], dtype=torch.bfloat16).to(self._module_device(self.model)), + self.talker.thinker_to_talker_proj(self.talker.get_input_embeddings(torch.tensor([TALKER_CODEC_BOS_TOKEN_ID,TALKER_CODEC_EOS_TOKEN_ID]).to(torch.bfloat16).to(self._module_device(self.model))))[0] ], dim=0), multimodal_outputs=None @@ -352,11 +355,11 @@ class Qwen2_5OmniForConditionalGeneration(nn.Module, SupportsMultiModal, # embed_text_bos_token self.tts_text_spk_token_ids = { - # M02:我是个会说标准普通话、带部分北方口音的男声 + # M02: Male voice with standard Mandarin and a slight northern accent 'm02': 151870, 'Ethan': 151870, - # F030:我是你的二次元虚拟女友 + # F030: Your anime-styled virtual girlfriend 'f030': 151872, 'Chelsie': 151872, } @@ -532,21 +535,22 @@ class Qwen2_5OmniForConditionalGeneration(nn.Module, SupportsMultiModal, def _convert_to_codec_tokens(self, talker_output: torch.Tensor, sampling_metadata: SamplingMetadata) -> torch.Tensor: """ - 参考 HF:使用 talker 的 codec 头得到 logits,抑制 BOS,再贪心选取当前步的下一个 codec token。 + Reference (HF): use the talker's codec head to obtain logits, suppress BOS, + then greedily select the next codec token for the current step. """ with torch.inference_mode(): logits = self.talker.compute_logits(talker_output, None) if logits is None: return torch.zeros((talker_output.size(0), 0), dtype=torch.long, device=talker_output.device) - # 仅抑制 codec_bos,与 HF generate 的 suppress_tokens 行为一致 + # Suppress only codec_bos, consistent with HF generate's suppress_tokens behavior bos_id = None if hasattr(self, 'talker_config') and hasattr(self.talker_config, 'tts_codec_start_token_id'): bos_id = int(getattr(self.talker_config, 'tts_codec_start_token_id')) if bos_id is not None: logits[..., bos_id] = -1e9 - # 取最后一步位置的分布并贪心选取 + # Take the distribution at the last step and select greedily next_id = self.talker.sample(logits, sampling_metadata).sampled_token_ids return next_id.to(dtype=torch.long) From 2c08c04730a909435a5632694b58a8f9bf6d1497 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Fri, 24 Oct 2025 10:17:22 +0800 Subject: [PATCH 21/25] fix for reviews of copilot --- vllm_omni/entrypoints/omni_llm.py | 13 +++++++------ vllm_omni/entrypoints/stage.py | 2 +- .../stage_input_processors/qwen2_5_omni.py | 6 ++++++ 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index 9cac1ee6f..1312af335 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -15,7 +15,7 @@ from vllm.logger import init_logger import vllm.envs as envs from vllm_omni.entrypoints.utils import load_stage_configs_from_model -from vllm_omni.entrypoints.stage import Stage +from vllm_omni.entrypoints.stage import OmniStage from vllm_omni.engine.arg_utils import OmniEngineArgs from vllm_omni.engine.output_processor import MultimodalOutputProcessor from vllm_omni.engine.processor import OmniProcessor @@ -40,8 +40,8 @@ class OmniLLM: def initialize_stages(self, model: str): for stage_config in self.stage_configs: - stage = Stage(stage_config) - stage_llm = StageLLM(model=model, **stage_config.engine_args) + stage = OmniStage(stage_config) + stage_llm = OmniStageLLM(model=model, **stage_config.engine_args) stage.set_engine(stage_llm) self.stage_list.append(stage) @@ -53,7 +53,8 @@ class OmniLLM: ) -> list[OmniRequestOutput]: """Generate text outputs for the given prompts.""" final_outputs: list[OmniRequestOutput] = [] - assert len(sampling_params_list) == len(self.stage_list), "sampling_params_list must be a list of length equal to the number of stages" + if len(sampling_params_list) != len(self.stage_list): + raise ValueError(f"Expected {len(self.stage_list)} sampling params, got {len(sampling_params_list)}") for stage_id, stage in enumerate(self.stage_list): if stage_id > 0: engine_inputs = stage.process_engine_inputs(self.stage_list, prompts) @@ -68,14 +69,14 @@ class OmniLLM: request_output=engine_outputs)) return final_outputs - def _run_generation(self, stage: Stage, sampling_params: SamplingParams, prompts: Union[PromptType, Sequence[PromptType]]): + def _run_generation(self, stage: OmniStage, sampling_params: SamplingParams, prompts: Union[PromptType, Sequence[PromptType]]): engine_outputs = [] for ro in stage.engine.generate(prompts, sampling_params): engine_outputs.append(ro) return engine_outputs -class StageLLM(LLM): +class OmniStageLLM(LLM): def __init__(self, model: str, compilation_config: Optional[Union[int, dict[str, Any], diff --git a/vllm_omni/entrypoints/stage.py b/vllm_omni/entrypoints/stage.py index a130abc6f..ef2249ac3 100644 --- a/vllm_omni/entrypoints/stage.py +++ b/vllm_omni/entrypoints/stage.py @@ -12,7 +12,7 @@ from vllm.v1.engine import EngineCoreOutput from vllm_omni.inputs.data import OmniTokensPrompt -class Stage: +class OmniStage: def __init__(self, stage_config): self.stage_config = stage_config self.engine = None diff --git a/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py index 08585a765..474e2c344 100644 --- a/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py +++ b/vllm_omni/model_executor/stage_input_processors/qwen2_5_omni.py @@ -6,7 +6,13 @@ from vllm.inputs import TextPrompt from vllm_omni.inputs.data import OmniTokensPrompt def thinker2talker(stage_list, engine_input_source, prompt: Union[OmniTokensPrompt, TextPrompt] = None): + if not engine_input_source: + raise ValueError("engine_input_source cannot be empty") source_stage_id = engine_input_source[0] + if source_stage_id >= len(stage_list): + raise IndexError(f"Invalid stage_id: {source_stage_id}") + if stage_list[source_stage_id].engine_outputs is None: + raise RuntimeError(f"Stage {source_stage_id} has no outputs yet") thinker_outputs = stage_list[source_stage_id].engine_outputs talker_inputs = [] multi_modal_data = {thinker_output.request_id: From 61a215fb545be34397a5b5685f304ac42596fc17 Mon Sep 17 00:00:00 2001 From: Gaohan123 Date: Fri, 24 Oct 2025 10:19:35 +0800 Subject: [PATCH 22/25] fix file name --- vllm_omni/entrypoints/omni_llm.py | 2 +- vllm_omni/entrypoints/{stage.py => omni_stage.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename vllm_omni/entrypoints/{stage.py => omni_stage.py} (100%) diff --git a/vllm_omni/entrypoints/omni_llm.py b/vllm_omni/entrypoints/omni_llm.py index 1312af335..cdfe5d53c 100644 --- a/vllm_omni/entrypoints/omni_llm.py +++ b/vllm_omni/entrypoints/omni_llm.py @@ -15,7 +15,7 @@ from vllm.logger import init_logger import vllm.envs as envs from vllm_omni.entrypoints.utils import load_stage_configs_from_model -from vllm_omni.entrypoints.stage import OmniStage +from vllm_omni.entrypoints.omni_stage import OmniStage from vllm_omni.engine.arg_utils import OmniEngineArgs from vllm_omni.engine.output_processor import MultimodalOutputProcessor from vllm_omni.engine.processor import OmniProcessor diff --git a/vllm_omni/entrypoints/stage.py b/vllm_omni/entrypoints/omni_stage.py similarity index 100% rename from vllm_omni/entrypoints/stage.py rename to vllm_omni/entrypoints/omni_stage.py From 634915652d11a38f4661d45de7323ec906b80e78 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Fri, 24 Oct 2025 10:34:41 +0800 Subject: [PATCH 23/25] Remove unused code and comments in GPUDiffusionModelRunner for improved clarity - Deleted the `has_kv_transfer_group` import as it was not utilized. - Cleaned up commented-out code related to model methods, focusing on the forward method. - Added a TODO comment to implement the diffuse method for additional model support. --- vllm_omni/worker/gpu_diffusion_model_runner.py | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/vllm_omni/worker/gpu_diffusion_model_runner.py b/vllm_omni/worker/gpu_diffusion_model_runner.py index 2cf308895..6626844d9 100644 --- a/vllm_omni/worker/gpu_diffusion_model_runner.py +++ b/vllm_omni/worker/gpu_diffusion_model_runner.py @@ -1,5 +1,3 @@ -# SPDX-License-Identifier: Apache-2.0 - from __future__ import annotations from typing import Optional, Union, List @@ -13,7 +11,6 @@ from vllm.v1.worker.gpu_model_runner import ( EMPTY_MODEL_RUNNER_OUTPUT, IntermediateTensors, get_pp_group, - has_kv_transfer_group, set_forward_context, ) from vllm.v1.worker.utils import sanity_check_mm_encoder_outputs @@ -187,21 +184,13 @@ class GPUDiffusionModelRunner(OmniGPUModelRunner): # For Qwen 2.5 Omni's current implementation, we only support the forward method if hasattr(self.model, "forward"): return self.model.forward(**kwargs) - - # if hasattr(self.model, "sample"): - # return self.model.sample(**kwargs) - # if hasattr(self.model, "forward"): - # return self.model.forward(**kwargs) - # if hasattr(self.model, "diffuse"): - # return self.model.diffuse(**kwargs) + #TODO: add the diffuse method for other models raise RuntimeError( "The loaded model does not expose diffusion interfaces 'sample', " "'forward', or 'diffuse'. Please implement one of them or adapt the runner.") - - @torch.inference_mode() def _dummy_run( self, From 6b196dee2423271c1b780eff8e77d839b1a423d2 Mon Sep 17 00:00:00 2001 From: tzhouam Date: Fri, 24 Oct 2025 11:01:32 +0800 Subject: [PATCH 24/25] Enhance error handling in output processing for multimodal tensors - Added logging for exceptions during tensor movement to CPU in OmniRequestState and MultimodalOutputProcessor. - Improved robustness by ensuring the output pipeline continues without crashing on errors. - Updated comments for clarity on error handling behavior. --- vllm_omni/engine/output_processor.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/vllm_omni/engine/output_processor.py b/vllm_omni/engine/output_processor.py index 2b573c40b..211db8545 100644 --- a/vllm_omni/engine/output_processor.py +++ b/vllm_omni/engine/output_processor.py @@ -91,13 +91,15 @@ class OmniRequestState(RequestState): try: t = t.to("cpu") except Exception: - pass + # Best-effort CPU move; keep original device if conversion fails + logger.debug("Failed to move multimodal tensor to CPU", exc_info=True) if self.mm_accumulated is None: self.mm_accumulated = t else: self.mm_accumulated = torch.cat([self.mm_accumulated, t], dim=0) except Exception: - pass + # Log and continue without crashing the output pipeline + logger.exception("Error accumulating multimodal tensor") # Override: do not route to pooling-only path; always create completion # outputs, and attach pooling_result into the CompletionOutput. @@ -153,14 +155,13 @@ class OmniRequestState(RequestState): try: tensor = tensor.detach().to("cpu") except Exception: - pass + logger.debug("Failed to move accumulated multimodal tensor to CPU", exc_info=True) # Attach on the completion output for downstream consumers. if not hasattr(base_output, "multimodal_output"): setattr(base_output, "multimodal_output", {}) setattr(base_output, "multimodal_output", {self.mm_type: tensor}) - except Exception as e: - logger.warning("Error in _new_completion_output", e) - pass + except Exception: + logger.exception("Error in _new_completion_output") return base_output @@ -265,7 +266,7 @@ class MultimodalOutputProcessor(VLLMOutputProcessor): if pooling_output is not None and isinstance(req_state, OmniRequestState): req_state.add_multimodal_tensor(pooling_output, mm_type) except Exception: - pass + logger.debug("Failed to accumulate multimodal tensor for request %s", req_id, exc_info=True) # 3) Create RequestOutput objects, forcing combined mode to keep ids pooling_for_make = pooling_output @@ -285,9 +286,8 @@ class MultimodalOutputProcessor(VLLMOutputProcessor): if not hasattr(ro, "multimodal_output"): setattr(ro, "multimodal_output", {}) ro.multimodal_output[mm_key] = req_state.mm_accumulated - except Exception as e: - logger.warning("Error in process_outputs", e) - pass + except Exception: + logger.exception("Error attaching multimodal payload in process_outputs") if req_state.queue is not None: req_state.queue.put(ro) else: @@ -323,7 +323,7 @@ class MultimodalOutputProcessor(VLLMOutputProcessor): self.output_handlers[output_type](eco) # Fall through to default fixups in case the handler left gaps except Exception: - pass + logger.exception("Error in custom output handler for %s", output_type) if output_type == "image": self._process_image_output(eco) From ee4822327ba4608d54ed79493b19455f13be14af Mon Sep 17 00:00:00 2001 From: tzhouam Date: Fri, 24 Oct 2025 11:08:42 +0800 Subject: [PATCH 25/25] Remove unnecessary numpy imports and update return type in extract_multimodal_outputs method in OmniGPUModelRunner - Eliminated redundant numpy import statements to streamline the code. - Changed the return type of extract_multimodal_outputs from dict to tuple for better clarity on output structure. --- vllm_omni/worker/gpu_model_runner.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/vllm_omni/worker/gpu_model_runner.py b/vllm_omni/worker/gpu_model_runner.py index df021e4a5..d1d73e6f3 100644 --- a/vllm_omni/worker/gpu_model_runner.py +++ b/vllm_omni/worker/gpu_model_runner.py @@ -123,7 +123,6 @@ class OmniGPUModelRunner(GPUModelRunner): try: if getattr(new_req_data, "prompt_embeds", None) is not None: payload = new_req_data.prompt_embeds - import numpy as np dtype = getattr(np, payload.dtype) arr = np.frombuffer(payload.data, dtype=dtype) arr = arr.reshape(payload.shape) @@ -147,7 +146,6 @@ class OmniGPUModelRunner(GPUModelRunner): else: from vllm.v1.engine import AdditionalInformationPayload if isinstance(payload_info, AdditionalInformationPayload): - import numpy as np for k, entry in payload_info.entries.items(): if entry.tensor_data is not None: dt = np.dtype(getattr(entry, "tensor_dtype", "float32")) @@ -613,7 +611,7 @@ class OmniGPUModelRunner(GPUModelRunner): ) @torch.inference_mode() - def extract_multimodal_outputs(self, hidden_states: torch.Tensor) -> dict: + def extract_multimodal_outputs(self, hidden_states: torch.Tensor) -> tuple[torch.Tensor, dict]: if hasattr(self.model, "have_multimodal_outputs") and self.model.have_multimodal_outputs: text_hidden_states = hidden_states.text_hidden_states multimodal_outputs = hidden_states.multimodal_outputs @@ -736,7 +734,6 @@ class OmniGPUModelRunner(GPUModelRunner): else: hidden_states = outputs - logger.warning(f"Multimodal outputs are not returned in the dummy run, need to double check the implementation!") text_hidden_states, multimodal_outputs = self.extract_multimodal_outputs(hidden_states)