Skip to content

Commit 284419b

Browse files
Cosmos3 ModularPipeline (#14110)
* Cosmos3 ModularPipeline initial commit * Fix from_pretrained for modular pipleine without modular_model_index.json * Isolate text prep inside Cosmos3TextEncoderStep; add Cosmos3ActionTextStep * Cosmos3 modular encode/decode flow * Drop device/dtype required InputParams; use components._execution_device and model dtype * Add lazy pipeline properties and requires_safety_checker-based safety gating * Drop callback_on_step_end from Cosmos3 denoise loop * Move container/auto blocks into modular_blocks * Move action_gen check, remove unused params * Declare video_processor as ComponentSpec; drop unused pipeline state * Split VAE encode block by modality (image and video) * Remove Cosmos3AssembleResultStep; add Cosmos3ExportStep * Remove unused action_mode * Refactor Cosmos3 modular denoising by modality * Document Cosmos3 modular output selection * Address Cosmos3 modular review feedback * Remaining local-variable device cleanup * Address Cosmos3 modular review feedback * Address final Cosmos3 modular feedback --------- Co-authored-by: Atharva Joshi <atjoshi@nvidia.com>
1 parent 208704a commit 284419b

18 files changed

Lines changed: 3863 additions & 3 deletions

docs/source/en/api/pipelines/cosmos3.md

Lines changed: 202 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,211 @@ pipe = Cosmos3OmniPipeline.from_pretrained(
738738
- all
739739
- __call__
740740

741+
## Cosmos3OmniModularPipeline
742+
743+
Cosmos 3 is also available as a Modular Diffusers pipeline. The task-based [`Cosmos3OmniPipeline`] remains available; the modular pipeline coexists with it and covers the same modes (`text2image`, `text2video`, `image2video`, `video2video`, and action-conditioned generation, with optional sound when supported by the checkpoint).
744+
745+
```python
746+
import torch
747+
from diffusers import Cosmos3OmniModularPipeline
748+
749+
pipe = Cosmos3OmniModularPipeline.from_pretrained(
750+
"nvidia/Cosmos3-Nano", torch_dtype=torch.bfloat16
751+
)
752+
pipe.load_components(torch_dtype=torch.bfloat16)
753+
pipe.enable_safety_checker()
754+
755+
videos = pipe(
756+
prompt='{"scene":"A robot arm in a kitchen"}',
757+
num_frames=1,
758+
height=720,
759+
width=1280,
760+
output="videos",
761+
)
762+
763+
# Modular pipelines expose declared outputs directly instead of using the task pipeline's
764+
# `return_dict`/`Cosmos3OmniPipelineOutput` API.
765+
image = videos[0]
766+
```
767+
768+
You can also load through [`ModularPipeline`] and let the repository config select the blocks class:
769+
770+
```python
771+
import torch
772+
from diffusers import ModularPipeline
773+
774+
pipe = ModularPipeline.from_pretrained("nvidia/Cosmos3-Nano", torch_dtype=torch.bfloat16)
775+
pipe.load_components(torch_dtype=torch.bfloat16)
776+
pipe.enable_safety_checker()
777+
videos = pipe(
778+
prompt='{"scene":"A robot arm in a kitchen"}', num_frames=1, height=720, width=1280, output="videos"
779+
)
780+
```
781+
782+
To inspect or customize a specific Cosmos modular workflow, use `available_workflows` + `get_workflow()`:
783+
784+
```python
785+
available = pipe.blocks.available_workflows
786+
image2video_blocks = pipe.blocks.get_workflow("image2video")
787+
```
788+
789+
### Modular examples for all existing workflows
790+
791+
The modular pipeline supports the same call signatures as the task pipeline. The snippets below mirror every generation example shown above (`text2video`, `text2image`, `image2video`, `video2video`, `video2video_sound`, `text2video_sound`, and `action_policy`).
792+
793+
```python
794+
import json
795+
import torch
796+
from diffusers import Cosmos3OmniModularPipeline, CosmosActionCondition
797+
from diffusers.schedulers.scheduling_unipc_multistep import UniPCMultistepScheduler
798+
from diffusers.utils import encode_video, export_to_video, load_image, load_video
799+
800+
pipe = Cosmos3OmniModularPipeline.from_pretrained("nvidia/Cosmos3-Nano", torch_dtype=torch.bfloat16)
801+
pipe.load_components(torch_dtype=torch.bfloat16)
802+
pipe.enable_safety_checker()
803+
pipe.to("cuda")
804+
pipe.scheduler = UniPCMultistepScheduler.from_config(
805+
pipe.scheduler.config, flow_shift=10.0, use_karras_sigmas=False
806+
)
807+
808+
# text2video
809+
json_prompt = json.load(open("assets/example_t2v_prompt.json"))
810+
negative_prompt = json.load(open("assets/negative_prompt.json"))
811+
videos = pipe(
812+
prompt=json.dumps(json_prompt),
813+
negative_prompt=json.dumps(negative_prompt),
814+
num_frames=189,
815+
height=720,
816+
width=1280,
817+
num_inference_steps=35,
818+
guidance_scale=6.0,
819+
fps=24.0,
820+
output="videos",
821+
)
822+
export_to_video(videos, "cosmos3_modular_t2v.mp4", fps=24, macro_block_size=1)
823+
824+
# text2image
825+
json_prompt = json.load(open("assets/example_t2i_prompt.json"))
826+
videos = pipe(prompt=json.dumps(json_prompt), num_frames=1, height=720, width=1280, output="videos")
827+
videos[0].save("cosmos3_modular_t2i.jpg", format="JPEG", quality=85)
828+
829+
# image2video
830+
json_prompt = json.load(open("assets/example_i2v_prompt.json"))
831+
negative_prompt = json.load(open("assets/negative_prompt_i2v.json"))
832+
image = load_image("https://github.com/nvidia-cosmos/cosmos-dependencies/releases/download/assets/robot_153.jpg")
833+
videos = pipe(
834+
prompt=json.dumps(json_prompt),
835+
negative_prompt=json.dumps(negative_prompt),
836+
image=image,
837+
num_frames=189,
838+
height=720,
839+
width=1280,
840+
fps=24.0,
841+
output="videos",
842+
)
843+
export_to_video(videos, "cosmos3_modular_i2v.mp4", fps=24, macro_block_size=1)
844+
845+
# video2video
846+
json_prompt = json.load(open("assets/example_v2v_prompt.json"))
847+
negative_prompt = json.load(open("assets/negative_prompt_i2v.json"))
848+
video = load_video(
849+
"https://github.com/nvidia-cosmos/cosmos-dependencies/raw/refs/heads/assets/cosmos3/inputs/vision/robot_pouring.mp4"
850+
)
851+
videos = pipe(
852+
prompt=json.dumps(json_prompt),
853+
negative_prompt=json.dumps(negative_prompt),
854+
video=video,
855+
condition_frame_indexes_vision=[0, 1],
856+
condition_video_keep="first",
857+
num_frames=189,
858+
height=720,
859+
width=1280,
860+
num_inference_steps=35,
861+
guidance_scale=6.0,
862+
fps=24.0,
863+
output="videos",
864+
)
865+
export_to_video(videos, "cosmos3_modular_v2v.mp4", fps=24, macro_block_size=1)
866+
867+
# video2video_sound
868+
outputs = pipe(
869+
prompt=json.dumps(json_prompt),
870+
negative_prompt=json.dumps(negative_prompt),
871+
video=video,
872+
condition_frame_indexes_vision=[0, 1],
873+
condition_video_keep="first",
874+
num_frames=189,
875+
height=720,
876+
width=1280,
877+
fps=24.0,
878+
enable_sound=True,
879+
output=["videos", "sound", "sampling_rate"],
880+
)
881+
encode_video(
882+
outputs["videos"],
883+
fps=24,
884+
audio=outputs["sound"],
885+
audio_sample_rate=outputs["sampling_rate"],
886+
output_path="cosmos3_modular_v2v_with_sound.mp4",
887+
)
888+
889+
# text2video_sound
890+
json_prompt = json.load(open("assets/example_t2v_sound_prompt.json"))
891+
negative_prompt = json.load(open("assets/negative_prompt.json"))
892+
outputs = pipe(
893+
prompt=json.dumps(json_prompt),
894+
negative_prompt=json.dumps(negative_prompt),
895+
num_frames=189,
896+
height=720,
897+
width=1280,
898+
fps=24.0,
899+
enable_sound=True,
900+
output=["videos", "sound", "sampling_rate"],
901+
)
902+
encode_video(
903+
outputs["videos"],
904+
fps=24,
905+
audio=outputs["sound"],
906+
audio_sample_rate=outputs["sampling_rate"],
907+
output_path="cosmos3_modular_t2v_with_sound.mp4",
908+
)
909+
910+
# action_policy
911+
prompt = "Put the pot to the left of the purple item."
912+
action_video = load_video(
913+
"https://github.com/nvidia-cosmos/cosmos-dependencies/raw/refs/heads/assets/cosmos3/inputs/action/bridge_20260501_0.mp4"
914+
)
915+
outputs = pipe(
916+
prompt=prompt,
917+
action=CosmosActionCondition(
918+
mode="policy",
919+
chunk_size=16,
920+
domain_name="bridge_orig_lerobot",
921+
resolution_tier=480,
922+
video=action_video,
923+
view_point="ego_view",
924+
),
925+
fps=5,
926+
num_inference_steps=30,
927+
guidance_scale=1.0,
928+
use_system_prompt=False,
929+
output=["videos", "action"],
930+
)
931+
export_to_video(outputs["videos"], "cosmos3_modular_action_policy.mp4", fps=5, macro_block_size=1)
932+
if outputs["action"] is not None:
933+
with open("cosmos3_modular_action_policy.json", "w") as f:
934+
json.dump(outputs["action"][0].tolist(), f)
935+
```
936+
937+
[[autodoc]] Cosmos3OmniModularPipeline
938+
939+
- all
940+
- __call__
941+
741942
## CosmosActionCondition
742943

743944
[[autodoc]] CosmosActionCondition
744945

745946
## Cosmos3OmniPipelineOutput
746947

747-
[[autodoc]] pipelines.cosmos.pipeline_cosmos3_omni.Cosmos3OmniPipelineOutput
948+
[[autodoc]] pipelines.cosmos.pipeline_cosmos3_omni.Cosmos3OmniPipelineOutput

src/diffusers/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,8 @@
477477
[
478478
"AnimaAutoBlocks",
479479
"AnimaModularPipeline",
480+
"Cosmos3OmniBlocks",
481+
"Cosmos3OmniModularPipeline",
480482
"ErnieImageAutoBlocks",
481483
"ErnieImageModularPipeline",
482484
"Flux2AutoBlocks",
@@ -1327,6 +1329,8 @@
13271329
from .modular_pipelines import (
13281330
AnimaAutoBlocks,
13291331
AnimaModularPipeline,
1332+
Cosmos3OmniBlocks,
1333+
Cosmos3OmniModularPipeline,
13301334
ErnieImageAutoBlocks,
13311335
ErnieImageModularPipeline,
13321336
Flux2AutoBlocks,

src/diffusers/modular_pipelines/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@
9797
"AnimaAutoBlocks",
9898
"AnimaModularPipeline",
9999
]
100+
_import_structure["cosmos"] = [
101+
"Cosmos3OmniBlocks",
102+
"Cosmos3OmniModularPipeline",
103+
]
100104
_import_structure["ernie_image"] = [
101105
"ErnieImageAutoBlocks",
102106
"ErnieImageModularPipeline",
@@ -124,6 +128,7 @@
124128
else:
125129
from .anima import AnimaAutoBlocks, AnimaModularPipeline
126130
from .components_manager import ComponentsManager
131+
from .cosmos import Cosmos3OmniBlocks, Cosmos3OmniModularPipeline
127132
from .ernie_image import ErnieImageAutoBlocks, ErnieImageModularPipeline
128133
from .flux import FluxAutoBlocks, FluxKontextAutoBlocks, FluxKontextModularPipeline, FluxModularPipeline
129134
from .flux2 import (
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from typing import TYPE_CHECKING
2+
3+
from ...utils import (
4+
DIFFUSERS_SLOW_IMPORT,
5+
OptionalDependencyNotAvailable,
6+
_LazyModule,
7+
get_objects_from_module,
8+
is_torch_available,
9+
is_transformers_available,
10+
)
11+
12+
13+
_dummy_objects = {}
14+
_import_structure = {}
15+
16+
try:
17+
if not (is_transformers_available() and is_torch_available()):
18+
raise OptionalDependencyNotAvailable()
19+
except OptionalDependencyNotAvailable:
20+
from ...utils import dummy_torch_and_transformers_objects # noqa F403
21+
22+
_dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
23+
else:
24+
_import_structure["modular_blocks_cosmos3"] = ["Cosmos3OmniBlocks"]
25+
_import_structure["modular_pipeline"] = ["Cosmos3OmniModularPipeline"]
26+
27+
if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
28+
try:
29+
if not (is_transformers_available() and is_torch_available()):
30+
raise OptionalDependencyNotAvailable()
31+
except OptionalDependencyNotAvailable:
32+
from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
33+
else:
34+
from .modular_blocks_cosmos3 import Cosmos3OmniBlocks
35+
from .modular_pipeline import Cosmos3OmniModularPipeline
36+
else:
37+
import sys
38+
39+
sys.modules[__name__] = _LazyModule(
40+
__name__,
41+
globals()["__file__"],
42+
_import_structure,
43+
module_spec=__spec__,
44+
)
45+
46+
for name, value in _dummy_objects.items():
47+
setattr(sys.modules[__name__], name, value)

0 commit comments

Comments
 (0)