Fix/adjust diffusion: clear stale metadata on failed swap for PR #5754

When a swap load fails after the previous pipeline is released,
status() previously reported is_loaded=false on top of the OLD
repo/family/base_repo metadata, which the frontend then rendered
as a misleading 'still loaded: X' label. Clear all metadata
atomically with the pipe drop so a failed swap reports a clean
empty status plus last_error. Add regression test.
This commit is contained in:
Daniel Han-Chen 2026-05-25 00:33:40 +00:00
commit f44b55c796
2 changed files with 59 additions and 3 deletions

View file

@ -334,9 +334,10 @@ class DiffusionBackend:
for VAE / text encoders. ``family_override`` short-circuits the
substring matcher when an exotic repo name confuses it.
Raises ``RuntimeError`` on failure with a user-facing message;
the previous pipeline (if any) stays loaded so a failed swap
does not leave Studio in an unusable state.
Raises ``RuntimeError`` on failure with a user-facing message.
On a failed swap the previous pipeline is also released to
keep peak VRAM bounded; status() reports is_loaded=false with
last_error set so the caller can react.
"""
from huggingface_hub import hf_hub_download
import diffusers
@ -469,7 +470,19 @@ class DiffusionBackend:
old = self._pipe
if old is not None:
with self._lock:
# Clear ALL metadata together so a failed swap
# cannot leave status() reporting the previous
# repo / family / base_repo on top of an empty
# pipe. The except block below will restore
# last_error so the caller knows what happened.
self._pipe = None
self._family = None
self._repo_id = None
self._gguf_path = None
self._base_repo = None
self._device = None
self._dtype = None
self._loaded_at = None
_release(old)
old = None

View file

@ -417,6 +417,49 @@ def test_load_model_recovers_after_failure(monkeypatch):
assert s["last_error"] and "simulated load failure" in s["last_error"]
def test_failed_swap_clears_previous_metadata(monkeypatch):
"""After a successful load, a subsequent failing load must NOT
leave status() reporting the OLD repo/family/base_repo on top of
is_loaded=false. The clear must be atomic with the pipe drop."""
import sys
_install_fake_diffusers(monkeypatch)
from core.inference.diffusion import get_diffusion_backend
backend = get_diffusion_backend()
# First load succeeds.
backend.load_model(
"unsloth/FLUX.2-klein-4B-GGUF",
gguf_filename = "flux-2-klein-4b-Q4_K_S.gguf",
)
s_before = backend.status()
assert s_before["is_loaded"] is True
assert s_before["repo_id"] == "unsloth/FLUX.2-klein-4B-GGUF"
# Replace from_pretrained on the SAME fake module with a raising one
# without re-installing the rest of the fakes.
fake = sys.modules["diffusers"]
def _boom(cls, *a, **kw):
raise RuntimeError("simulated swap failure")
fake.Flux2KleinPipeline.from_pretrained = classmethod(_boom)
with pytest.raises(RuntimeError, match = "Failed to load diffusion model"):
backend.load_model(
"unsloth/FLUX.2-dev-GGUF",
gguf_filename = "flux2-dev-Q4_K_S.gguf",
)
s_after = backend.status()
assert s_after["is_loaded"] is False
# Critically: stale metadata from the previous successful load
# must be cleared, not just the pipe.
assert s_after["repo_id"] is None
assert s_after["family"] is None
assert s_after["base_repo"] is None
assert s_after["gguf_filename"] is None
assert s_after["last_error"] and "simulated swap failure" in s_after["last_error"]
def test_load_model_swap_drops_previous(monkeypatch):
_install_fake_diffusers(monkeypatch)
from core.inference.diffusion import get_diffusion_backend