reabse 2025/2/9

Signed-off-by: tzhouam <tzhouam@connect.ust.hk>
This commit is contained in:
tzhouam committed 2026-02-09 03:07:43 +00:00
1 parent 03c6a58f69
commit 7e4a604d4d
14 files changed
+208 -146

No files matched your search

@@ -24,6 +24,7 @@ def _make_runner(multimodal_outputs):
None,
None,
multimodal_outputs,
None,
)
runner.kv_connector_output = None
runner.input_batch = _DummyInputBatch()
+9
View File
@@ -65,6 +65,15 @@ class OmniModelConfig(ModelConfig):
def architectures(self) -> list[str]:
return [self.model_arch]
@property
def embedding_size(self):
if self.hf_config_name is not None:
stage_config = getattr(self.hf_config, self.hf_config_name, None)
override = getattr(stage_config, "embedding_size", None)
if override is not None:
return override
return super().embedding_size
def draw_hf_text_config(self):
# transformers' get_text_config method is used to get the text config from thinker_config.
# to handle the case that each model stage has their own text config,
@@ -42,7 +42,13 @@ class SharedMemoryConnector(OmniConnectorBase):
"inline_writes": 0,
}
def put(self, from_stage: str, to_stage: str, put_key: str, data: Any) -> tuple[bool, int, dict[str, Any] | None]:
def put(
self,
from_stage: str,
to_stage: str,
put_key: str,
data: Any,
) -> tuple[bool, int, dict[str, Any] | None]:
try:
# Always serialize first to check size (and for SHM writing)
# Note: For extremely large objects in "inline" mode (e.g. Ray),
@@ -51,9 +57,7 @@ class SharedMemoryConnector(OmniConnectorBase):
payload = self.serialize_obj(data)
size = len(payload)
metadata = {}
# if size > self.threshold:
if True: # TODO: correct put & get logic
if size > self.threshold:
# Use Shared Memory
lock_file = f"/dev/shm/shm_{put_key}_lockfile.lock"
with open(lock_file, "w") as lockf:
@@ -62,13 +66,13 @@ class SharedMemoryConnector(OmniConnectorBase):
fcntl.flock(lockf, fcntl.LOCK_UN)
# meta contains {'name': ..., 'size': ...}
metadata[put_key] = {"shm": meta, "size": size}
metadata = {"shm": meta, "size": size}
self._metrics["shm_writes"] += 1
else:
# Inline - pass bytes directly to avoid double serialization of the object
# We already serialized it to check size, so we pass the bytes.
# The Queue will pickle these bytes (fast), avoiding re-serializing the complex object.
metadata[put_key] = {"inline_bytes": payload, "size": size}
metadata = {"inline_bytes": payload, "size": size}
self._metrics["inline_writes"] += 1
self._metrics["puts"] += 1
@@ -80,7 +84,47 @@ class SharedMemoryConnector(OmniConnectorBase):
logger.error(f"SharedMemoryConnector put failed for req {put_key}: {e}")
return False, 0, None
def get(self, from_stage: str, to_stage: str, get_key: str, metadata=None) -> tuple[Any, int] | None:
def get(
self,
from_stage: str,
to_stage: str,
get_key: str,
metadata=None,
) -> tuple[Any, int] | None:
if metadata is not None:
# Some callers may wrap metadata by request id.
if isinstance(metadata, dict) and get_key in metadata:
metadata = metadata.get(get_key)
if not isinstance(metadata, dict):
return None
if "inline_bytes" in metadata:
try:
obj = self.deserialize_obj(metadata["inline_bytes"])
return obj, int(metadata.get("size", 0))
except Exception as e:
logger.error(f"SharedMemoryConnector inline get failed for req {get_key}: {e}")
return None
if "shm" in metadata:
try:
shm_handle = metadata["shm"]
lock_file = f"/dev/shm/shm_{shm_handle['name']}_lockfile.lock"
with open(lock_file, "w") as lockf:
fcntl.flock(lockf, fcntl.LOCK_SH)
data_bytes = shm_read_bytes(shm_handle)
fcntl.flock(lockf, fcntl.LOCK_UN)
if os.path.exists(lock_file):
os.remove(lock_file)
obj = self.deserialize_obj(data_bytes)
return obj, int(metadata.get("size", 0))
except Exception as e:
logger.error(f"SharedMemoryConnector shm get failed for req {get_key}: {e}")
return None
return None
from multiprocessing import shared_memory as shm_pkg
# Wait for shared memory to be available (with retry logic)
@@ -98,10 +142,10 @@ class SharedMemoryConnector(OmniConnectorBase):
else:
# Max retries reached, return None
logger.warning(f"Shared memory '{get_key}' not found after {max_retries} retries")
return None, 0
return None
if shm is None:
return None, 0
return None
try:
lock_file = f"/dev/shm/shm_{get_key}_lockfile.lock"
+5 -2
View File
@@ -14,7 +14,9 @@ from vllm.multimodal.processing.context import set_request_id
from vllm.multimodal.utils import argsort_mm_positions
from vllm.platforms import current_platform
from vllm.pooling_params import PoolingParams
from vllm.renderers.inputs import DictPrompt, TokPrompt
from vllm.sampling_params import SamplingParams
from vllm.tasks import SupportedTask
from vllm.utils import length_from_prompt_token_ids_or_embeds
from vllm.utils.torch_utils import set_default_torch_num_threads
from vllm.v1.engine.input_processor import InputProcessor
@@ -89,7 +91,7 @@ class OmniInputProcessor(InputProcessor):
def process_inputs(
self,
request_id: str,
prompt: PromptType,
prompt: PromptType | DictPrompt | TokPrompt,
params: SamplingParams | PoolingParams,
arrival_time: float | None = None,
lora_request: LoRARequest | None = None,
@@ -97,6 +99,7 @@ class OmniInputProcessor(InputProcessor):
trace_headers: Mapping[str, str] | None = None,
priority: int = 0,
data_parallel_rank: int | None = None,
supported_tasks: tuple[SupportedTask, ...] | None = None,
resumable: bool = False,
) -> OmniEngineCoreRequest:
"""Process input prompt into an engine core request.
@@ -129,7 +132,7 @@ class OmniInputProcessor(InputProcessor):
has incorrect shape
"""
self._validate_lora(lora_request)
self._validate_params(params)
self._validate_params(params, supported_tasks)
parallel_config = self.vllm_config.parallel_config
dp_size = parallel_config.data_parallel_size
+3 -3
View File
@@ -53,8 +53,8 @@ from vllm.entrypoints.openai.models.protocol import BaseModelPath
from vllm.entrypoints.openai.models.serving import OpenAIServingModels
from vllm.entrypoints.openai.orca_metrics import metrics_header
from vllm.entrypoints.openai.responses.serving import OpenAIServingResponses
from vllm.entrypoints.openai.server_utils import load_log_config
from vllm.entrypoints.openai.translations.serving import (
from vllm.entrypoints.openai.server_utils import get_uvicorn_log_config
from vllm.entrypoints.openai.speech_to_text.serving import (
OpenAIServingTranscription,
OpenAIServingTranslation,
)
@@ -191,7 +191,7 @@ async def omni_run_server_worker(listen_address, sock, args, client_config=None,
ReasoningParserManager.import_reasoning_parser(args.reasoning_parser_plugin)
# Load logging config for uvicorn if specified
log_config = load_log_config(getattr(args, "log_config_file", None))
log_config = get_uvicorn_log_config(args)
if log_config is not None:
uvicorn_kwargs["log_config"] = log_config
+46 -63
View File
@@ -3,7 +3,7 @@ import base64
import json
import time
import uuid
from collections.abc import AsyncGenerator, AsyncIterator, Callable, Sequence
from collections.abc import AsyncGenerator, AsyncIterator, Callable
from datetime import datetime, timedelta, timezone
from io import BytesIO
from typing import TYPE_CHECKING, Any, Final, Optional, cast
@@ -66,12 +66,14 @@ from vllm.entrypoints.utils import should_include_usage
from vllm.inputs.data import PromptType, TokensPrompt
from vllm.logger import init_logger
from vllm.outputs import RequestOutput
from vllm.reasoning import ReasoningParser
from vllm.renderers.hf import (
resolve_chat_template_content_format,
)
from vllm.renderers.hf import (
safe_apply_chat_template as apply_hf_chat_template,
)
from vllm.renderers.inputs import TokPrompt
from vllm.renderers.mistral import (
safe_apply_chat_template as apply_mistral_chat_template,
)
@@ -182,6 +184,17 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
if tokenizer is None:
tokenizer = await self.engine_client.get_tokenizer()
reasoning_parser: ReasoningParser | None = None
if self.reasoning_parser_cls:
chat_template_kwargs = self._prepare_extra_chat_template_kwargs(
request.chat_template_kwargs,
self.default_chat_template_kwargs,
)
reasoning_parser = self.reasoning_parser_cls(
tokenizer,
chat_template_kwargs=chat_template_kwargs, # type: ignore[call-arg]
)
tool_parser = self.tool_parser
if isinstance(tokenizer, MistralTokenizer):
@@ -236,11 +249,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
chat_template_kwargs,
self.default_chat_template_kwargs,
)
(
conversation,
request_prompts,
engine_prompts,
) = await self._preprocess_chat(
conversation, engine_prompts = await self._preprocess_chat(
request,
request.messages,
default_template=request.chat_template or self.chat_template,
@@ -258,7 +267,6 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
else:
should_include_tools = tool_dicts is not None
conversation, engine_prompts = self._make_request_with_harmony(request, should_include_tools)
request_prompts = [engine_prompt.get("prompt_token_ids", []) for engine_prompt in engine_prompts]
except (ValueError, TypeError, RuntimeError, jinja2.TemplateError) as e:
logger.exception("Error in preprocessing prompt inputs")
@@ -287,7 +295,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
self._log_inputs(
request_id,
request_prompts[i],
engine_prompt,
params_list=sampling_params_list,
lora_request=lora_request,
)
@@ -316,6 +324,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
conversation,
tokenizer,
request_metadata,
reasoning_parser,
)
try:
@@ -327,6 +336,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
conversation,
tokenizer,
request_metadata,
reasoning_parser,
)
except ValueError as e:
return self.create_error_response(e)
@@ -346,11 +356,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
continue_final_message: bool = False,
documents: list[dict[str, str]] | None = None,
add_special_tokens: bool = False,
) -> tuple[
list[ConversationMessage],
Sequence[PromptType],
list[TokensPrompt],
]:
) -> tuple[list[ConversationMessage], list[TokPrompt]]:
model_config = self.model_config
# OMNI: Use self.renderer if renderer not provided (vLLM new API)
if renderer is None:
@@ -468,7 +474,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
if hasattr(request, "cache_salt") and request.cache_salt is not None:
engine_prompt["cache_salt"] = request.cache_salt
return conversation, [request_prompt], [engine_prompt]
return conversation, [engine_prompt]
def _to_sampling_params_list(self, sampling_params_list: list[dict]) -> list[SamplingParams]:
final_sampling_params_list = []
@@ -564,20 +570,16 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
def _log_inputs(
self,
request_id: str,
inputs: PromptType,
inputs: PromptType | TokPrompt,
params_list: list[SamplingParams] | None,
lora_request: LoRARequest | None,
) -> None:
if self.request_logger is None:
return
prompt, prompt_token_ids, prompt_embeds = None, None, None
if isinstance(inputs, str):
prompt = inputs
elif isinstance(inputs, list):
prompt_token_ids = inputs
else:
prompt = getattr(inputs, "prompt", None)
prompt_token_ids = getattr(inputs, "prompt_token_ids", None)
components = self._extract_prompt_components(inputs)
prompt = components.text
prompt_token_ids = components.token_ids
prompt_embeds = components.embeds
logger.info(
"Received request %s: prompt: %r, params_list: %s, prompt_token_ids: %s, prompt_embeds shape: %s, lora_request: %s.", # noqa: E501
@@ -598,6 +600,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
conversation: list[ConversationMessage],
tokenizer: AnyTokenizer,
request_metadata: RequestResponseMetadata,
reasoning_parser: ReasoningParser | None = None,
):
created_time = int(time.time())
chunk_object_type: Final = "chat.completion.chunk"
@@ -639,7 +642,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
# Only one of these will be used, thus previous_texts and
# all_previous_token_ids will not be used twice in the same iteration.
if tool_choice_auto or self.reasoning_parser:
if tool_choice_auto or reasoning_parser:
# These are only required in "auto" tool choice case
all_previous_token_ids = [[]] * num_choices
# For reasoning parser and tool call all enabled
@@ -647,23 +650,6 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
reasoning_end_arr = [False] * num_choices
else:
all_previous_token_ids = None
try:
if self.reasoning_parser:
chat_template_kwargs = self._prepare_extra_chat_template_kwargs(
request.chat_template_kwargs,
self.default_chat_template_kwargs,
)
reasoning_parser = self.reasoning_parser(
tokenizer,
chat_template_kwargs=chat_template_kwargs, # type: ignore
)
except RuntimeError as e:
logger.exception("Error in reasoning parser creation.")
data = self.create_streaming_error_response(e)
yield f"data: {data}\n\n"
yield "data: [DONE]\n\n"
return
# Prepare the tool parser if it's needed
try:
if tool_choice_auto and self.tool_parser:
@@ -815,7 +801,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
delta_message: DeltaMessage | None
# just update previous_texts and previous_token_ids
if tool_choice_auto or self.reasoning_parser:
if tool_choice_auto or reasoning_parser:
assert previous_texts is not None
assert all_previous_token_ids is not None
previous_text = previous_texts[i]
@@ -882,7 +868,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
# handle streaming deltas for tools with named tool_choice
elif tool_choice_function_name:
if (
self.reasoning_parser
reasoning_parser
and not reasoning_end_arr[i]
and not reasoning_parser.is_reasoning_end(previous_token_ids)
):
@@ -912,7 +898,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
current_text = ""
else:
# Just to add remaining `content`
if self.reasoning_parser:
if reasoning_parser:
delta_text = previous_text + delta_text
current_text = ""
@@ -948,14 +934,14 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
output_token_ids = as_list(output.token_ids)
if (
self.reasoning_parser is not None
reasoning_parser is not None
and not reasoning_end_arr[i]
and res.prompt_token_ids
and reasoning_parser.is_reasoning_end(res.prompt_token_ids)
):
reasoning_end_arr[i] = True
if self.reasoning_parser and not reasoning_end_arr[i]:
if reasoning_parser and not reasoning_end_arr[i]:
delta_message = reasoning_parser.extract_reasoning_streaming(
previous_text,
current_text,
@@ -994,7 +980,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
# handle streaming deltas for tools with "auto" tool choice
# and reasoning parser
elif tool_choice_auto and self.reasoning_parser:
elif tool_choice_auto and reasoning_parser:
assert tool_parser is not None
assert reasoning_parser is not None
assert added_content_delta_arr is not None
@@ -1075,7 +1061,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
tools_streamed[i] = True
# when only reasoning
elif self.reasoning_parser:
elif reasoning_parser:
delta_message = reasoning_parser.extract_reasoning_streaming(
previous_text,
current_text,
@@ -1089,7 +1075,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
delta_message = DeltaMessage(content=delta_text)
# update the previous values for the next iteration
if (tool_choice_auto or self.reasoning_parser) and not self.use_harmony:
if (tool_choice_auto or reasoning_parser) and not self.use_harmony:
assert previous_texts is not None
assert all_previous_token_ids is not None
previous_texts[i] = current_text
@@ -1327,6 +1313,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
conversation: list[ConversationMessage],
tokenizer: TokenizerLike,
request_metadata: RequestResponseMetadata,
reasoning_parser: ReasoningParser | None = None,
) -> ErrorResponse | ChatCompletionResponse:
created_time = int(time.time())
final_res: RequestOutput | None = None
@@ -1372,7 +1359,14 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
prompt_logprobs,
prompt_token_ids,
kv_transfer_params,
) = self._create_text_choice(request, omni_outputs, tokenizer, conversation, role)
) = self._create_text_choice(
request,
omni_outputs,
tokenizer,
conversation,
role,
reasoning_parser,
)
elif omni_outputs.final_output_type == "audio":
choices_data = self._create_audio_choice(omni_outputs, role, request, stream=False)
elif omni_outputs.final_output_type == "image":
@@ -1432,6 +1426,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
tokenizer: TokenizerLike,
conversation: list[ConversationMessage],
role: str,
reasoning_parser: ReasoningParser | None = None,
):
final_res = omni_outputs.request_output
if self.tool_call_id_type == "kimi_k2":
@@ -1499,19 +1494,7 @@ class OmniOpenAIServingChat(OpenAIServingChat, AudioMixin):
choices.append(choice_data)
continue
if self.reasoning_parser:
try:
chat_template_kwargs = self._prepare_extra_chat_template_kwargs(
request.chat_template_kwargs,
self.default_chat_template_kwargs,
)
reasoning_parser = self.reasoning_parser(
tokenizer,
chat_template_kwargs=chat_template_kwargs, # type: ignore
)
except RuntimeError as e:
logger.exception("Error in reasoning parser creation.")
return self.create_error_response(e)
if reasoning_parser:
# If the reasoning parser is enabled,
# tool calls are extracted exclusively from the content.
reasoning_content, content = reasoning_parser.extract_reasoning(output.text, request=request)
+2 -2
View File
@@ -106,8 +106,8 @@ class OmniEmbedsPrompt(EmbedsPrompt):
# Must ensure that all additional prompt types are inherited from vLLM prompt types
# Because TypedDict doesn't support isinstance and are dict. Cannot distinguish them in runtime.
# Inheritance ensure that there are only additional fields but not removing fields--safe to route to LLM.generate()
OmniSingletonPrompt: TypeAlias = str | OmniTextPrompt | OmniTokensPrompt | OmniEmbedsPrompt
"""Omni singleton prompt type extending vLLM's SingletonPrompt with additional fields."""
OmniSingletonPrompt: TypeAlias = str | list[int] | OmniTextPrompt | OmniTokensPrompt | OmniEmbedsPrompt
"""Omni singleton prompt type extending vLLM's SingletonPrompt."""
OmniPromptType: TypeAlias = PromptType | OmniTextPrompt | OmniTokensPrompt | OmniEmbedsPrompt
+42 -19
View File
@@ -1,11 +1,29 @@
from vllm.inputs.parse import (
ParsedEmbedsPrompt,
ParsedSingletonPrompt,
ParsedStrPrompt,
ParsedTextPrompt,
ParsedTokensPrompt,
SingletonPrompt,
)
from typing import Literal, TypedDict
from vllm.inputs import EmbedsPrompt, SingletonPrompt, TextPrompt, TokensPrompt
class ParsedStrPrompt(TypedDict):
type: Literal["str"]
content: str
class ParsedTextPrompt(TypedDict):
type: Literal["text"]
content: TextPrompt
class ParsedTokensPrompt(TypedDict):
type: Literal["tokens"]
content: TokensPrompt
class ParsedEmbedsPrompt(TypedDict):
type: Literal["embeds"]
content: EmbedsPrompt
ParsedSingletonPrompt = ParsedStrPrompt | ParsedTextPrompt | ParsedTokensPrompt | ParsedEmbedsPrompt
def parse_singleton_prompt_omni(
@@ -29,16 +47,21 @@ def parse_singleton_prompt_omni(
TypeError: If the prompt type is not supported
"""
if isinstance(prompt, str):
return ParsedStrPrompt(type="str", content=prompt)
elif isinstance(prompt, dict):
# Type ignores are because mypy does not correctly infer the TypedDicts
# Pyright does succeed.
return {"type": "str", "content": prompt}
if isinstance(prompt, list):
if not all(isinstance(token, int) for token in prompt):
raise TypeError("Token prompt should be a list of integers")
return {
"type": "tokens",
"content": TokensPrompt(prompt_token_ids=prompt),
}
if isinstance(prompt, dict):
# Priority tokens: When both tokens and embeds exist, keep both and
# follow the tokens path
# follow the tokens path.
if "prompt_token_ids" in prompt:
return ParsedTokensPrompt(type="tokens", content=prompt) # type: ignore[typeddict-item]
elif "prompt_embeds" in prompt:
return ParsedEmbedsPrompt(type="embeds", content=prompt) # type: ignore[typeddict-item]
elif "prompt" in prompt:
return ParsedTextPrompt(type="text", content=prompt)
raise TypeError("inputs must be a string, TextPrompt, TokensPrompt, or EmbedsPrompt")
return {"type": "tokens", "content": prompt}
if "prompt_embeds" in prompt:
return {"type": "embeds", "content": prompt}
if "prompt" in prompt:
return {"type": "text", "content": prompt}
raise TypeError("inputs must be a string, list of token IDs, TextPrompt, TokensPrompt, or EmbedsPrompt")
+4 -3
View File
@@ -128,9 +128,10 @@ class OmniRequestOutput:
For diffusion outputs, this returns the local _multimodal_output field.
"""
if self.request_output is not None:
# Check completion outputs first (where multimodal_output is attached)
if self.request_output.outputs:
for output in self.request_output.outputs:
# Check completion outputs first (where multimodal_output is attached).
outputs = getattr(self.request_output, "outputs", None)
if isinstance(outputs, list) and outputs:
for output in outputs:
mm = getattr(output, "multimodal_output", None)
if mm:
return mm
+1
View File
@@ -91,4 +91,5 @@ class OmniRequest(Request):
block_hasher=block_hasher,
additional_information=request.additional_information,
resumable=request.resumable,
reasoning_ended=request.reasoning_ended,
)
+2
View File
@@ -4,6 +4,7 @@ import os
import torch
from vllm.logger import init_logger
from vllm.platforms import current_platform
from vllm.tracing import instrument
from vllm.utils.mem_utils import MemorySnapshot, format_gib
from vllm.utils.torch_utils import set_random_seed
from vllm.v1.utils import report_usage_stats
@@ -25,6 +26,7 @@ class GPUARWorker(OmniWorkerMixin, OmniGPUWorkerBase):
model runners for text generation stages (e.g., thinker stages).
"""
@instrument(span_name="Init device")
def init_device(self):
if self.device_config.device_type == "cuda":
# This env var set by Ray causes exceptions with graph building.
+36 -45
View File
@@ -735,55 +735,46 @@ class GPUGenerationModelRunner(OmniGPUModelRunner):
assert mm_budget is not None
if (encoder_budget := mm_budget.get_encoder_budget()) > 0:
# NOTE: Currently model is profiled with a single non-text
# modality with the max possible input tokens even when
# it supports multiple.
dummy_modality = mm_budget.get_modality_with_max_tokens()
max_mm_items_per_batch = mm_budget.max_items_per_batch_by_modality[dummy_modality]
if not mm_budget.mm_max_toks_per_item:
# All modality limits are 0 — embedding-only mode.
# Budget is non-zero for embedding storage, but
# there's no encoder to profile.
logger.info(
"Skipping encoder profiling for embedding-only "
"mode (all modality limits=0 with "
"enable_mm_embeds=True).",
)
else:
# NOTE: Currently model is profiled with a single non-text
# modality with the max possible input tokens even when
# it supports multiple.
dummy_modality = mm_budget.get_modality_with_max_tokens()
max_mm_items_per_batch = mm_budget.mm_max_items_per_batch[dummy_modality]
logger.info(
"Encoder cache will be initialized with a budget of "
"%s tokens, and profiled with %s %s items of the "
"maximum feature size.",
encoder_budget,
max_mm_items_per_batch,
dummy_modality,
)
logger.info(
"Encoder cache will be initialized with a budget of "
"%s tokens, and profiled with %s %s items of the "
"maximum feature size.",
encoder_budget,
max_mm_items_per_batch,
dummy_modality,
)
# Create dummy batch of multimodal inputs.
batched_dummy_mm_inputs = self._get_mm_dummy_batch(
dummy_modality,
max_mm_items_per_batch,
)
# Create dummy batch of multimodal inputs.
batched_dummy_mm_inputs = self._get_mm_dummy_batch(
dummy_modality,
max_mm_items_per_batch,
)
# Run multimodal encoder.
dummy_encoder_outputs = self.model.embed_multimodal(**batched_dummy_mm_inputs)
# Run multimodal encoder.
dummy_encoder_outputs = self.model.embed_multimodal(**batched_dummy_mm_inputs)
sanity_check_mm_encoder_outputs(
dummy_encoder_outputs,
expected_num_items=max_mm_items_per_batch,
)
# NOTE: This happens when encoder cache needs to store
# the embeddings that encoder outputs are scattered onto.
# In this case we create dummy embeddings of size
# (max_tokens_for_modality, hidden_size) and scatter
# encoder output into it.
encoder_output_shape = dummy_encoder_outputs[0].shape
max_mm_tokens_per_item = mm_budget.max_tokens_by_modality[dummy_modality]
if encoder_output_shape[0] < max_mm_tokens_per_item:
encoder_hidden_size = encoder_output_shape[-1]
expanded_outputs = []
for output in dummy_encoder_outputs:
expanded = output.new_zeros((max_mm_tokens_per_item, encoder_hidden_size))
num_tokens = output.shape[0]
expanded[:num_tokens].copy_(output)
expanded_outputs.append(expanded)
dummy_encoder_outputs = expanded_outputs
# Cache the dummy encoder outputs.
self.encoder_cache["tmp"] = dict(enumerate(dummy_encoder_outputs))
sanity_check_mm_encoder_outputs(
dummy_encoder_outputs,
expected_num_items=max_mm_items_per_batch,
)
for i, output in enumerate(dummy_encoder_outputs):
self.encoder_cache[f"tmp_{i}"] = output
# Add `is_profile` here to pre-allocate communication buffers
hidden_states, _ = self._dummy_run(self.max_num_tokens, is_profile=True)
@@ -4,6 +4,7 @@ import os
import torch
from vllm.logger import init_logger
from vllm.platforms import current_platform
from vllm.tracing import instrument
from vllm.utils.mem_utils import MemorySnapshot, format_gib
from vllm.utils.torch_utils import set_random_seed
from vllm.v1.utils import report_usage_stats
@@ -25,6 +26,7 @@ class GPUGenerationWorker(OmniWorkerMixin, OmniGPUWorkerBase):
worker_cls: "vllm_omni.worker.gpu_generation_model_runner.GPUGenerationModelRunner"
"""
@instrument(span_name="Init device")
def init_device(self):
if self.device_config.device_type == "cuda":
# This env var set by Ray causes exceptions with graph building.
+2
View File
@@ -11,6 +11,7 @@ from vllm.model_executor.layers.rotary_embedding import MRotaryEmbedding
from vllm.model_executor.models.interfaces import supports_mrope
from vllm.model_executor.models.interfaces_base import VllmModelForPooling
from vllm.sampling_params import SamplingType
from vllm.tracing import instrument
from vllm.utils.import_utils import LazyLoader
from vllm.utils.math_utils import cdiv
from vllm.v1.spec_decode.eagle import EagleProposer
@@ -40,6 +41,7 @@ class OmniGPUModelRunner(GPUModelRunner):
self._omni_num_scheduled_tokens_np: np.ndarray | None = None
self._omni_last_model_output: object | None = None
@instrument(span_name="Loading (GPU)")
def load_model(self, *args, **kwargs) -> None:
super().load_model(*args, **kwargs)
# TODO move this model specific logic to a separate class