fix(diffusion): reuse warm HF cache instead of re-downloading models - #2747
Merged
Conversation
The diffusers bridge and preprocessing processors passed bare repo ids to diffusers' from_pretrained, which resolves in online mode by default and re-validates (and can re-download) a warm HF_HOME over the network on every run. The transformers bridge avoids this by pre-resolving the repo to a local snapshot dir; port the same discipline to the diffusion path. - Add resolve_diffusion_model_dir() helper: returns local paths unchanged, downloads once only on a cold cache when online, then resolves the local snapshot with local_files_only=True so from_pretrained does no network I/O. - Use it in NeMoAutoDiffusionPipeline.from_pretrained and from_config. - Use it in the flux, flux2, hunyuan, qwen_image, and wan processors. - Add unit tests for the helper. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Pranav Prashant Thombre <pthombre@nvidia.com>
Contributor
Author
|
/ok to test f6a139a |
Contributor
Author
|
/claude review |
| # Online: fetch once (cold cache), then resolve the local dir without revalidation. | ||
| assert mock_sd.call_count == 2 | ||
| assert mock_sd.call_args_list[0].args == ("some/repo-id",) | ||
| assert mock_sd.call_args_list[1].kwargs == {"local_files_only": True} |
Contributor
There was a problem hiding this comment.
nit: This assertion verifies the kwargs of the second snapshot_download call but doesn't check that "some/repo-id" was also passed as the positional arg. If someone accidentally dropped the model_id argument from the second call, this test would still pass.
Suggested change
| assert mock_sd.call_args_list[1].kwargs == {"local_files_only": True} | |
| assert mock_sd.call_args_list[1] == (("some/repo-id",), {"local_files_only": True}) |
…sts offline The cache-resolution helper imported snapshot_download inside the function body, so test_hf_cache.py's patch of the module-level name failed at setup. Bind it at module scope (guarded by the optional-dep check) so it is patchable, with identical runtime behavior. The auto_diffusion_pipeline from_pretrained/from_config tests mock DiffusionPipeline but not the newly added resolve_diffusion_model_dir call, so they reached the Hub for fake repo ids (401). Add an autouse fixture that resolves repo ids to a passthrough, keeping the unit tests offline. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Pranav Prashant Thombre <pthombre@nvidia.com>
Contributor
Author
|
/ok to test a75a683 |
akoumpa
approved these changes
Jun 24, 2026
akoumpa
pushed a commit
that referenced
this pull request
Jun 24, 2026
…dels (2747)` into `r0.5.0` (#2754) fix(diffusion): reuse warm HF cache instead of re-downloading models (#2747) * fix(diffusion): reuse warm HF cache instead of re-downloading models The diffusers bridge and preprocessing processors passed bare repo ids to diffusers' from_pretrained, which resolves in online mode by default and re-validates (and can re-download) a warm HF_HOME over the network on every run. The transformers bridge avoids this by pre-resolving the repo to a local snapshot dir; port the same discipline to the diffusion path. - Add resolve_diffusion_model_dir() helper: returns local paths unchanged, downloads once only on a cold cache when online, then resolves the local snapshot with local_files_only=True so from_pretrained does no network I/O. - Use it in NeMoAutoDiffusionPipeline.from_pretrained and from_config. - Use it in the flux, flux2, hunyuan, qwen_image, and wan processors. - Add unit tests for the helper. * fix(diffusion): make HF cache resolver patchable and keep diffuser tests offline The cache-resolution helper imported snapshot_download inside the function body, so test_hf_cache.py's patch of the module-level name failed at setup. Bind it at module scope (guarded by the optional-dep check) so it is patchable, with identical runtime behavior. The auto_diffusion_pipeline from_pretrained/from_config tests mock DiffusionPipeline but not the newly added resolve_diffusion_model_dir call, so they reached the Hub for fake repo ids (401). Add an autouse fixture that resolves repo ids to a passthrough, keeping the unit tests offline. --------- Signed-off-by: Pranav Prashant Thombre <pthombre@nvidia.com> Signed-off-by: NeMo Bot <nemo-bot@nvidia.com> Co-authored-by: Pranav Thombre <pthombre@nvidia.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do ?
Stops the diffusion recipes (and their preprocessing step) from re-fetching models from the HuggingFace Hub on every run even when the model is already cached in
HF_HOME.The issue
The diffusers bridge and the preprocessing processors pass a bare repo id straight to diffusers'
from_pretrained:NeMoAutoDiffusionPipeline.from_pretrained/from_config→DiffusionPipeline.from_pretrained(model_id, ...)tools/diffusion/processors/{flux,flux2,hunyuan,qwen_image,wan}.py→<Pipeline>.from_pretrained(model_name, ...)With a bare repo id,
huggingface_hubresolves in online mode by default: for every file it issues a network request to the Hub to revalidate the commit hash / ETag before deciding whether to reuse the cache. So even a warmHF_HOMEis re-validated over the network, and any ETag drift, partial cache, or unreachable-but-not-offline state turns into a fresh download. A diffusion pipeline loads ~6 sub-components (VAE, two text encoders, two tokenizers, transformer) in one call, which multiplies the round-trips.The behavior depended entirely on the ambient
HF_HUB_OFFLINEenv var being set in every stage/subprocess. The diffusion launcher never sets it itself — it only relies on the outer CI template — so any context where it isn't propagated (local dev, a runner without it, a subprocess that drops it) re-downloads.The transformers bridge does not have this problem: it pre-resolves the repo to a local snapshot directory in code (
_resolve_model_dir→snapshot_download(..., local_files_only=True)innemo_automodel/_transformers/model_init.py, plus an explicitHF_HUB_OFFLINEshort-circuit) and hands that local path to HF — which then performs zero network I/O. The diffusers bridge is a separate integration layer and never went through those helpers, so the cache discipline was simply missing on the diffusion path.The fix
Port the same discipline to the diffusion path. Once HF
from_pretrainedreceives a local directory, it does no network I/O regardless of env-var propagation, which is what removes the per-run re-download.Changelog
nemo_automodel/_diffusers/_hf_cache.pywithresolve_diffusion_model_dir(): returns local paths unchanged; for repo ids it downloads the snapshot once only when the cache is cold and the process is online (HF_HUB_OFFLINEunset), then resolves the local snapshot withlocal_files_only=Trueso the subsequentfrom_pretrainedperforms no network I/O. Guarded withsafe_import('huggingface_hub')and passes through unchanged when the lib is unavailable.NeMoAutoDiffusionPipeline.from_pretrained(finetune) andfrom_config(pretrain).flux,flux2,hunyuan,qwen_image, andwanpreprocessing processors (wanresolves once and reuses the dir across its threesubfolder=loads).tests/unit_tests/_diffusers/test_hf_cache.pycovering local-path passthrough, offline (single cache-only call), online (download-then-resolve), and hub-unavailable passthrough.HF_HUB_OFFLINEBefore your PR is "Ready for review"
Pre checks:
Additional Information
snapshot_download('modal-labs/dissolve')indiffusion_finetune_launcher.shhas the same online-by-default gap, but only runs in CI whereHF_HUB_OFFLINEis already set. Can harden separately if desired.🤖 Generated with Claude Code