# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project """Generate a LingBot-World v2 video from an image and camera trajectory. The official checkpoint is licensed separately under CC BY-NC-SA and is restricted to non-commercial use. This vLLM-Omni integration remains Apache-2.0 licensed. Example: python examples/offline_inference/diffusion/lingbot_world_v2.py \ --prompt "The camera moves slowly forward through the scene." \ --image /path/to/first_frame.png \ --action-dir /path/to/actions/forward \ --output lingbot_world_v2.mp4 ``--action-dir`` must contain ``poses.npy`` and ``intrinsics.npy``. The example configures its parent as the trusted action root and sends the contained directory through ``extra_args["action_path"]``. """ from __future__ import annotations import argparse import math from collections.abc import Sequence from dataclasses import dataclass from pathlib import Path from typing import Any import numpy as np _MODEL = "robbyant/lingbot-world-v2-14b-causal-fast-diffusers" _MAX_SEQUENCE_LENGTH = 512 _MAX_PIXEL_AREA = 480 * 832 _MAX_SOURCE_CAMERA_FRAMES = 4096 _TEMPORAL_COMPRESSION = 4 _LATENT_FRAMES_PER_BLOCK = 3 _SPATIAL_ALIGNMENT = 16 @dataclass(frozen=True) class LingBotPaths: image: Path action_dir: Path action_root: Path action_relative: Path camera_frames: int output: Path def _validate_camera_arrays(action_dir: Path) -> int: """Validate the bounded camera contract before starting the engine.""" try: poses = np.load(action_dir / "poses.npy", mmap_mode="r", allow_pickle=False) intrinsics = np.load(action_dir / "intrinsics.npy", mmap_mode="r", allow_pickle=False) except (OSError, TypeError, ValueError): raise ValueError("Camera files must be valid numeric poses.npy and intrinsics.npy arrays.") from None if poses.ndim != 3 or poses.shape[1:] != (4, 4): raise ValueError("poses.npy must have shape [frames, 4, 4].") if intrinsics.ndim != 2 or intrinsics.shape[1:] != (4,): raise ValueError("intrinsics.npy must have shape [frames, 4].") if poses.shape[0] != intrinsics.shape[0]: raise ValueError("poses.npy and intrinsics.npy must contain the same frame count.") if not 1 <= poses.shape[0] <= _MAX_SOURCE_CAMERA_FRAMES: raise ValueError("Camera arrays must contain between 1 and 4096 source frames.") if poses.dtype.kind not in "fiu" or intrinsics.dtype.kind not in "fiu": raise ValueError("Camera arrays must contain real numeric values.") if not np.isfinite(poses).all() or not np.isfinite(intrinsics).all(): raise ValueError("Camera arrays must contain only finite values.") return int(poses.shape[0]) def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: parser = argparse.ArgumentParser( description="Generate a LingBot-World v2 video from a first-frame image and camera trajectory." ) parser.add_argument("--model", default=_MODEL, help="Official Hugging Face model ID or local checkpoint path.") parser.add_argument("--prompt", required=True, help="Text description of the scene and desired motion.") parser.add_argument("--image", required=True, help="Path to the first-frame image.") parser.add_argument( "--action-dir", required=True, help="Camera action directory containing poses.npy and intrinsics.npy.", ) parser.add_argument("--height", type=int, default=480, help="Output height; must be divisible by 16.") parser.add_argument("--width", type=int, default=832, help="Output width; must be divisible by 16.") parser.add_argument( "--num-frames", type=int, default=9, help="Raw output frame count. Nine frames are one three-latent-frame causal block.", ) parser.add_argument("--seed", type=int, default=42, help="Random seed.") parser.add_argument( "--tensor-parallel-size", type=int, default=1, help="Number of GPUs used for tensor parallelism inside the DiT.", ) parser.add_argument("--ulysses-degree", type=int, default=1, help="Pure Ulysses sequence parallel degree.") parser.add_argument("--flow-shift", type=float, default=5.0, help="Positive FlowUniPC scheduler shift.") parser.add_argument("--fps", type=int, default=16, help="Frames per second in the exported MP4.") parser.add_argument("--output", default="lingbot_world_v2.mp4", help="Output MP4 path.") parser.add_argument( "--enforce-eager", action="store_true", help="Disable compilation and force eager execution.", ) return parser.parse_args(argv) def resolve_cli_paths(args: argparse.Namespace) -> LingBotPaths: """Canonicalize local inputs and construct a safe trusted-root mapping.""" image = Path(args.image).expanduser().resolve() if not image.is_file(): raise ValueError("--image must point to an existing file.") action_dir = Path(args.action_dir).expanduser().resolve() if not action_dir.is_dir(): raise ValueError("--action-dir must point to an existing directory.") if not all((action_dir / name).is_file() for name in ("poses.npy", "intrinsics.npy")): raise ValueError("--action-dir must contain poses.npy and intrinsics.npy.") camera_frames = _validate_camera_arrays(action_dir) action_root = action_dir.parent.resolve() try: action_relative = action_dir.relative_to(action_root) except ValueError: raise ValueError("--action-dir must be contained by its canonical trusted action root.") from None if not action_relative.parts: raise ValueError("--action-dir must name a directory contained by a trusted action root.") output = Path(args.output).expanduser().resolve() return LingBotPaths( image=image, action_dir=action_dir, action_root=action_root, action_relative=action_relative, camera_frames=camera_frames, output=output, ) def _positive_finite(value: float, flag: str) -> float: result = float(value) if not math.isfinite(result) or result <= 0: raise ValueError(f"{flag} must be a positive finite number.") return result def build_omni_kwargs( args: argparse.Namespace, paths: LingBotPaths, ) -> dict[str, Any]: """Build Omni configuration without overriding checkpoint class discovery.""" if args.tensor_parallel_size <= 0: raise ValueError("--tensor-parallel-size must be a positive integer.") if args.ulysses_degree <= 0: raise ValueError("--ulysses-degree must be a positive integer.") flow_shift = _positive_finite(args.flow_shift, "--flow-shift") model_path = Path(args.model).expanduser() model = str(model_path.resolve()) if model_path.exists() else args.model return { "model": model, "flow_shift": flow_shift, "tensor_parallel_size": args.tensor_parallel_size, "ulysses_degree": args.ulysses_degree, "enforce_eager": args.enforce_eager, "model_config": {"lingbot_action_root": str(paths.action_root)}, } def build_request( args: argparse.Namespace, paths: LingBotPaths, ) -> tuple[dict[str, Any], dict[str, Any]]: """Build the prompt and fixed v1 causal-DMD sampling contract.""" prompt_text = args.prompt.strip() if not prompt_text: raise ValueError("--prompt must contain non-whitespace text.") if args.height <= 0 or args.width <= 0: raise ValueError("--height and --width must be positive integers.") if args.height % _SPATIAL_ALIGNMENT or args.width % _SPATIAL_ALIGNMENT: raise ValueError("--height and --width must each be divisible by 16.") if args.height * args.width > _MAX_PIXEL_AREA: raise ValueError("--height * --width must not exceed 480 * 832 pixels.") if args.num_frames <= 0: raise ValueError("--num-frames must be a positive integer.") if args.num_frames > paths.camera_frames: raise ValueError("Camera arrays must contain at least --num-frames frames.") if (args.num_frames - 1) % _TEMPORAL_COMPRESSION: raise ValueError("--num-frames must satisfy (num_frames - 1) divisible by 4.") latent_frames = (args.num_frames - 1) // _TEMPORAL_COMPRESSION + 1 if latent_frames % _LATENT_FRAMES_PER_BLOCK: raise ValueError("--num-frames must map to whole three-frame latent blocks.") if args.fps <= 0: raise ValueError("--fps must be a positive integer.") flow_shift = _positive_finite(args.flow_shift, "--flow-shift") prompt = { "prompt": prompt_text, "multi_modal_data": {"image": str(paths.image)}, } sampling_kwargs = { "height": args.height, "width": args.width, "num_frames": args.num_frames, "max_sequence_length": _MAX_SEQUENCE_LENGTH, "seed": args.seed, "fps": args.fps, "extra_args": { "action_path": paths.action_relative.as_posix(), "flow_shift": flow_shift, }, } return prompt, sampling_kwargs def extract_video_array(outputs: Any) -> np.ndarray: """Unwrap one standard Omni diffusion video into ``[frames, H, W, C]``.""" if not isinstance(outputs, list) or len(outputs) != 1: raise ValueError("Expected exactly one Omni output for the single LingBot request.") output = outputs[0] request_output = getattr(output, "request_output", None) if request_output is not None and getattr(request_output, "images", None): output = request_output images = getattr(output, "images", None) if not images: multimodal_output = getattr(output, "multimodal_output", None) or {} images = multimodal_output.get("video") if images is None: raise ValueError("No video frames found in Omni output.") if isinstance(images, list) and len(images) == 1: images = images[0] video: np.ndarray = np.asarray(images) if video.ndim == 5: if video.shape[0] != 1: raise ValueError(f"Expected one generated video, got batch shape {video.shape}.") video = video[0] if video.ndim != 4 or video.shape[-1] not in (3, 4): raise ValueError(f"Expected video shape [frames, height, width, channels], got {video.shape}.") return video def main(argv: Sequence[str] | None = None) -> Path: args = parse_args(argv) paths = resolve_cli_paths(args) prompt, sampling_kwargs = build_request(args, paths) paths.output.parent.mkdir(parents=True, exist_ok=True) # Keep vLLM imports below pure CLI/path validation so ``--help`` and the # helper tests work on machines without the native vLLM runtime. from diffusers.utils import export_to_video from vllm_omni.entrypoints.omni import Omni from vllm_omni.inputs.data import OmniDiffusionSamplingParams omni_kwargs = build_omni_kwargs(args, paths) omni = Omni(**omni_kwargs) try: outputs = omni.generate( prompt, OmniDiffusionSamplingParams(**sampling_kwargs), use_tqdm=False, ) finally: omni.close() frames = extract_video_array(outputs) export_to_video(frames, str(paths.output), fps=args.fps) print(f"Saved {frames.shape[0]} frames to {paths.output}") return paths.output if __name__ == "__main__": main()