Skip to content

fix(model-manager): filter repo-id Diffusers files by index - #9912

Open
lorenzozanee wants to merge 5 commits into
invoke-ai:mainfrom
lorenzozanee:fix/classic-diffusers-repo-id-filter
Open

lorenzozanee wants to merge 5 commits into
invoke-ai:mainfrom
lorenzozanee:fix/classic-diffusers-repo-id-filter

Conversation

@lorenzozanee

@lorenzozanee lorenzozanee commented Oct 18, 2026 •

Copy link
Copy Markdown

Summary

Repo-id Diffusers installs currently skip classic model_index.json filtering because metadata paths include the repository root. This can include files outside the pipeline component folders. Normalize index lookup and component membership relative to the repository root and selected model subfolder, while preserving the original paths.

Related Issues / Discussions

Fixes #9901

QA Instructions

  • uv run --no-sync pytest -q tests/backend/model_manager/test_huggingface_metadata_download_urls.py tests/backend/model_manager/test_huggingface_metadata_fetch_timeout.py tests/backend/model_manager/util/test_hf_model_select.py: 22 passed.
  • Ruff check and format check passed for both changed files; git diff --check passed.

Review

No material review findings remain. Regression coverage includes repository-root and selected-subfolder paths. Hosted CI and other remote repository layouts have not been verified.

Checklist

  • The PR has a short but descriptive title, suitable for a changelog
  • Meaningful regression coverage added / updated where needed; obsolete tests/code removed
  • Persisted-state and API changes include required migrations / compatibility validation
  • Relevant performance/efficiency opportunities considered; material claims have evidence
  • Material review findings resolved and relevant checks rerun
  • Documentation added / updated (if applicable)
  • Updated What's New copy (if doing a release after this PR)

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T06:28:12.428944Z 7db0926 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added python PRs that change python files backend PRs that change backend files python-tests PRs that change python tests labels Oct 18, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7db09264c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

submodels = resp.json()
paths = [Path(subfolder or "", x) for x in paths if Path(x).parent.as_posix() in submodels]
paths.insert(0, Path(f"{prefix}model_index.json"))
paths = [x for x in paths if x.relative_to(model_root).parent.as_posix() in submodels]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve the modular pipeline index

When installing MiniMaxAI/MiniMax-H3 by repo ID, filter_files() selects both root indexes (tests/backend/model_manager/util/test_hf_model_select.py:419-420), but modular_model_index.json has relative parent . and is removed by this predicate before only the classic index is reinserted. Main_Diffusers_MiniMaxH3_Config.from_model_on_disk() requires the modular index to identify H3 (invokeai/backend/model_manager/configs/main.py:1757-1763), so a normal full-repo install is left unrecognizable; retain this required root pipeline index while filtering unrelated root artifacts.

AGENTS.md reference: invokeai/backend/AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

if Path(f"{prefix}model_index.json") in paths:
model_root = repo_root / prefix
model_index_path = model_root / "model_index.json"
if model_index_path in paths:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Fetch the model index from the selected revision

When a repository exposes an actual variant revision (for example fp16) whose index differs from its default branch, this newly reachable condition filters the variant's files with the wrong index: HuggingFaceMetadataFetch.from_id() builds each file URL with revision=variant (metadata/fetch/huggingface.py:123-130), while the request below reconstructs model_index.json without a revision. Required variant-only components can then be silently omitted; request the URL already stored for model_index_path, which also records whether metadata fetching fell back to the default revision.

AGENTS.md reference: invokeai/backend/AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

@shivenaggarwal

Copy link
Copy Markdown

