Skip to content

Commit 27783f0

Browse files
committed
Align Cosmos3 transfer blocks with modular conventions
1 parent 181fc26 commit 27783f0

5 files changed

Lines changed: 618 additions & 204 deletions

File tree

src/diffusers/modular_pipelines/cosmos/before_denoise.py

Lines changed: 89 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import torch
55

66
from ...configuration_utils import FrozenDict
7-
from ...models.autoencoders.autoencoder_kl_wan import AutoencoderKLWan
87
from ...models.transformers.transformer_cosmos3 import Cosmos3OmniTransformer
98
from ...pipelines.cosmos.pipeline_cosmos3_omni import _EMBODIMENT_TO_DOMAIN_ID, CosmosActionCondition
109
from ...schedulers import UniPCMultistepScheduler
@@ -1129,113 +1128,65 @@ class Cosmos3TransferPrepareLatentsStep(ModularPipelineBlocks):
11291128
@property
11301129
def description(self) -> str:
11311130
return (
1132-
"Per-chunk transfer latent prep: slice + pad the chunk's control maps, seed the target's conditioning "
1133-
"frames (first chunk from the input video, later chunks from the previous chunk's tail), encode the "
1134-
"controls as clean latents and build the noisy target latents, velocity mask and condition latents."
1131+
"Per-chunk transfer latent prep: takes the clean target latents encoded by "
1132+
"Cosmos3TransferChunkVaeEncoderStep and builds the noisy target latents, velocity mask, condition latents "
1133+
"and conditioned-frame indexes for this chunk."
11351134
)
11361135

11371136
@property
11381137
def expected_components(self) -> list[ComponentSpec]:
1139-
return [
1140-
ComponentSpec("transformer", Cosmos3OmniTransformer),
1141-
ComponentSpec("vae", AutoencoderKLWan),
1142-
ComponentSpec(
1143-
"video_processor",
1144-
VideoProcessor,
1145-
config=FrozenDict({"vae_scale_factor": 16, "resample": "bilinear"}),
1146-
default_creation_method="from_config",
1147-
),
1148-
]
1138+
return [ComponentSpec("transformer", Cosmos3OmniTransformer)]
11491139

11501140
@property
11511141
def inputs(self) -> list[InputParam]:
11521142
return [
1153-
# Loop carry (set by the chunk-loop wrapper):
1154-
InputParam(name="chunk_id", type_hint=int, default=0),
1155-
InputParam(name="previous_output", default=None),
1156-
# Setup artifacts the loop can't cheaply re-derive (preprocessed controls + chunk geometry):
1157-
InputParam(name="control_frames", required=True),
1158-
InputParam(name="chunk_frames", required=True),
1159-
InputParam(name="total_frames", required=True),
1160-
InputParam(name="stride", required=True),
1161-
# User inputs (mirrors how the other prepare-latents steps declare height/width/generator/etc.):
1162-
InputParam(name="height", required=True),
1163-
InputParam(name="width", required=True),
1164-
InputParam(name="video", default=None),
1165-
InputParam(name="num_first_chunk_conditional_frames", type_hint=int, default=0),
1166-
InputParam(name="num_conditional_frames", type_hint=int, default=1),
1167-
InputParam(name="generator", default=None),
1143+
InputParam(
1144+
name="x0_tokens_vision",
1145+
type_hint=torch.Tensor,
1146+
required=True,
1147+
description="Clean target vision latents encoded from the seeded target frames.",
1148+
),
1149+
InputParam(
1150+
name="current_conditional_frames",
1151+
type_hint=int,
1152+
required=True,
1153+
description="Number of pixel frames used to seed this chunk's target.",
1154+
),
1155+
InputParam.template("generator"),
11681156
]
11691157

