Files
vllm-omni/recipes/cosmos3/Cosmos3-Nano.md
T

876 lines
43 KiB
Markdown
Raw Normal View History

2026-06-01 22:04:31 +02:00
# Cosmos3-Nano
2026-06-08 21:04:05 +02:00
> Text-to-image, text-to-video, image-to-video, and video-to-video serving
2026-06-01 22:04:31 +02:00
## Summary
- Vendor: NVIDIA
- Model: `nvidia/Cosmos3-Nano`
2026-06-21 03:59:18 +02:00
- Task: Text-to-image (T2I), text-to-video (T2V), image-to-video (I2V), and video-to-video (V2V) generation, with optional transfer controls, synchronized audio (video + sound), action policy
2026-06-01 22:04:31 +02:00
- Mode: Online serving with the OpenAI-compatible image/video APIs, plus offline generation via the `Omni` API
- Maintainer: Community
## When to use this recipe
Use this recipe to deploy `nvidia/Cosmos3-Nano` for image and video generation.
2026-06-08 21:04:05 +02:00
A single pipeline class (`Cosmos3OmniDiffusersPipeline`) serves these modes; the
mode is selected per request:
2026-06-01 22:04:31 +02:00
- **T2I** — `POST /v1/images/generations` (or a prompt carrying `modalities=["image"]`).
2026-06-08 21:04:05 +02:00
- **T2V** — `POST /v1/videos/sync` with `num_frames > 1` and no reference image/video.
2026-06-01 22:04:31 +02:00
- **I2V** — `POST /v1/videos/sync` with a reference image (`input_reference` file
upload, or `image_reference` JSON).
2026-06-08 21:04:05 +02:00
- **V2V** — `POST /v1/videos/sync` with a reference video (`input_reference`
video upload, or `video_reference` JSON). Cosmos3 conditions on selected
reference-video latent frames; use `extra_params.condition_frame_indexes_vision`
and `extra_params.condition_video_keep` to choose which prefix/tail frames guide
generation.
2026-06-21 03:59:18 +02:00
- **Transfer V2V** — pass one or more transfer hints in `extra_params`
(`edge`, `blur`, `depth`, `seg`, `wsm`) to guide generation with control
frames. Transfer mode is video-only and cannot be combined with sound or action.
2026-06-03 02:51:44 +02:00
- **T2VS / I2VS** — add `generate_sound=true` (and optional `sound_duration`) to a
T2V/I2V `/v1/videos/sync` request to also generate synchronized audio, muxed into
the mp4 as AAC 48 kHz stereo. See the official model card's "Video + Audio" examples.
2026-06-03 21:30:39 +02:00
- **Action** — pass `extra_params={"action_mode": ...}` to drive Physical-AI tasks:
- `forward_dynamics` — given a first frame or video **and** an action trajectory,
2026-06-08 21:04:05 +02:00
roll out the resulting video. Synchronous: `POST /v1/videos/sync`.
- `policy` — given a first frame or video and a language instruction,
2026-06-08 21:04:05 +02:00
**predict** the action trajectory (and a rollout video). Use the async
`POST /v1/videos` endpoint and read the predicted action from the top-level
`action` field.
- `inverse_dynamics` — given a video, **recover** the action trajectory. Use
2026-06-08 21:04:05 +02:00
the async `POST /v1/videos` endpoint and read the recovered action from
the top-level `action` field
2026-06-03 21:30:39 +02:00
(`{data, shape, dtype, raw_action_dim, domain_id}`).
Action requests also take `domain_name` (e.g. `av`, `bridge_orig_lerobot`,
`droid_lerobot`, `agibotworld`, …; or a numeric `domain_id`), `raw_action_dim`,
and `action_chunk_size` (must equal `num_frames` or `num_frames - 1`). For
`forward_dynamics` also pass the `action` array. The dedicated policy checkpoint
**`nvidia/Cosmos3-Nano-Policy-DROID`** is served the same way
(`domain_name=droid_lerobot`).
2026-09-10 16:32:50 +02:00
- **DROID policy server** — serve `nvidia/Cosmos3-Nano-Policy-DROID` and connect
RoboLab or another OpenPI-compatible client to
`/v1/realtime/robot/openpi`. This endpoint returns action chunks directly
instead of an mp4.
2026-06-08 21:04:05 +02:00
Action requests can use `input_reference` or `video_reference` for video input.
`policy` and `forward_dynamics` can also use an image reference; `inverse_dynamics`
requires a video reference.
2026-06-01 22:04:31 +02:00
## References
- Model card (authoritative usage + example assets): <https://huggingface.co/nvidia/Cosmos3-Nano>
- Example inputs/outputs live in the repo's `assets/` (`example_t2v_prompt.json`,
2026-06-03 02:51:44 +02:00
`example_i2v_prompt.json`, `example_i2v_input.jpg`, `negative_prompt.json`;
audio examples: `example_t2vs_prompt.json`, `example_t2vs_output.mp4`,
`example_i2vs_output.mp4`).
2026-06-01 22:04:31 +02:00
- Prompt upsampling (recommended for quality): the model expects JSON-upsampled
structured prompts; see NVIDIA's `cosmos-framework` prompt-upsampling docs.
- Pipeline: [`vllm_omni/diffusion/models/cosmos3/pipeline_cosmos3.py`](../../vllm_omni/diffusion/models/cosmos3/pipeline_cosmos3.py)
- Smoke tests (canonical request formats): [`tests/e2e/accuracy/test_cosmos3_similarity.py`](../../tests/e2e/accuracy/test_cosmos3_similarity.py)
## Hardware Support
## GPU
### 1x H200 141GB / B300 (Online serving)
#### Environment
- OS: Ubuntu 22.04+
- Python: 3.12+
- Driver / runtime: NVIDIA CUDA environment
- vLLM version: match the repository requirements from your current checkout
- vLLM-Omni version or commit: use the commit you are deploying from
#### Command
2026-06-03 02:51:44 +02:00
Requires the `vllm-omni` package (or the `vllm/vllm-omni:cosmos3` container),
which provides the `vllm serve … --omni` entrypoint used below.
2026-06-01 22:04:31 +02:00
Safety guardrails are **on by default** (NVIDIA Open Model License). They load
the **gated** `nvidia/Cosmos-1.0-Guardrail` model, so to keep them on you must:
1. `pip install cosmos-guardrail`
2. Accept the license at <https://huggingface.co/nvidia/Cosmos-1.0-Guardrail>
3. Export a token with access: `export HF_TOKEN=hf_...`
Then launch the recommended server:
```bash
vllm serve nvidia/Cosmos3-Nano \
--omni \
--host 0.0.0.0 --port 8000 \
--init-timeout 1800
```
For Cosmos3, SeaCache is the recommended default choice when opting into
diffusion caching. Caching remains opt-in; add `--cache-backend sea_cache` to
the command above. Its default threshold and maximum cached-step streak are
tuned for Cosmos3, so no `--cache-config` is required.
Override individual defaults with a JSON cache configuration; for example:
```bash
vllm serve nvidia/Cosmos3-Nano \
--omni \
--cache-backend sea_cache \
--cache-config '{"sea_threshold":0.2,"sea_max_consecutive_cached":3}'
```
Lower `sea_threshold` values and smaller `sea_max_consecutive_cached` caps are
more conservative. Higher values allow more cached steps and may improve
speed, but can increase quality loss. Setting `sea_max_consecutive_cached` to
`0` removes the streak cap.
2026-06-01 22:04:31 +02:00
To run **without** guardrails (you are responsible for license compliance),
add `--no-guardrails` (no token/`cosmos-guardrail` needed). For extra GPUs use
`--ulysses-degree N` (context parallel) or `--tensor-parallel-size N`;
`--enable-layerwise-offload` reduces VRAM on smaller GPUs;
`--vae-fast-path channels_last` speeds up the Wan VAE video decode by switching the
decoder convolutions to channels-last kernels (output no longer bit-identical to
diffusers; the default `lossless` fast path is bit-exact, see
[Wan VAE Decoder Fast Path](../../docs/user_guide/diffusion/vae_fast_path.md));
`--quantization fp8` (online, no calibration) cuts peak VRAM for 720p video
generation from ~50 GB to ~36 GB with BF16-level quality (T2V composition can
shift at the same seed). The pipeline
2026-06-01 22:04:31 +02:00
auto-resolves from `model_index.json`; pass
`--model-class-name Cosmos3OmniDiffusersPipeline` to force it explicitly.
For a serialized ModelOpt FP8 or NVFP4 checkpoint, an experimental
mixed-precision schedule can use native W8A8/W4A4 in middle denoising steps
and dense W8A16/W4A16 in the first and last steps:
```bash
vllm serve /path/to/Cosmos3-Nano-modelopt \
--omni \
--additional-config \
'{"cosmos3_mixed_precision":{"first_steps":3,"last_steps":3,"reasoner":"a16"}}'
```
The nested object's presence supplies an explicit runtime override; use an
empty object for these defaults. A compatible checkpoint can instead declare
the versioned `runtime.diffusion_step_policy` in its authoritative
`transformer/config.json`. The checkpoint policy is used only when no runtime
override is present. Use
`--additional-config '{"cosmos3_mixed_precision":{"enabled":false}}'` to
disable the checkpoint schedule without disabling checkpoint quantization.
This path currently requires tensor parallel size 1 and does not support HSDP
or block-scaled FP8. It keeps one live quantized weight representation:
scheduled FP8 requires serialized tensorwise scales and a canonical backend,
while scheduled NVFP4 requires a supported CUTLASS-compatible native or
FlashInfer layout. Its dequantization is a correctness baseline; no speedup is
implied. Quantized reasoner weights use A16 by default; set
`"reasoner":"native"` in the nested object to keep W8A8/W4A4.
The checkpoint's ModelOpt configuration selects FP8 or NVFP4; mixed FP8/NVFP4
checkpoints are not supported by this schedule. BF16 linears are left unchanged.
The schedule currently supports one active request per worker. Model-level and
layer-wise offload are designed to work with this live-weight path but still
require GPU validation. Distributed layer-wise offload and incompatible native
weight layouts fail during loading.
2026-06-01 22:04:31 +02:00
#### Verification
Best quality uses the JSON-upsampled prompts from `assets/` (download with
`hf download nvidia/Cosmos3-Nano assets/ --local-dir Cosmos3-Nano`). Minimal
self-contained examples:
```bash
curl http://localhost:8000/v1/models
# Text-to-image -> /v1/images/generations (1024x1024, 50 steps; base64 PNG)
curl -sS -X POST http://localhost:8000/v1/images/generations \
-H "Content-Type: application/json" \
-d '{
"model": "nvidia/Cosmos3-Nano",
"prompt": "A photorealistic red sports car on a city street at golden hour, cinematic lighting.",
"negative_prompt": "blurry, distorted, low quality",
"size": "1024x1024", "n": 1, "response_format": "b64_json",
"num_inference_steps": 50, "guidance_scale": 7.0, "seed": 42
}' | python -c "import sys,json,base64; open('cosmos3_t2i.png','wb').write(base64.b64decode(json.load(sys.stdin)['data'][0]['b64_json']))"
# Text-to-video -> /v1/videos/sync (720p, 189 frames @ 24fps; official params)
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=A robot arm is cleaning a plate in the kitchen" \
-F "negative_prompt=blurry, distorted, low quality, jittery, deformed" \
-F "size=1280x720" -F "num_frames=189" -F "fps=24" \
-F "num_inference_steps=35" -F "guidance_scale=6.0" \
-F "max_sequence_length=4096" -F "flow_shift=10.0" \
-F 'extra_params={"use_resolution_template":false,"use_duration_template":false,"guardrails":true}' \
-F "seed=123" \
-o cosmos3_t2v.mp4
# Image-to-video -> /v1/videos/sync with an uploaded reference image
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=The scene comes to life with smooth, natural motion." \
-F "negative_prompt=blurry, distorted, low quality" \
-F "size=1280x720" -F "num_frames=189" -F "fps=24" \
-F "num_inference_steps=35" -F "guidance_scale=6.0" \
-F "max_sequence_length=4096" -F "flow_shift=10.0" \
-F 'extra_params={"use_resolution_template":false,"use_duration_template":false,"guardrails":true}' \
-F "seed=1111" \
-F "input_reference=@/path/to/reference.jpg;type=image/jpeg" \
-o cosmos3_i2v.mp4
2026-06-03 02:51:44 +02:00
2026-06-08 21:04:05 +02:00
# Video-to-video -> /v1/videos/sync with an uploaded reference video.
# By default Cosmos3 conditions on latent indexes [0, 1]. For the default
# temporal VAE stride this decodes only the first 5 input frames.
# The model works best when the prompt describes the actual situation happening in the video.
# Generic prompts may create sub-standard generations.
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=Continue the same scene with smooth natural motion and consistent subjects." \
-F "negative_prompt=blurry, distorted, low quality, jittery, deformed" \
-F "size=1280x720" -F "num_frames=189" -F "fps=24" \
-F "num_inference_steps=35" -F "guidance_scale=6.0" \
-F "max_sequence_length=4096" -F "flow_shift=10.0" \
-F 'extra_params={"use_resolution_template":false,"use_duration_template":false,"guardrails":true,"condition_frame_indexes_vision":[0,1],"condition_video_keep":"first"}' \
-F "seed=2222" \
-F "input_reference=@/path/to/reference.mp4;type=video/mp4" \
-o cosmos3_v2v.mp4
# V2V can also use a JSON-safe URL/data-URL video reference. Do not combine
# video_reference with input_reference or image_reference.
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=Continue the same scene with smooth natural motion and consistent subjects." \
-F "size=1280x720" -F "num_frames=189" -F "fps=24" \
-F "num_inference_steps=35" -F "guidance_scale=6.0" \
-F "max_sequence_length=4096" -F "flow_shift=10.0" \
-F 'extra_params={"condition_frame_indexes_vision":[0,1],"condition_video_keep":"last"}' \
-F 'video_reference={"video_url":"https://example.com/reference.mp4"}' \
-o cosmos3_v2v_from_url.mp4
2026-06-03 02:51:44 +02:00
2026-06-21 03:59:18 +02:00
# Transfer V2V with a precomputed depth control video. `control_path` can point
# to a local image/video; edge and blur can also be computed from `input_reference`
2026-08-20 20:40:51 +02:00
# by passing `"edge":true` or `"blur":true`. The reference negative prompt is
# optional; omit the `negative_prompt` form field to use an empty negative branch.
2026-06-21 03:59:18 +02:00
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=Generate a realistic scene following the provided control video." \
2026-08-20 20:40:51 +02:00
--form-string "negative_prompt=$(jq -c . recipes/cosmos3/negative_prompt.json)" \
2026-06-21 03:59:18 +02:00
-F "size=1280x720" -F "num_frames=121" \
-F "num_inference_steps=50" -F "seed=125" \
-F 'extra_params={"depth":{"control_path":"/path/to/depth_control.mp4"},"max_frames":121,"resolution":"720","num_video_frames_per_chunk":121}' \
-o cosmos3_transfer_depth.mp4
# The same transfer control can be uploaded by a remote client instead of
# being placed on the server filesystem. Uploads are limited to 512 MiB.
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=Generate a realistic scene following the provided world-state control." \
-F "input_reference=@/path/to/input.mp4;type=video/mp4" \
-F "control_reference=@/path/to/wsm.mp4;type=video/mp4" \
-F "control_type=wsm" \
-F 'extra_params={"wsm":{"control_weight":1.0},"max_frames":121,"resolution":"720","num_video_frames_per_chunk":121}' \
-o cosmos3_transfer_wsm.mp4
2026-06-03 02:51:44 +02:00
# Text-to-video-with-sound
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=The video opens with a view of a well-lit indoor fruit display. A robotic arm picks up a pear, an orange, and a carambola one by one, placing each into a plastic bag in a shopping cart with red handles. The video is 7.875 seconds long, 24 FPS, and 1280x720. Audio description: soft servo whirs, gentle fruit thuds, plastic bag rustling, and a faint refrigeration hum." \
-F "negative_prompt=blurry, distorted, low quality" \
-F "size=1280x720" \
-F "num_frames=189" \
-F "fps=24" \
-F "num_inference_steps=35" \
-F "guidance_scale=6.0" \
-F "max_sequence_length=4096" \
-F "flow_shift=10.0" \
-F "seed=0" \
-F "generate_sound=true" \
-F "sound_duration=7.875" \
-F 'extra_params={"use_resolution_template":false,"use_duration_template":false,"guardrails":true}' \
-o cosmos3_t2v_with_sound.mp4
2026-06-03 21:30:39 +02:00
# Action — forward dynamics (first frame + action trajectory -> rollout video).
# Synchronous; `action` is a JSON array shaped [action_chunk_size, raw_action_dim].
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
--form-string "model=nvidia/Cosmos3-Nano" \
--form-string "prompt=You are an autonomous vehicle. This video is captured from a first-person perspective." \
-F "input_reference=@first_frame.jpg;type=image/jpeg" \
-F "size=640x480" -F "num_frames=61" -F "fps=10" \
-F "num_inference_steps=30" -F "guidance_scale=1.0" -F "flow_shift=5.0" \
--form-string "extra_params={\"action_mode\":\"forward_dynamics\",\"domain_name\":\"av\",\"raw_action_dim\":9,\"action_chunk_size\":60,\"action\":$(cat action.json)}" \
-F "seed=0" \
-o cosmos3_forward_dynamics.mp4
# Action — policy (first frame + instruction -> predicted action trajectory + video).
# Asynchronous: POST returns a job id; poll, then read the predicted action from
# the top-level `action` field ({data, shape, dtype, raw_action_dim, domain_id}).
VIDEO_ID=$(curl -sS -X POST http://localhost:8000/v1/videos \
-H "Accept: application/json" \
--form-string "model=nvidia/Cosmos3-Nano" \
--form-string "prompt=Pick up the banana and place it in the bowl." \
-F "input_reference=@first_frame.jpg;type=image/jpeg" \
-F "size=640x480" -F "num_frames=17" -F "fps=5" \
-F "num_inference_steps=30" -F "guidance_scale=1.0" -F "flow_shift=5.0" \
--form-string 'extra_params={"action_mode":"policy","domain_name":"bridge_orig_lerobot","raw_action_dim":10,"action_chunk_size":16}' \
-F "seed=0" | jq -r '.id')
# poll until status == completed, then:
curl -sS "http://localhost:8000/v1/videos/$VIDEO_ID" | jq '.action | {shape, dtype, raw_action_dim, domain_id}'
curl -sS -L "http://localhost:8000/v1/videos/$VIDEO_ID/content" -o cosmos3_policy.mp4
2026-06-08 21:04:05 +02:00
# Action — inverse dynamics (video -> recovered action trajectory).
# Asynchronous: use the job metadata to read the recovered action.
VIDEO_ID=$(curl -sS -X POST http://localhost:8000/v1/videos \
-H "Accept: application/json" \
--form-string "model=nvidia/Cosmos3-Nano" \
--form-string "prompt=Recover the robot action trajectory from this clip." \
-F "input_reference=@motion_clip.mp4;type=video/mp4" \
-F "size=640x480" -F "num_frames=17" -F "fps=5" \
-F "num_inference_steps=30" -F "guidance_scale=1.0" -F "flow_shift=5.0" \
--form-string 'extra_params={"action_mode":"inverse_dynamics","domain_name":"bridge_orig_lerobot","raw_action_dim":10,"action_chunk_size":16}' \
-F "seed=0" | jq -r '.id')
# poll until status == completed, then:
curl -sS "http://localhost:8000/v1/videos/$VIDEO_ID" | jq '.action | {shape, dtype, raw_action_dim, domain_id}'
curl -sS -L "http://localhost:8000/v1/videos/$VIDEO_ID/content" -o cosmos3_inverse_dynamics.mp4
2026-09-10 16:32:50 +02:00
# DROID websocket policy server. Use the tested cosmos-framework revision
# directly from source; installing the package can introduce dependency conflicts.
export COSMOS_FRAMEWORK_ROOT=/path/to/cosmos-framework
git -C "$COSMOS_FRAMEWORK_ROOT" checkout c14617c2bc93dacbf69674fb964eec93182933d9
export PYTHONPATH="$COSMOS_FRAMEWORK_ROOT"
2026-09-10 16:32:50 +02:00
# Validate every cosmos-framework symbol used by the action-policy pipeline
# before allocating the model.
python - <<'PY'
from vllm_omni.diffusion.models.cosmos3.utils import (
get_robolab_domain_id,
preflight_cosmos3_action_framework_imports,
)
preflight_cosmos3_action_framework_imports()
print("cosmos-framework action imports OK; DROID domain:", get_robolab_domain_id("droid_lerobot"))
PY
# The bundled deploy config (vllm_omni/deploy/cosmos3_policy_droid.yaml)
# selects the registered cosmos3_policy pipeline and carries the DROID
# checkpoint's serving defaults: JSON prompt formatting (a property of this
# checkpoint's training recipe, not a generic server default), the
# model-specific OpenPI binary handshake metadata (policy_server_config), and
# guardrails off (policy serving emits robot actions; the guardrail stack is
# not part of it, so --no-guardrails is implied).
# Policy checkpoints cannot be auto-detected — they share their HF metadata
# with the T2I/video Cosmos3 checkpoints — so --deploy-config is required.
# Per-checkpoint model_config tweaks can be layered on top with
# --stage-overrides; dict overrides deep-merge with the deploy yaml.
export VLLM_OMNI_ROOT=/absolute/path/to/vllm-omni
vllm serve nvidia/Cosmos3-Nano-Policy-DROID \
--omni \
--host 0.0.0.0 --port 8000 \
2026-09-10 16:32:50 +02:00
--deploy-config "$VLLM_OMNI_ROOT/vllm_omni/deploy/cosmos3_policy_droid.yaml" \
--robot-openpi-idle-timeout 0
2026-09-10 16:32:50 +02:00
# From a RoboLab checkout:
python policies/cosmos3/run.py \
--remote-uri ws://localhost:8000/v1/realtime/robot/openpi \
--task BananaInBowlTask
# For a server started with "--api-key $POLICY_API_KEY", pass the same value as
# a Bearer token. --remote-token takes precedence over COSMOS3_API_TOKEN.
COSMOS3_API_TOKEN="$POLICY_API_KEY" python policies/cosmos3/run.py \
--remote-uri wss://policy.example/v1/realtime/robot/openpi \
--task BananaInBowlTask
2026-06-01 22:04:31 +02:00
```
2026-09-10 16:32:50 +02:00
The endpoint sends `policy_server_config` as its initial binary MsgPack
message and returns the action array directly. RoboLab accepts that direct
array as well as proxy responses shaped as `{"action": ...}` or
`{"actions": ...}`, and turns structured server errors into client exceptions.
RoboLab supplies `session_id=robolab-episode-<episode>-env-<env_id>`, so
parallel environments have independent policy state even though they share
one WebSocket. The server tracks interleaved session IDs independently.
The default receive-idle timeout is 30 seconds; set
`--robot-openpi-idle-timeout 0` when simulator steps between replans can take
longer, or set another non-negative timeout in seconds.
If vLLM-Omni is started with `--api-key` or `VLLM_API_KEY`, the standard
OpenPI route uses the normal API authentication middleware. RoboLab's
`--remote-token` and `COSMOS3_API_TOKEN` send the required
`Authorization: Bearer ...` header.
2026-06-01 22:04:31 +02:00
#### Notes
- **Measured latency (1x B300, bf16, guardrails off, diffusion cache off):**
- T2I 1024² — 10 / 25 / 50 steps → ~0.4 / 0.7 / **1.3 s**
- T2V 1280×720 @ 35 steps — 25 / 49 / 93 / **189** frames → ~7 / 15 / 33 / **~93 s**
- I2V 1280×720, 189 frames @ 35 steps → ~**99 s**
- Action 640×480 @ 30 steps — forward-dynamics 61f ~**4 s**, policy 17f ~**1–3 s**.
- Guardrails-on overhead: ~8% on T2I, negligible on video.
2026-06-01 22:04:31 +02:00
- **Memory:** transformer ~17 GiB (bf16); peak ~46 GiB for 720p video on 1 GPU;
full repo (transformer + Wan VAE + Qwen3-VL vision encoder + audio tokenizer)
~33 GB on disk.
- **Determinism:** identical seed reproduces identical output on the same
hardware; outputs are not bit-identical across different GPU types.
- **Supported sizes (per model card):** 256p / 480p / 720p at 16:9, 4:3, 1:1,
2026-06-08 21:04:05 +02:00
3:4, 9:16. Defaults: T2I 1024², 50 steps, guidance 7.0; T2V/I2V/V2V
1280×720, 189 frames, 35 steps, guidance 6.0, `flow_shift=10.0`.
2026-06-01 22:04:31 +02:00
- **Key flags / params:** `--no-guardrails` (server) or
2026-06-03 02:51:44 +02:00
`extra_params={"guardrails":false}` (per request) toggles safety. The
per-request flag only takes effect when the server was launched **with**
guardrails enabled (it cannot re-enable them on a `--no-guardrails` server).
2026-08-20 20:40:51 +02:00
Outside transfer mode, `use_resolution_template` / `use_duration_template`
are off by default and only needed when not using upsampled prompts that
already encode resolution/duration.
2026-06-08 21:04:05 +02:00
For V2V, `condition_frame_indexes_vision` selects the clean conditioned latent
frame indexes (default `[0, 1]`), and `condition_video_keep` selects whether the
API decodes the first or last needed reference frames (`"first"` by default).
2026-06-21 03:59:18 +02:00
- **Transfer controls:** `extra_params` may include `edge`, `blur`, `depth`,
`seg`, or `wsm`. Each hint accepts `true`, a path string, or an object such as
`{"control_path": "/path/to/control.mp4"}`; `edge` also accepts
2026-08-20 20:40:51 +02:00
`preset_edge_threshold` and `blur` accepts `preset_blur_strength`. Every hint
accepts a non-negative `control_weight`; weights are normalized across active
controls and therefore only set their relative influence. A single positive
weight always normalizes to `1.0`; use `control_guidance` to change the
absolute strength of a single control. With two or more active controls, the
per-control attention passes run replicated on every sequence-parallel
(Ulysses) rank, so Ulysses does not reduce per-rank memory or latency for
multi-control transfer requests.
Transfer always uses Cosmos3's transfer-specific system prompt. By default it
also appends a directive naming every active hint and asking the model to
follow its shape, position, and motion precisely; set the request-level
`emphasize_control_in_prompt` option to `false` for prompt ablations. Transfer
does not add a negative prompt automatically. An optional reference prompt is
provided in [`negative_prompt.json`](negative_prompt.json); compact the JSON
with `jq -c` and pass it through `negative_prompt` as shown in the transfer
example. Transfer enables duration/FPS and resolution metadata on both CFG
branches. Set `use_duration_template` or `use_resolution_template` to
`false` to disable either template. `negative_metadata_mode` accepts `same`
(the transfer default), `inverse`, or `none`.
2026-06-21 03:59:18 +02:00
Transfer-level options include `control_guidance`,
`control_guidance_interval`, `num_video_frames_per_chunk` (default `93`,
`101` for WSM), `num_conditional_frames` (default `1`),
`num_first_chunk_conditional_frames`, `max_frames`,
`show_control_condition`, `show_input`, and
`share_vision_temporal_positions`. Non-WSM transfer preserves the input video
fps when available; WSM defaults to 10 fps unless `fps` is supplied.
- **DROID OpenPI observations:** include a string `prompt`, either
`observation/image` or the three-view DROID camera keys
(`observation/wrist_image_left`, `observation/exterior_image_1_left`,
`observation/exterior_image_2_left`), plus `observation/gripper_position` and
`observation/joint_position`. Optional extra params include `history_length`,
`conditioning_fps`, `action_chunk_size`, `raw_action_dim`, `deterministic_seed`,
and `session_id`.
2026-06-01 22:04:31 +02:00
- **Known limitations:**
- Guardrails-on requires `cosmos-guardrail` **and** access to the gated
2026-06-01 22:04:31 +02:00
`nvidia/Cosmos-1.0-Guardrail` repo (accept license + `HF_TOKEN`); otherwise
the server fails at pipeline build with a gated-repo / safety-checker error.
- A guardrail-blocked prompt currently returns HTTP 500
2026-06-01 22:04:31 +02:00
(`"Guardrail blocked prompt"`).
- Action `forward_dynamics`, `policy`, and `inverse_dynamics` are supported
2026-06-08 21:04:05 +02:00
online. Use async `POST /v1/videos` when you need the predicted/recovered
action payload under the top-level `action` field; sync `/v1/videos/sync`
returns raw MP4 bytes and does not expose action metadata in the response body.
2026-06-01 22:04:31 +02:00
### 1x GPU (Offline generation)
#### Environment
- OS: Ubuntu 22.04+
- Python: 3.12+
- Driver / runtime: NVIDIA CUDA environment
- vLLM-Omni version or commit: use the commit you are deploying from
#### Command
Cosmos3 runs through the standard task examples; pass model-specific knobs via
`--extra-body`. Guardrails are on by default — pass `"guardrails": false` for a
quick local run (install `cosmos-guardrail` + accept the gated repo to enable them).
2026-06-01 22:04:31 +02:00
```bash
# Text-to-image -> examples/offline_inference/text_to_image
python examples/offline_inference/text_to_image/text_to_image.py \
--model nvidia/Cosmos3-Nano \
--prompt "A photorealistic red sports car at golden hour, cinematic lighting." \
--negative-prompt "blurry, distorted, low quality" \
--height 1024 --width 1024 --num-inference-steps 50 --guidance-scale 7.0 \
--extra-body '{"flow_shift": 3.0, "guardrails": false}' \
--output cosmos3_t2i.png
2026-06-01 22:04:31 +02:00
# Text-to-video -> examples/offline_inference/text_to_video
python examples/offline_inference/text_to_video/text_to_video.py \
--model nvidia/Cosmos3-Nano \
--prompt "A robot arm is cleaning a plate in the kitchen." \
--negative-prompt "blurry, distorted, low quality, jittery, deformed" \
--height 720 --width 1280 --num-frames 189 --fps 24 \
--num-inference-steps 35 --guidance-scale 6.0 \
--extra-body '{"flow_shift": 10.0, "max_sequence_length": 4096, "guardrails": false,
"use_resolution_template": false, "use_duration_template": false}' \
--output cosmos3_t2v.mp4
2026-06-01 22:04:31 +02:00
# Image-to-video -> examples/offline_inference/image_to_video
# (Cosmos3 bundles example frames under assets/; any RGB image works too.)
python examples/offline_inference/image_to_video/image_to_video.py \
--model nvidia/Cosmos3-Nano \
--image /path/to/Cosmos3-Nano/assets/example_i2v_input.jpg \
--prompt "The scene comes to life with smooth, natural motion." \
--height 720 --width 1280 --num-frames 189 --fps 24 \
--num-inference-steps 35 --guidance-scale 6.0 \
--extra-body '{"flow_shift": 10.0, "max_sequence_length": 4096, "guardrails": false}' \
--output cosmos3_i2v.mp4
2026-06-01 22:04:31 +02:00
```
#### Verification
```bash
python -c "from PIL import Image; im=Image.open('cosmos3_t2i.png'); print('image', im.size, im.mode)"
ffprobe -v error -show_entries stream=codec_type,nb_frames,width,height cosmos3_t2v.mp4
2026-06-01 22:04:31 +02:00
```
#### Notes
- A single `Cosmos3OmniDiffusersPipeline` serves every mode; the standard examples
select it automatically from `model_index.json`. T2I is chosen by the
`text_to_image` prompt builder (which marks `modalities=["image"]`); `text_to_video`
defaults to T2V; `image_to_video` adds `multi_modal_data={"image": ...}` (I2V).
V2V is served online (`/v1/videos/sync`).
- Model-specific knobs (`flow_shift`, `max_sequence_length`, `condition_*`,
`generate_sound`/`sound_duration`, `guardrails`, `action_*`, ...) are declared
once in `vllm_omni/model_extras/cosmos3.py` and forwarded through `--extra-body`;
unknown keys for the model are dropped.
## ROCm
### 1x MI350X (Online serving)
#### Environment
- OS: Ubuntu 22.04+
- Python: 3.12+
- Driver / runtime: ROCm, HIP 7.2, **gfx942 or gfx950** (validated on gfx950 / MI350X,
which exposes 252 GiB of HBM to PyTorch)
- PyTorch: 2.11.0 (ROCm build)
- vLLM version: match the repository requirements from your current checkout
- vLLM-Omni version or commit: use the commit you are deploying from
No ROCm-specific patch or flag is needed: Cosmos3 runs on the stock code path.
#### Command
The NVIDIA command works unchanged. Guardrails need the gated
`nvidia/Cosmos-1.0-Guardrail` repo, so the quick path is to disable them (you are
then responsible for license compliance):
```bash
vllm serve nvidia/Cosmos3-Nano \
--omni \
--no-guardrails \
--host 0.0.0.0 --port 8000 \
--init-timeout 1800
```
For 720p video, add `--vae-use-tiling`: it cuts peak VRAM by ~68% for ~13%
latency, which is the difference between needing a 120 GiB card and a 40 GiB one.
`--quantization fp8` and `--enable-layerwise-offload` are also supported; see the
Notes for the measured cost of each. For extra GPUs use `--ulysses-degree N` or
`--tensor-parallel-size N` (not validated on ROCm yet).
#### Verification
Use the same `curl` requests as the CUDA section above. The ROCm images do not
ship `ffprobe`, so the CUDA section's `ffprobe` line does not work here. `imageio`
and `pyav` are already installed and report the same thing:
```bash
python -c "from PIL import Image; im=Image.open('cosmos3_t2i.png'); print(im.size, im.mode)"
python -c "import imageio.v3 as iio; print(iio.improps('cosmos3_t2v.mp4', plugin='pyav').shape)"
```
Expect `(1024, 1024) RGB` and `(189, 720, 1280, 3)` for the requests above.
Re-running the same request with the same seed reproduces a byte-identical mp4,
so an md5 comparison across two runs also works as a smoke check.
#### Notes
- **Warm up once per output shape before timing anything.** The first generation
of a shape pays a one-time cost that is cached on disk and reused by later
processes in the same container: a ~250 s aiter attention-kernel JIT build plus
a ~390 s build of the VAE decode path *for that shape*, so warming 1024² images
does nothing for 189-frame video. At 720p / 189 frames / 35 steps this is
**540 s cold vs 161 s warm, with byte-identical output**.
- **Measured on 1x MI350X (bf16, guardrails off, diffusion cache off, warm):** T2I 1024² @ 50 steps
**~2.7 s**; T2V 1280×720 / 189 frames @ 35 steps **~161 s**, of which ~92% is
the DiT denoise loop and ~4% VAE decode, so optimization effort belongs in the
denoise loop. The optional flags act on different terms of the memory bill and
are therefore complementary rather than redundant:
| Flag | Latency | Peak reserved | Peak allocated | Acts on |
| --- | --- | --- | --- | --- |
| *(none)* | 161 s | 120 GiB | 95 GiB | — |
| `--vae-use-tiling` | 183 s (+13.5%) | **38 GiB** (−68%) | **36 GiB** (−62%) | decode activations |
| `--enable-layerwise-offload` | 162 s (+0.8%) | 84 GiB (−30%) | 69 GiB (−27%) | weights |
| `--quantization fp8` | 150 s (−6.6%) | 107 GiB (−11%) | 82 GiB (−14%) | weights (online, no calibration) |
Reserved is the caching-allocator high water mark and runs ~2–25 GiB above
allocated; allocated is the figure to compare against another platform.
- **`--vae-use-tiling` is by far the biggest lever for video.** Of the 95 GiB
allocated by default only ~35 GiB is weights and fixed overhead; the remaining
**60 GiB is activations from decoding all 189 frames in one piece**, which is
why fp8 and offload cannot touch it and why tiling, which leaves under 5 GiB of
them, wins so much. `--vae-use-slicing` is a no-op for a single video request:
peak memory is bit-identical to the default.
- **`--enable-layerwise-offload` is close to free on this part:** per-block DiT
compute is long enough to hide the weight transfer, so on a card this size it
buys headroom for concurrency or higher resolution rather than the
speed-for-memory trade it is on small cards.
- **Attention backend:** the numbers above are aiter FlashAttention, not SDPA.
ROCm picks aiter when it is installed and the encoded device capability
satisfies `90 < major * 10 + minor < 100` (gfx942 and gfx950); otherwise it
falls back to `TORCH_SDPA` and says so only at debug level, which is easy to
miss. If you are reporting numbers, assert the choice instead of trusting the
default: `RocmOmniPlatform.get_diffusion_attn_backend_cls(None, 128)` returns
the resolved path.
- **Determinism is per-configuration:** same seed and same flags reproduce a
byte-identical mp4 across processes, but different configurations do not match
each other — including offload, which nominally only relocates weights, so do
not use it as a bit-exact regression baseline. Quality was not evaluated for
any configuration.
### 1x MI350X (Offline generation)
#### Environment
Same as the online serving section above.
#### Command
The standard task examples run unchanged:
```bash
# Text-to-image
python examples/offline_inference/text_to_image/text_to_image.py \
--model nvidia/Cosmos3-Nano \
--prompt "A photorealistic red sports car at golden hour, cinematic lighting." \
--negative-prompt "blurry, distorted, low quality" \
--height 1024 --width 1024 --num-inference-steps 50 --guidance-scale 7.0 \
--extra-body '{"flow_shift": 3.0, "guardrails": false}' \
--output cosmos3_t2i.png
# Text-to-video
python examples/offline_inference/text_to_video/text_to_video.py \
--model nvidia/Cosmos3-Nano \
--prompt "A robot arm is cleaning a plate in the kitchen." \
--negative-prompt "blurry, distorted, low quality" \
--height 720 --width 1280 --num-frames 189 --fps 24 \
--num-inference-steps 35 --guidance-scale 6.0 --flow-shift 10.0 \
--extra-body '{"max_sequence_length": 4096, "guardrails": false,
"use_resolution_template": false, "use_duration_template": false}' \
--output cosmos3_t2v.mp4
```
Both scripts print `Total generation time` and `Worker peak GPU memory
(reserved)`, which is where the latency and reserved figures in the Notes above
come from. Add `--vae-use-tiling`, `--quantization fp8` or
`--enable-layerwise-offload` to reproduce the other rows. The allocated column
needs `VLLM_LOGGING_LEVEL=DEBUG`, which enables a per-request line reporting
reserved, allocated and pool overhead together.
#### Verification
Same as the online serving section above.
#### Notes
- Run each command twice and read the second run; the first pays the one-time
compilation described above. If you run in a container, keep the container
alive between runs (`docker run --rm` throws the JIT cache away and you pay
the ~250 s kernel build every time).
- Latency and memory figures are identical to the online serving section — they
were taken from this path, because a single process has no client/server
timing ambiguity.
## NPU
### 1x Ascend 910B / 910C (Atlas A2 / A3) — Online serving
#### Environment
- OS: Linux (aarch64)
- Python: 3.12+
- Driver / runtime: CANN 8.5.1 + NNAL + Ascend 910B / 910C
- vLLM version: match the repository requirements from your current checkout
- vLLM-Ascend version: match the repository requirements from your current checkout
- vLLM-Omni version or commit: use the commit you are deploying from
#### Command
Requires the `vllm-omni` package (or the `quay.io/atlas-ci/vllm-ascend` A2 / A3 container),
which provides the `vllm serve … --omni` entrypoint used below.
Safety guardrails are **on by default** (NVIDIA Open Model License). They load
the **gated** `nvidia/Cosmos-1.0-Guardrail` model, so to keep them on you must:
1. `pip install cosmos-guardrail`
2. Accept the license at <https://huggingface.co/nvidia/Cosmos-1.0-Guardrail>
3. Export a token with access: `export HF_TOKEN=hf_...`
Then launch the recommended server:
```bash
vllm serve nvidia/Cosmos3-Nano \
--omni \
--host 0.0.0.0 --port 8000 \
--init-timeout 1800
```
To run **without** guardrails (you are responsible for license compliance),
add `--no-guardrails` (no token/`cosmos-guardrail` needed). For tensor parallel
add `--tensor-parallel-size 8`. `--quantization fp8` and
`--enable-layerwise-offload` are not supported on NPU.
The pipeline auto-resolves from `model_index.json`; pass
`--model-class-name Cosmos3OmniDiffusersPipeline` to force it explicitly.
#### Verification
Best quality uses the JSON-upsampled prompts from `assets/` (download with
`hf download nvidia/Cosmos3-Nano assets/ --local-dir Cosmos3-Nano`). Minimal
self-contained examples:
```bash
curl http://localhost:8000/v1/models
# Text-to-image -> /v1/images/generations (1024x1024, 10 steps; base64 PNG)
curl -sS -X POST http://localhost:8000/v1/images/generations \
-H "Content-Type: application/json" \
-d '{
"model": "nvidia/Cosmos3-Nano",
"prompt": "A photorealistic red sports car on a city street at golden hour, cinematic lighting.",
"negative_prompt": "blurry, distorted, low quality",
"size": "1024x1024", "n": 1, "response_format": "b64_json",
"num_inference_steps": 10, "guidance_scale": 7.0, "seed": 42
}' | python -c "import sys,json,base64; open('cosmos3_t2i.png','wb').write(base64.b64decode(json.load(sys.stdin)['data'][0]['b64_json']))"
# Text-to-video -> /v1/videos/sync (720p, 49 frames @ 24fps)
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=A robot arm is cleaning a plate in the kitchen" \
-F "negative_prompt=blurry, distorted, low quality, jittery, deformed" \
-F "size=1280x720" -F "num_frames=49" -F "fps=24" \
-F "num_inference_steps=20" -F "guidance_scale=6.0" \
-F "max_sequence_length=4096" -F "flow_shift=10.0" \
-F "seed=123" \
-o cosmos3_t2v.mp4
# Image-to-video -> /v1/videos/sync with an uploaded reference image
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=The scene comes to life with smooth, natural motion." \
-F "size=1280x720" -F "num_frames=25" -F "fps=8" \
-F "num_inference_steps=10" -F "guidance_scale=6.0" \
-F "seed=42" \
-F "input_reference=@reference.jpg;type=image/jpeg" \
-o cosmos3_i2v.mp4
# Video-to-video -> /v1/videos/sync with an uploaded reference video
curl -sS -X POST http://localhost:8000/v1/videos/sync \
-H "Accept: video/mp4" \
-F "model=nvidia/Cosmos3-Nano" \
-F "prompt=Continue the same scene with smooth natural motion and consistent subjects." \
-F "size=1280x720" -F "num_frames=17" -F "fps=5" \
-F "num_inference_steps=10" -F "guidance_scale=6.0" \
-F "seed=42" \
-F "input_reference=@reference.mp4;type=video/mp4" \
-o cosmos3_v2v.mp4
```
#### Notes
- **Measured latency (1x Ascend 910B / 910C, bf16, guardrails off, diffusion cache off):**
- T2I 1024² — 10 steps → ~8 s
- T2V 1280×720 @ 20 steps — 49 frames → ~55 s
- I2V 1280×720 @ 10 steps — 25 frames → ~25 s
- V2V 480×320 @ 10 steps — 17 frames → ~12 s
- **Memory:** transformer ~17 GiB (bf16); peak ~46 GiB for 720p video on 1 NPU;
full repo (transformer + Wan VAE + Qwen3-VL vision encoder + audio tokenizer)
~33 GB on disk.
- **Determinism:** identical seed reproduces identical output on the same
hardware; outputs are not bit-identical across different GPU/NPU types.
- **Supported sizes (per model card):** 256p / 480p / 720p at 16:9, 4:3, 1:1,
3:4, 9:16. Defaults: T2I 1024², 50 steps, guidance 7.0; T2V/I2V/V2V
1280×720, 35 steps, guidance 6.0, `flow_shift=10.0`.
- **Key flags / params:** `--no-guardrails` (optional, to disable guardrails), `--init-timeout 1800`
(for model loading), `--tensor-parallel-size 8` for multi-NPU, and
`--model-class-name Cosmos3OmniDiffusersPipeline` to force the pipeline class.
- **Known limitations:**
- Transfer V2V with `extra_params` (`edge`/`blur`/`depth`/`seg`/`wsm`) hits a
resolution-parsing bug; basic V2V without transfer hints works.
- FP8 online quantization and layerwise offload are not supported on NPU.
### 1x Ascend 910B / 910C (Atlas A2 / A3) — Offline generation
#### Environment
- OS: Linux (aarch64)
- Python: 3.12+
- Driver / runtime: CANN 8.5.1 + NNAL + Ascend 910B / 910C
- vLLM-Omni version or commit: use the commit you are deploying from
#### Command
The same offline task examples run on NPU; pass model-specific knobs via
`--extra-body`. Guardrails are on by default — pass `"guardrails": false` for a
quick local run (install `cosmos-guardrail` + accept the gated repo to enable them).
```bash
# Text-to-image -> examples/offline_inference/text_to_image
python examples/offline_inference/text_to_image/text_to_image.py \
--model nvidia/Cosmos3-Nano \
--prompt "A photorealistic red sports car at golden hour, cinematic lighting." \
--negative-prompt "blurry, distorted, low quality" \
--height 1024 --width 1024 --num-inference-steps 50 --guidance-scale 7.0 \
--extra-body '{"flow_shift": 3.0, "guardrails": false}' \
--output cosmos3_t2i.png
# Text-to-video -> examples/offline_inference/text_to_video
python examples/offline_inference/text_to_video/text_to_video.py \
--model nvidia/Cosmos3-Nano \
--prompt "A robot arm is cleaning a plate in the kitchen." \
--negative-prompt "blurry, distorted, low quality, jittery, deformed" \
--height 720 --width 1280 --num-frames 189 --fps 24 \
--num-inference-steps 35 --guidance-scale 6.0 \
--extra-body '{"flow_shift": 10.0, "max_sequence_length": 4096, "guardrails": false,
"use_resolution_template": false, "use_duration_template": false}' \
--output cosmos3_t2v.mp4
# Image-to-video -> examples/offline_inference/image_to_video
# (Cosmos3 bundles example frames under assets/; any RGB image works too.)
python examples/offline_inference/image_to_video/image_to_video.py \
--model nvidia/Cosmos3-Nano \
--image /path/to/Cosmos3-Nano/assets/example_i2v_input.jpg \
--prompt "The scene comes to life with smooth, natural motion." \
--height 720 --width 1280 --num-frames 189 --fps 24 \
--num-inference-steps 35 --guidance-scale 6.0 \
--extra-body '{"flow_shift": 10.0, "max_sequence_length": 4096, "guardrails": false}' \
--output cosmos3_i2v.mp4
```
#### Verification
```bash
python -c "from PIL import Image; im=Image.open('cosmos3_t2i.png'); print('image', im.size, im.mode)"
ffprobe -v error -show_entries stream=codec_type,nb_frames,width,height cosmos3_t2v.mp4
```
#### Notes
- Guardrails are on by default on NPU with `cosmos-guardrail` installed. Pass
`"guardrails": false` in `--extra-body` to disable them (there is no
`--no-guardrails` flag for the offline scripts).
- Video at the 189-frame default takes ~15 min/clip on 1 NPU; reduce
`--num-frames` for faster iteration.