From d927bf155c2bd8d04fd948e1f3a900bae9873724 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 4 Jul 2026 01:06:42 +0000 Subject: [PATCH 1/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/sd_cpp_backend.py | 5 ++++- studio/backend/tests/test_sd_cpp_backend.py | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/studio/backend/core/inference/sd_cpp_backend.py b/studio/backend/core/inference/sd_cpp_backend.py index 7632551661..d8cb21d409 100644 --- a/studio/backend/core/inference/sd_cpp_backend.py +++ b/studio/backend/core/inference/sd_cpp_backend.py @@ -893,7 +893,10 @@ class SdCppDiffusionBackend: lora_stage = Path(server_lora_dir) / f"gen_{os.urandom(6).hex()}" materialized = diffusion_lora.materialize_native_dir(lora_resolved, lora_stage) lora_payload = [ - {"path": f"{lora_stage.name}/{Path(m.path).name}", "multiplier": float(m.weight)} + { + "path": f"{lora_stage.name}/{Path(m.path).name}", + "multiplier": float(m.weight), + } for m in materialized ] try: diff --git a/studio/backend/tests/test_sd_cpp_backend.py b/studio/backend/tests/test_sd_cpp_backend.py index 0b52ea6e14..be2fc22a4b 100644 --- a/studio/backend/tests/test_sd_cpp_backend.py +++ b/studio/backend/tests/test_sd_cpp_backend.py @@ -691,7 +691,11 @@ def _fake_materialize(resolved, dest): return out -def _patch_lora(monkeypatch, resolved, supported = True): +def _patch_lora( + monkeypatch, + resolved, + supported = True, +): from core.inference import diffusion_lora as dl monkeypatch.setattr(dl, "supports_lora", lambda **k: supported) @@ -706,7 +710,9 @@ def test_generate_oneshot_applies_loras_via_prompt_tags(monkeypatch): eng = _FakeEngine() b = _loaded_backend(engine = eng) # mode = "oneshot" - _patch_lora(monkeypatch, [dl.ResolvedLora("id1", "myalias", "/x/a.safetensors", "safetensors", 0.8)]) + _patch_lora( + monkeypatch, [dl.ResolvedLora("id1", "myalias", "/x/a.safetensors", "safetensors", 0.8)] + ) b.generate(prompt = "a fox", steps = 4, seed = 1, loras = [("id1", 0.8)]) _, params, _, _ = eng.calls[0] assert params.lora_dir is not None and params.lora_apply_mode == "auto" @@ -725,7 +731,9 @@ def test_generate_server_stages_loras_and_sends_structured_field(monkeypatch, tm servers: list = [] _run_server_load(monkeypatch, b, servers) servers[0].lora_dir = str(tmp_path) - _patch_lora(monkeypatch, [dl.ResolvedLora("id1", "myalias", "/x/a.safetensors", "safetensors", 0.7)]) + _patch_lora( + monkeypatch, [dl.ResolvedLora("id1", "myalias", "/x/a.safetensors", "safetensors", 0.7)] + ) b.generate(prompt = "x", steps = 4, seed = 1, batch_size = 1, loras = [("id1", 0.7)]) payload = servers[0].payloads[0] assert "lora" in payload and len(payload["lora"]) == 1 From 23c6457e62a4ef026fbaff24714df469bbc5187f Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sat, 4 Jul 2026 01:27:40 +0000 Subject: [PATCH 2/2] Address review: Train panel precision and notification edge cases - Reset mixed precision to bf16 when the family changes to a DiT: an fp16/no value left over from SDXL rode along in the DiT start payload and the backend rejected it (dense base precisions require bf16 compute). - Gate the dense base precisions behind the selected base: a bnb-4bit repo disables bf16/int8/fp8 with a hint, and a dense selection auto-flips to auto so the run does not fail at the validator. - Re-arm the run-completion notification in onStart, so a second run notifies even when its running phase is never observed by the poll. --- .../images/train/diffusion-train-panel.tsx | 64 ++++++++++++++++++- 1 file changed, 62 insertions(+), 2 deletions(-) diff --git a/studio/frontend/src/features/images/train/diffusion-train-panel.tsx b/studio/frontend/src/features/images/train/diffusion-train-panel.tsx index a76e8e5209..813a6c1fd2 100644 --- a/studio/frontend/src/features/images/train/diffusion-train-panel.tsx +++ b/studio/frontend/src/features/images/train/diffusion-train-panel.tsx @@ -92,6 +92,21 @@ const FAMILY_PRESETS: FamilyPreset[] = [ const CUSTOM_BASE = "__custom__"; const UPLOAD_DATASET = "__upload__"; +// The dense DiT base precisions: they load a dense (bf16) base and quantise/cast it, so the +// backend rejects them for an already-quantised bnb-4bit repo. "nf4"/"auto" stay valid. +const DENSE_PRECISIONS = new Set(["bf16", "int8", "fp8"]); +// Mirror the backend's repo_is_prequantized heuristic: a repo whose name marks a +// bitsandbytes 4-bit build already ships a quantised transformer and cannot serve the dense +// base precisions. Kept in sync with diffusion_train_common.repo_is_prequantized. +function repoIsPrequantized(baseModel: string): boolean { + const name = baseModel.toLowerCase(); + return ( + name.includes("bnb-4bit") || + name.includes("-4bit") || + name.includes("int4") || + name.includes("nf4") + ); +} // Dataset-select option value prefix for a not-yet-imported example; picking it imports. const EXAMPLE_PREFIX = "example:"; const DATASET_FILE_ACCEPT = ".png,.jpg,.jpeg,.webp,.bmp,.txt,.caption,.jsonl"; @@ -372,6 +387,16 @@ export function DiffusionTrainPanel({ } }, [family, loadedBaseRepo, reportedFamily?.recommended_precision]); + // mixed_precision is an SDXL-only lever (its UI control is hidden for DiT families). A + // dense DiT base precision (bf16/int8/fp8) requires bf16 compute, and every DiT family + // trains in bf16, so reset precision to bf16 when the family changes to a DiT. Without + // this, an fp16/no value left over from SDXL rides along in the DiT start payload and the + // backend rejects it (dense modes need mixed_precision=bf16). Kept in its own effect so it + // does not re-trigger the base/settings reseed above. + useEffect(() => { + if (isDiT) setPrecision("bf16"); + }, [isDiT]); + // The base actually used everywhere (request, deploy, select value). baseChoice can // briefly hold another family's repo between a family switch and the reseed effect // (or if that effect is skipped); a raw