11701158
@property
11711159
def intermediate_outputs(self) -> list[OutputParam]:
11721160
return [
1173-
OutputParam("latents"),
1174-
OutputParam("control_latents"),
1175-
OutputParam("velocity_mask"),
1176-
OutputParam("condition_latents"),
1177-
OutputParam("target_condition_indexes"),
1178-
OutputParam("current_conditional_frames"),
1161+
OutputParam("latents", type_hint=torch.Tensor, description="Noisy target latents for this chunk."),
1162+
OutputParam(
1163+
"velocity_mask",
1164+
type_hint=torch.Tensor,
1165+
description="Mask that zeroes the velocity on conditioned (clean) latent frames.",
1166+
),
1167+
OutputParam(
1168+
"condition_latents",
1169+
type_hint=torch.Tensor,
1170+
description="Clean target latents on the conditioned frames (the autoregressive seed).",
1171+
),
1172+
OutputParam(
1173+
"target_condition_indexes",
1174+
type_hint=list[int],
1175+
description="Latent-frame indexes fixed by the chunk's conditioning.",
1176+
),
11791177
]
11801178

11811179
@torch.no_grad()
11821180
def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState) -> PipelineState:
11831181
block_state = self.get_block_state(state)
11841182
device = components._execution_device
11851183
dtype = components.transformer.dtype
1186-
1187-
chunk_id = block_state.chunk_id
1188-
chunk_frames = block_state.chunk_frames
1189-
height = block_state.height
1190-
width = block_state.width
11911184
tcf = components.vae_scale_factor_temporal
11921185

1193-
# Slice this chunk's window out of the (padded) control maps and reflect-pad it up to a full chunk (repeat the
1194-
# last frame once too short to keep reflecting). control_frames is already in canonical hint order.
1195-
start_frame = chunk_id * block_state.stride
1196-
end_frame = min(start_frame + chunk_frames, block_state.total_frames)
1197-
chunk_controls = []
1198-
for frames in block_state.control_frames.values():
1199-
frames = frames[:, :, start_frame:end_frame]
1200-
while frames.shape[2] < chunk_frames:
1201-
pad_len = min(frames.shape[2] - 1, chunk_frames - frames.shape[2])
1202-
if pad_len <= 0:
1203-
pad_frame = frames[:, :, -1:].repeat(1, 1, chunk_frames - frames.shape[2], 1, 1)
1204-
frames = torch.cat([frames, pad_frame], dim=2)
1205-
break
1206-
frames = torch.cat([frames, frames.flip(dims=[2])[:, :, :pad_len]], dim=2)
1207-
chunk_controls.append(frames)
1208-
1209-
# Seed the target with conditioning frames (first chunk from the input video, later chunks from the
1210-
# previous chunk's tail), repeat-padding the remaining frames so the whole clip is well-defined.
1211-
target = torch.zeros(1, 3, chunk_frames, height, width, device=device, dtype=dtype)
1212-
current_conditional_frames = 0
1213-
if chunk_id == 0 and block_state.num_first_chunk_conditional_frames > 0 and block_state.video is not None:
1214-
input_frames = components.video_processor.preprocess_video(
1215-
block_state.video, height=height, width=width
1216-
).to(device=device, dtype=dtype)
1217-
current_conditional_frames = min(
1218-
block_state.num_first_chunk_conditional_frames, input_frames.shape[2], chunk_frames
1219-
)
1220-
if current_conditional_frames > 0:
1221-
target[:, :, :current_conditional_frames] = input_frames[:, :, :current_conditional_frames]
1222-
elif chunk_id > 0 and block_state.previous_output is not None:
1223-
current_conditional_frames = min(
1224-
block_state.num_conditional_frames, block_state.previous_output.shape[2], chunk_frames
1225-
)
1226-
if current_conditional_frames > 0:
1227-
target[:, :, :current_conditional_frames] = block_state.previous_output[
1228-
:, :, -current_conditional_frames:
1229-
].to(device=device, dtype=dtype)
1230-
if 0 < current_conditional_frames < chunk_frames:
1231-
fill = target[:, :, current_conditional_frames - 1 : current_conditional_frames]
1232-
target[:, :, current_conditional_frames:] = fill.expand(
1233-
-1, -1, chunk_frames - current_conditional_frames, -1, -1
1234-
)
1186+
target_x0 = block_state.x0_tokens_vision.to(device=device)
1187+
current_conditional_frames = block_state.current_conditional_frames
12351188

