Do not advertise a family the installed diffusers cannot build

The newer families (Z-Image, Krea 2, FLUX.2, LTX-2, HunyuanImage) exist only
from diffusers 0.39, and 0.39 cannot be installed on Python 3.9 at all --
diffusers dropped 3.9 in 0.38, so the requirement is conditional or the whole
extra becomes unresolvable. On such an environment the picker still offered
those rows, every pick failed deterministically, and the error's advice to run
pip install -U diffusers could not fix it without also upgrading Python.

The cached-repo picker now applies the same availability check
validate_load_request does, which is keyed on the pipeline class actually
present rather than on the Python version, so it is also right for an
intentionally pinned older diffusers on 3.10+. Fails open when diffusers cannot
be imported at all: that is a different problem and the load path reports it.
This commit is contained in:
Daniel Han 2026-07-27 09:50:02 +00:00
commit fdce76db2e
3 changed files with 69 additions and 2 deletions

View file

@ -641,6 +641,25 @@ def assert_pipeline_class_available(pipeline_class: str, family_name: str) -> No
)
def family_pipeline_available(fam: Optional[DiffusionFamily]) -> bool:
"""True when the installed diffusers actually has this family's pipeline class.
The boolean twin of ``assert_pipeline_class_available``, for the listing routes: the newer
families exist only from diffusers 0.39, and the packaging leaves an older diffusers
installable on Python 3.9 (diffusers dropped 3.9 in 0.38, so the 0.39 floor has to be
conditional or the extra becomes unresolvable). Advertising Z-Image or Krea 2 in the picker
on such an environment offers a pick that can only fail, and no `pip install -U diffusers`
can fix it without also upgrading Python. Fails OPEN (True) when diffusers cannot be
imported at all, so a listing never hides a model over an unrelated import problem."""
if fam is None:
return False
try:
import diffusers
except Exception: # noqa: BLE001 -- no diffusers here: the load path reports it properly
return True
return hasattr(diffusers, fam.pipeline_class)
def family_gguf_loadable(fam: DiffusionFamily) -> bool:
"""True when a GGUF transformer can be assembled for this family.

View file

@ -3672,11 +3672,18 @@ def _cached_repo_task(repo_info) -> Optional[str]:
# the image families this backend can assemble, so an unsloth-hosted pipeline of an unsupported
# class cleared the trust gate, was advertised as text-to-image, and then deterministically
# failed validate_load_request, which detects the family the same way.
# The third gate is the installed diffusers: the newer families exist only from 0.39, which
# cannot be installed on Python 3.9 at all (diffusers dropped 3.9 in 0.38), so on such an
# environment a Z-Image / Krea 2 / FLUX.2 row is a pick that can only fail, and the suggested
# `pip install -U diffusers` cannot fix it. validate_load_request refuses the same way.
try:
from core.inference.diffusion import _is_trusted_diffusion_repo
from core.inference.diffusion_families import detect_family
from core.inference.diffusion_families import detect_family, family_pipeline_available
if not _is_trusted_diffusion_repo(repo_id) or detect_family(repo_id) is None:
fam = detect_family(repo_id)
if not _is_trusted_diffusion_repo(repo_id) or fam is None:
return None
if not family_pipeline_available(fam):
return None
return "text-to-image"
except Exception: # noqa: BLE001 -- an import failure must not hide a usable repo

View file

@ -1854,3 +1854,44 @@ def test_cached_repo_task_agrees_with_the_image_loader(monkeypatch):
assert (
task == "text-to-image"
) == loader_accepts, f"{repo_id}: picker task={task} but loader accepts={loader_accepts}"
def test_cached_picker_hides_a_family_this_diffusers_cannot_build(monkeypatch):
# The newer families exist only from diffusers 0.39, which cannot be installed on Python 3.9 at
# all (diffusers dropped 3.9 in 0.38). Advertising one there is a pick that can only fail, and
# `pip install -U diffusers` cannot fix it without a Python upgrade -- so the picker applies the
# same availability check validate_load_request does.
import types
import routes.models as models_module
from core.inference.diffusion_families import detect_family, family_pipeline_available
fam = detect_family("unsloth/Z-Image-Turbo")
assert fam is not None
# Present in this environment's diffusers, so the row is offered.
assert family_pipeline_available(fam) is True
monkeypatch.setattr(models_module, "_repo_is_diffusers", lambda info: True)
monkeypatch.setattr(
"core.inference.diffusion._is_trusted_diffusion_repo", lambda repo_id: True
)
info = types.SimpleNamespace(repo_id = "unsloth/Z-Image-Turbo")
assert models_module._cached_repo_task(info) == "text-to-image"
# An older diffusers without the pipeline class hides the row instead.
monkeypatch.setattr(
"core.inference.diffusion_families.family_pipeline_available", lambda f: False
)
assert models_module._cached_repo_task(info) is None
def test_family_pipeline_available_fails_open_without_diffusers(monkeypatch):
# No diffusers at all is a different problem; the load path reports it properly, and a listing
# must not silently hide every image model over it.
import sys
from core.inference.diffusion_families import detect_family, family_pipeline_available
monkeypatch.setitem(sys.modules, "diffusers", None)
assert family_pipeline_available(detect_family("unsloth/Z-Image-Turbo")) is True
assert family_pipeline_available(None) is False