Files
vllm-omni/vllm_omni/outputs/output_metadata.py
T

239 lines
9.6 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from __future__ import annotations
from collections.abc import Mapping, Sequence
from typing import Any, Literal, TypeAlias, TypedDict
import numpy as np
import PIL.Image
import torch
# Final user-facing output modality returned by pipeline stages.
FinalOutputModalityType: TypeAlias = Literal["text", "image", "audio", "video"]
# Scalar values that may appear in either payload or metadata.
# Examples: text_output="done", fps=16, action_only_output=True.
DiffusionScalar: TypeAlias = str | int | float | bool | None
# Shape-like metadata. Examples: [1, 16, 3, 480, 832], (16, 480, 832).
DiffusionShape: TypeAlias = list[int] | tuple[int, ...]
# Named tensor controls used by transfer-style metadata.
# Example: {"pose": pose_tensor, "depth": depth_tensor}.
DiffusionTensorMap: TypeAlias = Mapping[str, torch.Tensor]
# Values allowed under metadata groups such as video/audio/actions/transfer.
# Examples: {"fps": 16}, {"shape": [1, 16, 3, 480, 832]},
# {"controls": {"pose": pose_tensor}}, {"hints": ["pose"]}.
DiffusionMetadataValue: TypeAlias = (
DiffusionScalar
| DiffusionShape
| DiffusionTensorMap
| torch.Tensor
| list["DiffusionMetadataValue"]
| tuple["DiffusionMetadataValue", ...]
| Mapping[str, "DiffusionMetadataValue"]
)
# Mutable metadata envelope side. Example:
# {"video": {"fps": 16}, "actions": {"raw_action_dim": 14}}.
DiffusionMetadata: TypeAlias = dict[str, DiffusionMetadataValue]
# Read-only metadata view for validators and serving readers.
DiffusionMetadataMapping: TypeAlias = Mapping[str, DiffusionMetadataValue]
class DiffusionTrajectoryPayload(TypedDict, total=False):
# Optional denoising trajectory payload for RL/debug consumers.
latents: torch.Tensor | dict[str, Any] | None
timesteps: torch.Tensor | dict[str, Any] | None
log_probs: torch.Tensor | dict[str, Any] | None
decoded: list[PIL.Image.Image] | None
# Concrete payload values produced by post_process.
# Examples: PIL image, video tensor, audio bytes/tensor, action tensor.
DiffusionPayloadValue: TypeAlias = (
DiffusionScalar
| bytes
| torch.Tensor
| np.ndarray
| PIL.Image.Image
| DiffusionTrajectoryPayload
| list["DiffusionPayloadValue"]
| tuple["DiffusionPayloadValue", ...]
| Mapping[str, "DiffusionPayloadValue"]
)
# Mutable payload side. Example: {"video": video_tensor, "action": action_tensor}.
DiffusionPayload: TypeAlias = dict[str, DiffusionPayloadValue]
# Read-only payload view for code that consumes but does not mutate payload.
DiffusionPayloadMapping: TypeAlias = Mapping[str, DiffusionPayloadValue]
# Serving-facing multimodal output. Non-metadata keys hold payload values.
# Example: {"video": video_tensor, "metadata": {"video": {"fps": 16}}}.
DiffusionMultimodalOutput: TypeAlias = dict[str, DiffusionPayloadValue | DiffusionMetadata]
class DiffusionOutputEnvelope(TypedDict, total=False):
# Canonical post_process return shape:
# {"payload": {"video": video_tensor}, "metadata": {"video": {"fps": 16}}}
payload: DiffusionPayload
metadata: DiffusionMetadata
# Raw post_process return before formatter normalization. This keeps backward
# compatibility with direct payload returns while accepting the new envelope.
# Examples: image, {"video": video_tensor}, or
# {"payload": {"video": video_tensor}, "metadata": {"video": {"fps": 16}}}.
DiffusionPostprocessRawOutput: TypeAlias = DiffusionPayloadValue | DiffusionPayload | DiffusionOutputEnvelope
def strip_internal_metadata(metadata: DiffusionMetadataMapping) -> DiffusionMetadata:
"""Return public metadata, dropping model-private postprocess state."""
return {key: value for key, value in metadata.items() if key != "internal"}
def validate_public_diffusion_metadata(metadata: DiffusionMetadataMapping) -> None:
"""Validate metadata that is safe to expose outside model postprocess."""
if "internal" in metadata:
raise ValueError("Diffusion metadata field 'internal' must not escape public formatting.")
validate_diffusion_metadata(metadata)
def validate_diffusion_metadata(metadata: DiffusionMetadataMapping) -> None:
"""Validate canonical diffusion output metadata fields.
Unknown model-specific groups and keys are intentionally tolerated. This
validator only guards fields that are part of the shared output protocol or
consumed by serving code.
"""
_validate_video_metadata(_optional_mapping(metadata, "video"))
_validate_audio_metadata(_optional_mapping(metadata, "audio"))
_validate_actions_metadata(_optional_mapping(metadata, "actions"))
_validate_transfer_metadata(_optional_mapping(metadata, "transfer"))
_validate_text_metadata(_optional_mapping(metadata, "text"))
_validate_common_metadata(_optional_mapping(metadata, "common"))
def _validate_video_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
_check_optional_positive_number(metadata, "fps", group="video")
_check_optional_int(metadata, "video_fps_multiplier", group="video", min_value=1)
_check_optional_shape(metadata, "shape", group="video")
def _validate_audio_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
_check_optional_int(metadata, "sample_rate", group="audio", min_value=1)
_check_optional_non_empty_str(metadata, "audiox_task", group="audio")
def _validate_actions_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
_check_optional_int(metadata, "raw_action_dim", group="actions", min_value=1)
_check_optional_non_empty_str(metadata, "action_mode", group="actions")
_check_optional_int(metadata, "domain_id", group="actions")
def _validate_transfer_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
controls = metadata.get("controls")
if controls is not None and not _is_tensor_mapping(controls):
raise TypeError("Diffusion metadata field 'transfer.controls' must be a mapping of str to torch.Tensor.")
hints = metadata.get("hints")
if hints is not None and not _is_str_list(hints):
raise TypeError("Diffusion metadata field 'transfer.hints' must be a list of strings.")
def _validate_text_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
_check_optional_str(metadata, "text_output", group="text")
_check_optional_str(metadata, "think_text", group="text")
def _validate_common_metadata(metadata: DiffusionMetadataMapping | None) -> None:
if metadata is None:
return
value = metadata.get("action_only_output")
if value is not None and not isinstance(value, bool):
raise TypeError("Diffusion metadata field 'common.action_only_output' must be bool.")
def _optional_mapping(metadata: DiffusionMetadataMapping, group: str) -> DiffusionMetadataMapping | None:
value = metadata.get(group)
if value is None:
return None
if not isinstance(value, Mapping):
raise TypeError(f"Diffusion metadata group '{group}' must be a mapping.")
return value
def _check_optional_positive_number(metadata: DiffusionMetadataMapping, key: str, *, group: str) -> None:
value = metadata.get(key)
if value is None:
return
if isinstance(value, bool) or not isinstance(value, (int, float)):
raise TypeError(f"Diffusion metadata field '{group}.{key}' must be a positive number.")
if value <= 0:
raise ValueError(f"Diffusion metadata field '{group}.{key}' must be > 0.")
def _check_optional_int(
metadata: DiffusionMetadataMapping,
key: str,
*,
group: str,
min_value: int | None = None,
) -> None:
value = metadata.get(key)
if value is None:
return
if isinstance(value, bool) or not isinstance(value, int):
raise TypeError(f"Diffusion metadata field '{group}.{key}' must be int.")
if min_value is not None and value < min_value:
raise ValueError(f"Diffusion metadata field '{group}.{key}' must be >= {min_value}.")
def _check_optional_str(metadata: DiffusionMetadataMapping, key: str, *, group: str) -> None:
value = metadata.get(key)
if value is not None and not isinstance(value, str):
raise TypeError(f"Diffusion metadata field '{group}.{key}' must be str.")
def _check_optional_non_empty_str(metadata: DiffusionMetadataMapping, key: str, *, group: str) -> None:
value = metadata.get(key)
if value is None:
return
if not isinstance(value, str):
raise TypeError(f"Diffusion metadata field '{group}.{key}' must be str.")
if not value:
raise ValueError(f"Diffusion metadata field '{group}.{key}' must be non-empty.")
def _check_optional_shape(metadata: DiffusionMetadataMapping, key: str, *, group: str) -> None:
value = metadata.get(key)
if value is None:
return
if not isinstance(value, Sequence) or isinstance(value, (str, bytes, bytearray)):
raise TypeError(f"Diffusion metadata field '{group}.{key}' must be a list or tuple of positive ints.")
if any(isinstance(dim, bool) or not isinstance(dim, int) or dim <= 0 for dim in value):
raise ValueError(f"Diffusion metadata field '{group}.{key}' must contain positive ints.")
def _is_tensor_mapping(value: object) -> bool:
return isinstance(value, Mapping) and all(
isinstance(key, str) and isinstance(tensor, torch.Tensor) for key, tensor in value.items()
)
def _is_str_list(value: object) -> bool:
return isinstance(value, list) and all(isinstance(item, str) for item in value)