1236-
# Encode controls as clean latents and build the noisy target latents + conditioning mask.
1237-
block_state.control_latents = [components._encode_video(ctrl).contiguous().float() for ctrl in chunk_controls]
1238-
target_x0 = components._encode_video(target).contiguous().float()
1189+
# Build the noisy target latents + conditioning mask from the clean target latents.
12391190
latent_t = target_x0.shape[2]
12401191
condition_mask = torch.zeros((latent_t, 1, 1), device=device, dtype=dtype)
12411192
latent_condition_frames = 0
@@ -1247,7 +1198,6 @@ def __call__(self, components: Cosmos3OmniModularPipeline, state: PipelineState)
12471198
block_state.velocity_mask = 1.0 - condition_mask
12481199
block_state.condition_latents = condition_mask * target_x0
12491200
block_state.target_condition_indexes = list(range(latent_condition_frames))
1250-
block_state.current_conditional_frames = current_conditional_frames
12511201

12521202
self.set_block_state(state, block_state)
12531203
return components, state
@@ -1266,22 +1216,60 @@ def description(self) -> str:
12661216
@property
12671217
def inputs(self) -> list[InputParam]:
12681218
return [
1269-
InputParam(name="cond_text_segment", required=True),
1270-
InputParam(name="uncond_text_segment", required=True),
1271-
InputParam(name="control_latents", required=True),
1272-
InputParam(name="latents", required=True),
1273-
InputParam(name="target_condition_indexes", required=True),
1274-
InputParam(name="fps", type_hint=float, default=24.0),
1275-
InputParam(name="share_vision_temporal_positions", type_hint=bool, default=True),
1219+
InputParam(name="cond_text_segment", type_hint=dict, required=True, description="Conditional text segment."),
1220+
InputParam(
1221+
name="uncond_text_segment", type_hint=dict, required=True, description="Unconditional text segment."
1222+
),
1223+
InputParam(
1224+
name="control_latents",
1225+
type_hint=list[torch.Tensor],
1226+
required=True,
1227+
description="Clean control latents for this chunk, one per hint in canonical order.",
1228+
),
1229+
InputParam(
1230+
name="latents", type_hint=torch.Tensor, required=True, description="Noisy target latents for this chunk."
1231+
),
1232+
InputParam(
1233+
name="target_condition_indexes",
1234+
type_hint=list[int],
1235+
required=True,
1236+
description="Latent-frame indexes fixed by the chunk's conditioning.",
1237+
),
1238+
InputParam(name="fps", type_hint=float, default=24.0, description="Frame rate of the generated video."),
1239+
InputParam(
1240+
name="share_vision_temporal_positions",
1241+
type_hint=bool,
1242+
default=True,
1243+
description="Whether control and target items share vision temporal positions.",
1244+
),
12761245
]
12771246

12781247
@property
12791248
def intermediate_outputs(self) -> list[OutputParam]:
12801249
return [
1281-
OutputParam("cond_full_static"),
1282-
OutputParam("cond_no_control_static"),
1283-
OutputParam("uncond_full_static"),
1284-
OutputParam("num_noisy_vision_tokens"),
1250+
OutputParam(
1251+
"cond_full_static",
1252+
type_hint=dict,
1253+
kwargs_type="denoiser_input_fields",
1254+
description="Conditional [control..., target] transfer sequence carrying every control item.",
1255+
),
1256+
OutputParam(
1257+
"cond_no_control_static",
1258+
type_hint=dict,
1259+
kwargs_type="denoiser_input_fields",
1260+
description="Conditional [target] transfer sequence with the control items dropped.",
1261+
),
1262+
OutputParam(
1263+
"uncond_full_static",
1264+
type_hint=dict,
1265+
kwargs_type="denoiser_input_fields",
1266+
description="Unconditional [control..., target] transfer sequence for text CFG.",
1267+
),
1268+
OutputParam(
1269+
"num_noisy_vision_tokens",
1270+
type_hint=int,
1271+
description="Number of noisy target vision tokens denoised each step.",
1272+
),
12851273
]
12861274

