Signed-off-by: samithuang <285365963@qq.com> Signed-off-by: Samit <285365963@qq.com> Signed-off-by: SamitHuang <285365963@qq.com> Co-authored-by: Flora Feng <4florafeng@gmail.com>
169 lines
6.2 KiB
Python
169 lines
6.2 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
|
"""
|
|
OpenAI-compatible protocol definitions for video generation.
|
|
|
|
This module provides Pydantic models for a video generation endpoint that
|
|
mirrors the OpenAI Images API shape, with vllm-omni extensions for diffusion
|
|
video models (e.g., Wan2.2).
|
|
"""
|
|
|
|
from enum import Enum
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class VideoResponseFormat(str, Enum):
|
|
"""Video response format."""
|
|
|
|
B64_JSON = "b64_json"
|
|
URL = "url" # Not implemented in PoC
|
|
|
|
|
|
class VideoParams(BaseModel):
|
|
"""Optional block for video-specific parameters."""
|
|
|
|
width: int | None = Field(default=None, ge=1, description="Video width in pixels")
|
|
height: int | None = Field(default=None, ge=1, description="Video height in pixels")
|
|
num_frames: int | None = Field(default=None, ge=1, description="Number of frames")
|
|
fps: int | None = Field(default=None, ge=1, description="Frames per second for output video")
|
|
|
|
|
|
class VideoGenerationRequest(BaseModel):
|
|
"""
|
|
OpenAI-style video generation request.
|
|
|
|
Follows the OpenAI Images API conventions with extensions for video.
|
|
"""
|
|
|
|
prompt: str = Field(..., description="Text description of the desired video(s)")
|
|
|
|
# OpenAI standard fields
|
|
model: str | None = Field(
|
|
default=None,
|
|
description="Model to use (optional, uses server's configured model if omitted)",
|
|
)
|
|
n: int = Field(default=1, ge=1, le=4, description="Number of videos to generate")
|
|
seconds: int | str | None = Field(
|
|
default=None,
|
|
description="Clip duration in seconds (OpenAI-compatible, e.g., 4, 8, 12)",
|
|
)
|
|
size: str | None = Field(
|
|
default=None,
|
|
description="Video dimensions in WIDTHxHEIGHT format (e.g., '1280x720')",
|
|
)
|
|
response_format: VideoResponseFormat = Field(
|
|
default=VideoResponseFormat.B64_JSON, description="Format of the returned video"
|
|
)
|
|
user: str | None = Field(default=None, description="User identifier for tracking")
|
|
stream: bool | None = Field(default=False, description="Whether to stream outputs (not yet supported)")
|
|
|
|
# Video params block for extensibility
|
|
video_params: VideoParams | None = Field(default=None, description="Optional video-specific parameters")
|
|
|
|
# Image-to-video input (OpenAI-style name)
|
|
input_reference: str | None = Field(
|
|
default=None,
|
|
description="Image input for I2V; provided via multipart form-data as a file upload.",
|
|
)
|
|
|
|
# Video-specific fields (top-level for OpenAI-style compatibility)
|
|
width: int | None = Field(default=None, ge=1, description="Video width in pixels")
|
|
height: int | None = Field(default=None, ge=1, description="Video height in pixels")
|
|
num_frames: int | None = Field(default=None, ge=1, description="Number of frames to generate")
|
|
fps: int | None = Field(default=None, ge=1, description="Frames per second for output video")
|
|
|
|
# vllm-omni extensions for diffusion control
|
|
negative_prompt: str | None = Field(default=None, description="Text describing what to avoid in the video")
|
|
num_inference_steps: int | None = Field(
|
|
default=None,
|
|
ge=1,
|
|
le=200,
|
|
description="Number of diffusion sampling steps (uses model defaults if not specified)",
|
|
)
|
|
guidance_scale: float | None = Field(
|
|
default=None,
|
|
ge=0.0,
|
|
le=20.0,
|
|
description="Classifier-free guidance scale (uses model defaults if not specified)",
|
|
)
|
|
guidance_scale_2: float | None = Field(
|
|
default=None,
|
|
ge=0.0,
|
|
le=20.0,
|
|
description="High-noise CFG scale for video models (Wan2.2)",
|
|
)
|
|
boundary_ratio: float | None = Field(
|
|
default=None,
|
|
ge=0.0,
|
|
le=1.0,
|
|
description="Boundary split ratio for low/high DiT (Wan2.2)",
|
|
)
|
|
flow_shift: float | None = Field(
|
|
default=None,
|
|
description="Scheduler flow_shift for video models (Wan2.2)",
|
|
)
|
|
true_cfg_scale: float | None = Field(
|
|
default=None,
|
|
ge=0.0,
|
|
le=20.0,
|
|
description="True CFG scale (model-specific parameter, may be ignored if not supported)",
|
|
)
|
|
seed: int | None = Field(default=None, description="Random seed for reproducibility")
|
|
|
|
# vllm-omni extension for per-request LoRA.
|
|
lora: dict[str, Any] | None = Field(
|
|
default=None,
|
|
description=(
|
|
"Optional LoRA adapter for this request. Expected shape: "
|
|
"{name/path/scale/int_id}. Field names are flexible "
|
|
"(e.g. name|lora_name|adapter, path|lora_path|local_path, "
|
|
"scale|lora_scale, int_id|lora_int_id)."
|
|
),
|
|
)
|
|
|
|
@field_validator("size")
|
|
@classmethod
|
|
def validate_size(cls, v):
|
|
if v is None:
|
|
return None
|
|
if not isinstance(v, str) or "x" not in v:
|
|
raise ValueError("size must be in format 'WIDTHxHEIGHT' (e.g., '1280x720')")
|
|
return v
|
|
|
|
@field_validator("response_format")
|
|
@classmethod
|
|
def validate_response_format(cls, v):
|
|
if v is not None and v != VideoResponseFormat.B64_JSON:
|
|
raise ValueError(f"Only 'b64_json' response format is supported, got: {v}")
|
|
return v
|
|
|
|
@field_validator("seconds")
|
|
@classmethod
|
|
def validate_seconds(cls, v):
|
|
if v is None:
|
|
return None
|
|
if isinstance(v, str):
|
|
if not v.isdigit():
|
|
raise ValueError("seconds must be an integer or numeric string")
|
|
return int(v)
|
|
if isinstance(v, int):
|
|
return v
|
|
raise ValueError("seconds must be an integer or numeric string")
|
|
|
|
|
|
class VideoData(BaseModel):
|
|
"""Single generated video data."""
|
|
|
|
b64_json: str | None = Field(default=None, description="Base64-encoded MP4 video")
|
|
url: str | None = Field(default=None, description="Video URL (not implemented)")
|
|
revised_prompt: str | None = Field(default=None, description="Revised prompt (OpenAI compatibility, always null)")
|
|
|
|
|
|
class VideoGenerationResponse(BaseModel):
|
|
"""OpenAI-style video generation response."""
|
|
|
|
created: int = Field(..., description="Unix timestamp of when the generation completed")
|
|
data: list[VideoData] = Field(..., description="Array of generated videos")
|