Files
vllm-omni/vllm_omni/worker_v2/model_states/intermediate_buffer.py
T
Taichang ZhouandClaude Opus 4.8 9678ae58cf [MR V2] Migrate to Model Runner V2
Squashed migration rebased onto current main. Collapses the 46-commit
dev/migrate-MR-v2 history (10 merge commits + superseded pre-rebase work)
into a single commit, so the diff reflects only the genuine MR-V2 feature
(worker_v2/ runners + model_states, omni schedulers, stage engine pipeline,
pooling_output serialization bridge). Content already merged to main
separately (step_audio2, tts_adapters, npu/_310p, etc.) drops out.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 07:19:40 +00:00

148 lines
5.5 KiB
Python

"""OmniIntermediateBuffer — per-request cross-stage state for Omni pipelines.
Uses ``req_index`` (not ``req_id``) for O(1) access, aligned with v2's
``RequestState`` slot management.
"""
from __future__ import annotations
from typing import Any
import numpy as np
import torch
from vllm.logger import init_logger
from vllm.v1.core.sched.output import NewRequestData
from vllm.v1.worker.gpu.input_batch import InputBatch
logger = init_logger(__name__)
def _resolve_prompt_embeds(pe: Any) -> torch.Tensor | None:
"""Convert a prompt_embeds payload to a contiguous CPU tensor."""
if pe is None:
return None
try:
if isinstance(pe, torch.Tensor):
return pe.detach().cpu().contiguous()
data = getattr(pe, "data", None)
shape = getattr(pe, "shape", None)
if data is not None and shape is not None:
dt = np.dtype(getattr(pe, "dtype", "float32"))
arr = np.frombuffer(data, dtype=dt).reshape(shape)
return torch.from_numpy(arr.copy())
except Exception:
logger.exception("Failed to decode prompt_embeds payload")
return None
def _resolve_additional_information(payload: Any) -> dict[str, Any]:
"""Convert an additional_information payload to a plain dict."""
if payload is None:
return {}
try:
from vllm_omni.data_entry_keys import unflatten_payload
from vllm_omni.engine.serialization import deserialize_additional_information
info = deserialize_additional_information(payload)
if isinstance(info, dict) and any(isinstance(key, str) and "." in key for key in info):
info = unflatten_payload(info)
return info if isinstance(info, dict) else {}
except Exception:
logger.exception("Failed to decode additional_information payload")
return {}
class OmniIntermediateBuffer:
"""Per-request intermediate state for multi-stage Omni pipelines.
Stores ``prompt_embeds``, ``additional_information``, ``mm_features``,
``req_id`` and any runtime updates written back by model postprocess.
"""
def __init__(self, max_num_reqs: int):
self.buffers: list[dict[str, Any]] = [{} for _ in range(max_num_reqs)]
def add_request(self, req_index: int, new_req_data: NewRequestData) -> None:
info: dict[str, Any] = {}
pe = getattr(new_req_data, "prompt_embeds", None)
if pe is not None:
pe_cpu = _resolve_prompt_embeds(pe)
if pe_cpu is not None:
info["prompt_embeds_cpu"] = pe_cpu
ai = getattr(new_req_data, "additional_information", None)
if ai is not None:
info.update(_resolve_additional_information(ai))
if new_req_data.mm_features:
info["mm_features"] = new_req_data.mm_features
info["req_id"] = new_req_data.req_id
self.buffers[req_index] = info
def remove_request(self, req_index: int) -> None:
self.buffers[req_index] = {}
def gather(self, input_batch: InputBatch) -> list[dict[str, Any]]:
"""Return buffer dicts in current batch order (via ``idx_mapping_np``)."""
return [self.buffers[idx] for idx in input_batch.idx_mapping_np]
@staticmethod
def _split_gpu_keys(gpu_resident_keys: set[Any] | None) -> tuple[set[Any], dict[Any, set[Any]]]:
top_level: set[Any] = set()
nested: dict[Any, set[Any]] = {}
for key in gpu_resident_keys or set():
if isinstance(key, tuple) and len(key) == 2:
nested.setdefault(key[0], set()).add(key[1])
else:
top_level.add(key)
return top_level, nested
@staticmethod
def _store_value(dest: dict[Any, Any], key: Any, value: Any, gpu_keys: set[Any]) -> None:
if isinstance(value, torch.Tensor):
if key in gpu_keys:
dest[key] = value.detach().clone()
else:
dest[key] = value.detach().cpu().contiguous()
elif isinstance(value, list):
dest[key] = [
(item.detach().cpu().contiguous() if isinstance(item, torch.Tensor) else item) for item in value
]
else:
dest[key] = value
def update(
self,
req_index: int,
updates: dict[Any, Any],
gpu_resident_keys: set[Any] | None = None,
) -> None:
"""Merge *updates* into the buffer at *req_index*.
Tensors are detached; those whose key is **not** in
*gpu_resident_keys* are moved to CPU.
"""
if not updates:
return
top_gpu_keys, nested_gpu_keys = self._split_gpu_keys(gpu_resident_keys)
existing = self.buffers[req_index]
for k, v in updates.items():
if isinstance(k, tuple) and len(k) == 2:
type_key, qualifier = k
existing_sub = existing.setdefault(type_key, {})
if not isinstance(existing_sub, dict):
existing_sub = {}
existing[type_key] = existing_sub
self._store_value(existing_sub, qualifier, v, nested_gpu_keys.get(type_key, set()))
elif isinstance(v, dict):
existing_sub = existing.setdefault(k, {})
if not isinstance(existing_sub, dict):
existing_sub = {}
existing[k] = existing_sub
for qualifier, value in v.items():
self._store_value(existing_sub, qualifier, value, nested_gpu_keys.get(k, set()))
else:
self._store_value(existing, k, v, top_gpu_keys)