Add Echo modular pipeline - #14696
Gelercatty wants to merge 5 commits into
Conversation
|
The docs for this PR live here. All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update. |
yiyixuxu
left a comment
There was a problem hiding this comment.
thanks, I left some feedbacks
|
@yiyixuxu Thanks for the review — all feedback is addressed in 64f423a and 679db3f.
Validation:
Self-review found no remaining blocking issue in this update. The repository-wide Could you please take another look? |
yiyixuxu
left a comment
There was a problem hiding this comment.
thanks!
i left some more comments
| mel = torch.log(torch.clamp(mel_transform(waveform), min=1e-5)).permute(0, 2, 1).unsqueeze(0) | ||
|
|
||
| latents = audio_vae.encode(mel.to(audio_vae.dtype)).latent_dist.mode() | ||
| latents = _pack_audio_latents(latents) |
There was a problem hiding this comment.
is it possible to make the audio latent and video latent consistent? i.e. both normalized in encoder and packed in before_denoise? see a bit more write up here #14730
There was a problem hiding this comment.
Done in 9d182ea. Both encoders now return normalized, unpacked VAE tensors. Audio statistics are reshaped over the channel/mel axes before normalization, and audio packing now lives in before_denoise.py, matching video. Added FP32/BF16 packed-statistics equivalence tests and standalone audio/video encoder coverage.
| latent_height = block_state.height // components.vae_spatial_compression_ratio | ||
| latent_width = block_state.width // components.vae_spatial_compression_ratio |
There was a problem hiding this comment.
| latent_height = block_state.height // components.vae_spatial_compression_ratio | |
| latent_width = block_state.width // components.vae_spatial_compression_ratio | |
| _, _, latent_height, latent_width = memory_video_latents[0].shape |
There was a problem hiding this comment.
Done in 9d182ea. The memory geometry is now derived from memory_video_latents[0].shape[-2:]. Echo's video VAE tensors are 5D (B, C, F, H, W), so this reads the spatial dimensions directly. Added a standalone preparation test with non-default rectangular latent geometry.
| InputParam.template("height", default=512), | ||
| InputParam.template("width", default=704), |
There was a problem hiding this comment.
| InputParam.template("height", default=512), | |
| InputParam.template("width", default=704), |
I think we can derive from latent shape
There was a problem hiding this comment.
Done in 9d182ea. Removed height and width from EchoPrepareConditioningStep. It derives memory geometry from the VAE latent tensors and runs standalone with only the transformer loaded.
| def __call__(self, components, block_state: BlockState, i: int, sigma: float): | ||
| batch_size = block_state.latents.shape[0] | ||
| transformer_dtype = components.transformer.dtype | ||
| video_context = self._expand_batch(block_state.connector_prompt_embeds, batch_size).to(transformer_dtype) |
There was a problem hiding this comment.
can we have a dedicated step to expand all the inputs? like https://github.com/huggingface/diffusers/blob/main/src/diffusers/modular_pipelines/krea2/before_denoise.py#L46
There was a problem hiding this comment.
Done in 9d182ea. Added a dedicated EchoInputsStep before latent preparation/the denoising loop. It expands both text branches, the attention mask, first-frame/memory tokens, and memory RoPE coordinates once. Removed the two old expansion helpers. Tests cover shared/per-prompt conditions, cached-input reuse without mutation, and two prompts with two videos each.
|
|
||
|
|
||
| # auto_docstring | ||
| class EchoDenoiseLoopStep(LoopSequentialPipelineBlocks): |
There was a problem hiding this comment.
ohh this is typically inside denoise.py (even though it is a multi-block) - sorry if I've confused you earlier
There was a problem hiding this comment.
Done in 9d182ea. Thanks for clarifying! Moved EchoDenoiseLoopStep and its loop block dictionary back into denoise.py. The top-level core-denoise sequence remains assembled in modular_blocks_echo.py.
| latent_num_frames = (block_state.num_frames - 1) // components.vae_temporal_compression_ratio + 1 | ||
| latent_height = block_state.height // components.vae_spatial_compression_ratio | ||
| latent_width = block_state.width // components.vae_spatial_compression_ratio | ||
| latents = _unpack_latents( |
There was a problem hiding this comment.
I think we can move the unpack to core denoise blocks (see #14730)
There was a problem hiding this comment.
Done in 9d182ea. Added EchoUnpackLatentsStep at the end of the core-denoise group for both video and audio. The decoders now accept normalized VAE-form tensors, require no geometry inputs, and only denormalize/decode. Initial noise inputs use the same unpacked form. Added zero-sigma round-trip and standalone-decoder tests, and documented the latent boundary.
|
@yiyixuxu Thanks for the follow-up review! All six suggestions are addressed in 9d182ea, with a reply on each thread.
Validation:
Self-review is complete with no blocking issues found. These checks used the tiny CPU fixture; full production-checkpoint/GPU validation was not rerun for this revision. Could you please take another look? Thanks! |
|
@sayakpaul can you do a review and help merge in? |
| [Echo](https://github.com/jd-opensource/JoyAI-Echo) is a long-video generation model. It adds an optional clean first | ||
| frame, ordered image/audio memory slots, and a stochastic few-step Distribution Matching Distillation (DMD) sampler. |
There was a problem hiding this comment.
| [Echo](https://github.com/jd-opensource/JoyAI-Echo) is a long-video generation model. It adds an optional clean first | |
| frame, ordered image/audio memory slots, and a stochastic few-step Distribution Matching Distillation (DMD) sampler. | |
| [Echo](https://github.com/jd-opensource/JoyAI-Echo) is a long-video generation model. It supports an optional clean first | |
| frame, ordered image/audio memory slots, and a stochastic few-step Distribution Matching Distillation (DMD) sampler. |
| Echo is implemented as a Modular Pipeline so its text encoding, memory conditioning, stochastic DMD denoising, and | ||
| decoding blocks can be run as a complete workflow or composed independently. |
There was a problem hiding this comment.
| Echo is implemented as a Modular Pipeline so its text encoding, memory conditioning, stochastic DMD denoising, and | |
| decoding blocks can be run as a complete workflow or composed independently. | |
| Echo is implemented as a [`ModularPipeline`] so its text encoding, memory conditioning, stochastic DMD denoising, and | |
| decoding blocks can be run as a complete workflow or composed independently. |
| Modular workflows can cache the VAE encoder's normalized, unpacked tensors: video latents have shape | ||
| `(batch, channels, frames, height, width)` and audio latents have shape `(batch, channels, time, mel_bins)`. |
There was a problem hiding this comment.
| Modular workflows can cache the VAE encoder's normalized, unpacked tensors: video latents have shape | |
| `(batch, channels, frames, height, width)` and audio latents have shape `(batch, channels, time, mel_bins)`. | |
| Modular workflows can cache the VAE encoder's normalized, unpacked tensors. Video latents have shape | |
| `(batch, channels, frames, height, width)` and audio latents have shape `(batch, channels, time, mel_bins)`. |
| The core `denoise` block packs these tensors, expands conditioning for `num_videos_per_prompt`, and unpacks its | ||
| denoised outputs back to the same VAE form. Pass initial `latents` and `audio_latents` in this unpacked form as well. | ||
| Decoders accept normalized VAE tensors without separate geometry arguments and denormalize immediately before | ||
| decoding. `output=["latents", "audio_latents"]` returns normalized VAE tensors; `output_type="latent"` with | ||
| `output=["videos", "audio"]` returns the denormalized VAE tensors without decoding. |
There was a problem hiding this comment.
| The core `denoise` block packs these tensors, expands conditioning for `num_videos_per_prompt`, and unpacks its | |
| denoised outputs back to the same VAE form. Pass initial `latents` and `audio_latents` in this unpacked form as well. | |
| Decoders accept normalized VAE tensors without separate geometry arguments and denormalize immediately before | |
| decoding. `output=["latents", "audio_latents"]` returns normalized VAE tensors; `output_type="latent"` with | |
| `output=["videos", "audio"]` returns the denormalized VAE tensors without decoding. | |
| The core `denoise` block packs those tensors, expands conditioning for `num_videos_per_prompt`, and unpacks | |
| denoised outputs back to the same VAE form. Pass initial `latents` and `audio_latents` in that unpacked form too. | |
| Decoders take normalized VAE tensors and denormalize immediately before | |
| decoding. `output=["latents", "audio_latents"]` returns normalized VAE tensors. With `output_type="latent"`and | |
| `output=["videos", "audio"]`, you get the denormalized VAE tensors without decoding. |
| ) | ||
| ``` | ||
|
|
||
| The default DMD sigma schedule is the released eight-step schedule. It predicts x0 at every step and re-noises with |
There was a problem hiding this comment.
| The default DMD sigma schedule is the released eight-step schedule. It predicts x0 at every step and re-noises with | |
| The default DMD sigma schedule is the released eight-step schedule. It predicts `x0` at every step and re-noises with |
What does this PR do?
Adds official Diffusers Modular Pipeline support for Echo, a long-video model that generates synchronized video and audio with cross-shot memory.
This supersedes #13910 with a modular implementation that incorporates the earlier review feedback:
diffusers.modular_pipelines.echopackage and the publicEchoModularPipeline/EchoBlocksAPIsscripts/instead of adding an original-checkpoint runtime pipelineOfficial resources:
Closes #13909
Supersedes #13910
Testing
make stylemake fix-copiesmake qualityPYTHONPATH=src python -m pytest -q tests/modular_pipelines/echo/test_modular_pipeline_echo.pyAI-assisted self-review
Codex was used to help review, refactor, test, and prepare this contribution. The final diff was reviewed using the repository's model-integration and self-review guidance.
The final self-review found no blocking correctness issues. Echo has its own public package and naming throughout; existing lower-level Diffusers component types are reused only as implementation dependencies. Memory-slot count, short-audio handling, variable audio lengths, response-aware 9.62-second cropping, deterministic DMD re-noising, mixed-precision decoding, loading, save/load, workflow, and memory behavior are covered by the test suite.
Validation scope: the final pass used the public
Echo-Team/tiny-echo-modular-pipefixture on CPU. A full production-checkpoint conversion and end-to-end GPU inference run was not repeated in this final pass.Before submitting
self-reviewskill on the diff?Who can review?
@asomoza @yiyixuxu