Skip to content

Add Echo modular pipeline - #14696

Open
Gelercatty wants to merge 3 commits into
huggingface:mainfrom
Echo-Team-Joy-Future-Academy-JD:echo
Open

Add Echo modular pipeline#14696
Gelercatty wants to merge 3 commits into
huggingface:mainfrom
Echo-Team-Joy-Future-Academy-JD:echo

Conversation

@Gelercatty

@Gelercatty Gelercatty commented Sep 3, 2026

Copy link
Copy Markdown

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:

  • adds the standalone diffusers.modular_pipelines.echo package and the public EchoModularPipeline / EchoBlocks APIs
  • reuses existing lower-level Diffusers components without exposing LTX in Echo's public API
  • keeps checkpoint conversion in scripts/ instead of adding an original-checkpoint runtime pipeline
  • supports an optional first frame and up to seven paired image/audio memory slots
  • supports variable-length memory audio and crops audio longer than 9.62 seconds to its highest-response window
  • implements Echo's positive-only conditioning and stochastic 8-step DMD denoising workflow
  • adds mixed-precision-safe audio decoding, documentation, and tests using a public tiny fixture

Official resources:

Closes #13909
Supersedes #13910

Testing

  • make style
  • make fix-copies
  • make quality
  • PYTHONPATH=src python -m pytest -q tests/modular_pipelines/echo/test_modular_pipeline_echo.py
    • 31 passed, 6 skipped

AI-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-pipe fixture on CPU. A full production-checkpoint conversion and end-to-end GPU inference run was not repeated in this final pass.

Before submitting

Who can review?

@asomoza @yiyixuxu

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests modular-pipelines utils size/L PR with diff > 200 LOC labels Sep 3, 2026
@github-project-automation github-project-automation Bot moved this to In Progress in Diffusers Roadmap Sep 4, 2026
@HuggingFaceDocBuilderDev

Copy link
Copy Markdown

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 yiyixuxu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I left some feedbacks

Comment thread src/diffusers/modular_pipelines/echo/denoise.py
Comment thread src/diffusers/modular_pipelines/echo/echo_encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/echo_encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/echo_encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/echo_encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/echo_encoders.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/encoders.py
Comment thread src/diffusers/modular_pipelines/echo/echo_before_denoise.py Outdated
Comment thread src/diffusers/modular_pipelines/echo/modular_blocks_echo.py Outdated
@Gelercatty

Gelercatty commented Sep 7, 2026

Copy link
Copy Markdown
Author

@yiyixuxu Thanks for the review — all feedback is addressed in 64f423a and 679db3f.

  • removed the echo_ prefix from leaf source filenames
  • changed module-level helpers to explicit component arguments
  • reduced text-encoder outputs and derived batch size/dtype downstream
  • moved all assembled blocks to modular_blocks_echo.py
  • split VAE-only encoding from transformer-specific packing/RoPE preparation
  • removed VAE requirements from before-denoise blocks
  • flattened EchoBlocks to text_encoder, vae_encoder, denoise, decode
  • inlined the single-use _as_list helper
  • added a behavior test that runs the VAE encoder standalone without a transformer

Validation:

  • make fix-copies
  • Echo-scoped Ruff check and format check
  • forward/call docstring check
  • AI contribution check
  • pytest -q tests/modular_pipelines/echo/test_modular_pipeline_echo.py: 31 passed, 6 skipped

Self-review found no remaining blocking issue in this update. The repository-wide make quality is currently blocked by 52 pre-existing formatting differences outside the Echo files; all Echo-scoped quality checks pass.

Could you please take another look?

@yiyixuxu yiyixuxu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +172 to +173
latent_height = block_state.height // components.vae_spatial_compression_ratio
latent_width = block_state.width // components.vae_spatial_compression_ratio

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines +118 to +119
InputParam.template("height", default=512),
InputParam.template("width", default=704),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
InputParam.template("height", default=512),
InputParam.template("width", default=704),

I think we can derive from latent shape

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.



# auto_docstring
class EchoDenoiseLoopStep(LoopSequentialPipelineBlocks):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh this is typically inside denoise.py (even though it is a multi-block) - sorry if I've confused you earlier

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(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can move the unpack to core denoise blocks (see #14730)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation fixes-issue modular-pipelines size/L PR with diff > 200 LOC tests utils

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[New Pipeline/Model] Add JoyAI-Echo multi-shot audio-video generation pipeline

3 participants