From 237d07b035b4accb3b504743a71007f80d0d431a Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Wed, 1 Jul 2026 10:16:04 +0000 Subject: [PATCH] Studio Images: clearer error for an unsupported diffusion model When a repo id resolves to no diffusion family the load raised 'Could not infer a diffusion family... Pass family_override (z-image)', which points at an unrelated family and doesn't say what is supported. Replace it with a message that lists the supported families (from a new supported_family_names helper) and notes that video models and image models whose diffusers transformer has no single-file loader are not supported. Applies to both the diffusers and native sd.cpp load paths. Also refreshes two stale family-registry comments that still called FLUX.2-dev omitted. --- studio/backend/core/inference/diffusion.py | 6 +++++- .../backend/core/inference/diffusion_families.py | 16 +++++++++++----- studio/backend/core/inference/sd_cpp_backend.py | 7 ++++++- studio/backend/tests/test_diffusion_backend.py | 11 +++++++++++ 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/studio/backend/core/inference/diffusion.py b/studio/backend/core/inference/diffusion.py index 9a29b3f797..405c2a81f2 100644 --- a/studio/backend/core/inference/diffusion.py +++ b/studio/backend/core/inference/diffusion.py @@ -33,6 +33,7 @@ from .diffusion_families import ( detect_family, resolve_base_repo, resolve_local_gguf_child, + supported_family_names, ) from .diffusion_device import ( DiffusionDeviceTarget, @@ -399,7 +400,10 @@ class DiffusionBackend: fam = detect_family(repo_id, family_override) if fam is None: raise ValueError( - f"Could not infer a diffusion family for '{repo_id}'. Pass family_override (z-image)." + f"'{repo_id}' is not a supported diffusion image model. Supported families: " + f"{', '.join(supported_family_names())}. If this is a variant of one of them, " + f"pass family_override with that family name. (Video models and image models " + f"whose diffusers transformer has no single-file loader are not supported.)" ) # Non-GGUF loads (a single-file safetensors transformer, or a full pipeline) # are gated to the unsloth org or a local path -- they fetch + deserialise diff --git a/studio/backend/core/inference/diffusion_families.py b/studio/backend/core/inference/diffusion_families.py index c342118b10..7294e4186b 100644 --- a/studio/backend/core/inference/diffusion_families.py +++ b/studio/backend/core/inference/diffusion_families.py @@ -95,9 +95,9 @@ class DiffusionFamily: # Keyed by architecture, not per model variant: a checkpoint's specific base repo # is read from its HF base_model tag at load time, so one entry covers Turbo/full, # schnell/dev, etc. base_repo here is only a fallback. Only archs whose diffusers -# transformer supports from_single_file load here (ERNIE-Image does not, yet). -# FLUX.2-dev and FLUX.2-klein-9B are left out only because their base diffusers -# repos are gated; the open klein-4B base stands in for the klein family below. +# transformer supports from_single_file load here (ERNIE-Image does not, yet; LTX +# video models are out of scope). FLUX.2-klein-9B shares the klein family (its base +# repo is resolved per-variant), and FLUX.2-dev has its own family below. _FAMILIES: tuple[DiffusionFamily, ...] = ( DiffusionFamily( name = "flux.1", @@ -114,8 +114,8 @@ _FAMILIES: tuple[DiffusionFamily, ...] = ( ), ), # FLUX.2-klein is a distinct pipeline (Flux2KleinPipeline) with a Qwen3 text - # encoder, not the Mistral-based Flux2Pipeline; it must precede a generic - # flux match. The base Flux2Pipeline (FLUX.2-dev) is gated, so it's omitted. + # encoder, not the Mistral-based Flux2Pipeline; it must precede a generic flux + # match. The Mistral-based Flux2Pipeline is the separate flux.2-dev family below. DiffusionFamily( name = "flux.2-klein", pipeline_class = "Flux2KleinPipeline", @@ -284,6 +284,12 @@ def detect_family(repo_id: str, override: Optional[str] = None) -> Optional[Diff return None +def supported_family_names() -> tuple[str, ...]: + """Family names accepted as ``family_override`` and shown in the unknown-model + error. Kept in registry order so the message lists what the backend can load.""" + return tuple(fam.name for fam in _FAMILIES) + + def resolve_base_repo(fam: DiffusionFamily, base_repo: Optional[str]) -> str: """The companion diffusers repo: caller-supplied if given, else the family fallback.""" base = (base_repo or "").strip() diff --git a/studio/backend/core/inference/sd_cpp_backend.py b/studio/backend/core/inference/sd_cpp_backend.py index 0905779787..17f07a619b 100644 --- a/studio/backend/core/inference/sd_cpp_backend.py +++ b/studio/backend/core/inference/sd_cpp_backend.py @@ -40,6 +40,7 @@ from core.inference.diffusion_families import ( family_sd_cpp_supported, resolve_base_repo, resolve_local_gguf_child, + supported_family_names, ) from core.inference.diffusion_memory import ( OFFLOAD_GROUP, @@ -247,7 +248,11 @@ class SdCppDiffusionBackend: ) fam = detect_family(repo_id, family_override) if fam is None: - raise ValueError(f"Could not infer a diffusion family for '{repo_id}'.") + raise ValueError( + f"'{repo_id}' is not a supported diffusion image model. Supported families: " + f"{', '.join(supported_family_names())}. If this is a variant of one of them, " + f"pass family_override with that family name." + ) if not family_sd_cpp_supported(fam): raise ValueError(f"Family '{fam.name}' has no native sd.cpp asset mapping.") diff --git a/studio/backend/tests/test_diffusion_backend.py b/studio/backend/tests/test_diffusion_backend.py index 931f22920b..f4d64b19f4 100644 --- a/studio/backend/tests/test_diffusion_backend.py +++ b/studio/backend/tests/test_diffusion_backend.py @@ -26,6 +26,7 @@ from core.inference.diffusion_families import ( detect_family, resolve_base_repo, resolve_local_gguf_child, + supported_family_names, ) @@ -82,6 +83,16 @@ def test_detect_family_override(): assert detect_family("local/path", override = "not-a-family") is None +def test_supported_family_names(): + names = supported_family_names() + # The unknown-model error lists these, so the key families must be present. + for expected in ("flux.1", "flux.2-klein", "flux.2-dev", "qwen-image", "z-image"): + assert expected in names + # Every listed name is a valid family_override (round-trips through detect_family). + for name in names: + assert detect_family("some/unknown-repo", override = name) is not None + + def test_resolve_base_repo(): fam = detect_family("x", override = "z-image") assert resolve_base_repo(fam, None) == fam.base_repo