12871275
@torch.no_grad()
@@ -1347,8 +1335,10 @@ def inputs(self) -> list[InputParam]:
13471335
@property
13481336
def intermediate_outputs(self) -> list[OutputParam]:
13491337
return [
1350-
OutputParam("timesteps"),
1351-
OutputParam("num_warmup_steps"),
1338+
OutputParam("timesteps", type_hint=torch.Tensor, description="Scheduler timesteps for this chunk."),
1339+
OutputParam(
1340+
"num_warmup_steps", type_hint=int, description="Number of scheduler warmup steps for this chunk."
1341+
),
13521342
]
13531343

13541344
@torch.no_grad()

src/diffusers/modular_pipelines/cosmos/decoders.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -134,17 +134,37 @@ def expected_components(self) -> list[ComponentSpec]:
134134
@property
135135
def inputs(self) -> list[InputParam]:
136136
return [
137-
InputParam(name="latents", required=True),
138-
InputParam(name="chunk_id", type_hint=int, default=0),
139-
InputParam(name="current_conditional_frames", required=True),
140-
InputParam(name="output_chunks", required=True),
137+
InputParam(
138+
name="latents", type_hint=torch.Tensor, required=True, description="Denoised target latents for this chunk."
139+
),
140+
InputParam(name="chunk_id", type_hint=int, default=0, description="Index of the current chunk."),
141+
InputParam(
142+
name="current_conditional_frames",
143+
type_hint=int,
144+
required=True,
145+
description="Number of pixel frames this chunk reused from the previous chunk.",
146+
),
147+
InputParam(
148+
name="output_chunks",
149+
type_hint=list[torch.Tensor],
150+
required=True,
151+
description="Decoded pixel chunks accumulated so far.",
152+
),
141153
]
142154

143155
@property
144156
def intermediate_outputs(self) -> list[OutputParam]:
145157
return [
146-
OutputParam("previous_output"),
147-
OutputParam("output_chunks"),
158+
OutputParam(
159+
"previous_output",
160+
type_hint=torch.Tensor,
161+
description="Decoded pixels of this chunk, used to seed the next chunk.",
162+
),
163+
OutputParam(
164+
"output_chunks",
165+
type_hint=list[torch.Tensor],
166+
description="Decoded pixel chunks accumulated so far (with this chunk appended).",
167+
),
148168
]
149169

150170
@torch.no_grad()
@@ -190,17 +210,24 @@ def expected_components(self) -> list[ComponentSpec]:
190210
@property
191211
def inputs(self) -> list[InputParam]:
192212
return [
193-
InputParam(name="output_chunks", required=True),
194-
InputParam(name="total_frames", required=True),
213+
InputParam(
214+
name="output_chunks",
215+
type_hint=list[torch.Tensor],
216+
required=True,
217+
description="Decoded pixel chunks to stitch together.",
218+
),
219+
InputParam(
220+
name="total_frames", type_hint=int, required=True, description="Total number of output frames to keep."
221+
),
195222
InputParam.template("output_type", default="pil"),
196223
]
197224

198225
@property
199226
def intermediate_outputs(self) -> list[OutputParam]:
200227
return [
201-
OutputParam("videos"),
202-
OutputParam("sound"),
203-
OutputParam("sampling_rate"),
228+
OutputParam("videos", description="The generated transfer video."),
229+
OutputParam("sound", description="Always None for transfer (no audio)."),
230+
OutputParam("sampling_rate", description="Always None for transfer (no audio)."),
204231
]
205232

206233
@torch.no_grad()

0 commit comments

Comments
 (0)