Now that the index check is reachable, gated and private repos go through it too. The model_index.json request is anonymous, so for a logged-out user (or one who hasn't accepted the license) installing e.g. FLUX.2-dev or SD3.5 it gets a 401/403, and resp.raise_for_status() raises out of download_urls() when the install starts, before the file downloads can mark the job as unauthorized.

The same call runs during _restore_incomplete_installs(), where anything other than ModelMetadataUnavailableError marks the job as errored and removes its install tmpdir. So a 401/403, or a transient 429/5xx, on restart would also throw away the partial downloads.

Something like this keeps both cases working:

if resp.status_code in (401, 403):
    # gated or private repo: keep the unfiltered selection and let the file downloads report access
    return [x for x in self.files if x.path in paths]
if resp.status_code == 429 or resp.status_code >= 500:
    raise ModelMetadataUnavailableError(f"Could not fetch model index for '{self.id}': HTTP {resp.status_code}")
resp.raise_for_status()

@joshistoast joshistoast self-assigned this Oct 19, 2026
# to the model (only for single subfolder case)
if Path(f"{prefix}model_index.json") in paths:
model_root = repo_root / prefix
model_index_path = model_root / "model_index.json"

@joshistoast joshistoast Oct 19, 2026 •

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.

[P1] Authenticate the newly enabled model-index request

Recognizing repository-prefixed paths now makes normal repo-id installs execute the index GET below, which uses a plain requests.Session without Hugging Face credentials. HuggingFaceMetadataFetch.from_id() can successfully retrieve a private repository through the authenticated HfApi, and the installer supplies the saved token to the download queue, but neither credential path reaches this request. Consequently, an otherwise authorized private or gated Diffusers install now fails with HTTP 401/403 before downloads start; the base revision skipped this request for these paths. Fetch the index with the same authentication context as the repository/download operations.

@Pfannkuchensack Pfannkuchensack left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Requesting changes. The index filter itself is right, but now that this branch runs for real installs, the gated starter models stop installing.

I reviewed against current main (v7). The PR merges cleanly there. Nothing upstream has touched metadata_base.py or select_hf_files.py since your base, so #9901 is still live on main.

Verified: I compared the old and new file selection for the 30 plain repo-id starter repos that have a root model_index.json. This used metadata only, with the default and fp16 variants.

  • 27 repos are unchanged.
  • The SDXL refiner drops the unlisted vae_1_0/, and SD3.5 L/M drop their root example-workflow JSONs. Both drops are intended.
  • No standard pipeline loses a component config, tokenizer, scheduler file or shard index.

With the PR merged into main, your tests, test_hf_model_select.py, test_huggingface_metadata_fetch_timeout.py and tests/app/services/model_install/ pass, and ruff is clean.

Before merge

  1. The points raised earlier are still open. The head only has main merges since 7db0926.
    • The model_index.json GET is anonymous. The production installer doesn't inject a session, so download_urls() falls back to a plain Session().
      • The anonymous request gets a 401 for eight starter repos: stabilityai/stable-diffusion-3.5-large, stabilityai/stable-diffusion-3.5-medium, black-forest-labs/FLUX.2-dev, black-forest-labs/FLUX.2-klein-9B, ideogram-ai/ideogram-4-fp8, ideogram-ai/ideogram-4-nf4, krea/Krea-2-Raw and krea/Krea-2-Turbo.
      • The install then raises HTTPError before any file is queued, also for users who are logged in to HF.
      • On restart the same error errors out a restored job and removes its partial download.
      • test_model_index_nontransient_http_error_is_not_reclassified currently pins that raise, so it needs to change with the fix.
    • A full MiniMaxAI/MiniMax-H3 install loses modular_model_index.json, so the model no longer identifies. I confirmed this on the live file list. Skipping its FL2VA/ and Ref2VA/ sub-pipelines is a nice side effect, though.
    • The index comes from main even when the files were listed from a variant revision.

Should fix

  1. Nested subfolders on Windows build a broken index URL (inline).
  2. Tests: please cover the gated (401/403) path, the kept modular index and the variant revision. The older download_urls tests in test_huggingface_metadata_fetch_timeout.py still use unprefixed paths. Moving them to Path(<repo-name>, ...) like your new tests would let repo_root drop its fallback (inline).

model_root = repo_root / prefix
model_index_path = model_root / "model_index.json"
if model_index_path in paths:
url = hf_hub_url(self.id, filename="model_index.json", subfolder=str(subfolder) if subfolder else None)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

On Windows, str(subfolder) uses backslashes for a nested subfolder. For example, hf_hub_url(..., subfolder=str(Path("FL2VA/sub"))) gives .../resolve/main/FL2VA%5Csub/model_index.json. That 404s, and the 404 now raises out of the install.

The line predates this PR, but this PR is what makes it reachable. subfolder.as_posix() fixes it.


paths = filter_files([x.path for x in self.files], variant, subfolder, subfolders) # all files in the model

repo_root = Path(self.name) if paths and paths[0].parts[0] == self.name else Path()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: In production every path starts with the repo name, because from_id() builds Path(name, rfilename). So the Path() fallback only serves the older unprefixed tests.

If the two ever disagree, the filter silently switches itself off again, which is exactly #9901. I'd use Path(self.name) unconditionally and move those tests to prefixed paths.

This branch has not been deployed

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

Labels

backend PRs that change backend files python PRs that change python files python-tests PRs that change python tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug]: Diffusers repo-id installs never apply the model_index.json component filter

4 participants