From 387cfd268b1289d2ad8fde618d1fdc89691ed429 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 10:47:50 +0000 Subject: [PATCH 01/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_precision.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 38a3434490..9b8dba28e3 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -121,7 +121,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: From 91780a250ef73b3a5f88d4e2f0c0343a0828dd5f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:26:10 +0000 Subject: [PATCH 02/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_precision.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: From 7410f8fa1ce0fdda7890a25ddb61d4e02d1d6c5b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:28:55 +0000 Subject: [PATCH 03/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_speed.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index b774370519..9184f29f88 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -240,6 +240,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). From 394f7985df0f7b0e7b42bd7e249bf95df0647dce Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:30:37 +0000 Subject: [PATCH 04/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_transformer_quant.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 6e1341983f..7cb1074425 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -257,6 +257,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail From ab27ea85203ce9d1a968ac753262e903c0131896 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:31:14 +0000 Subject: [PATCH 05/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index b774370519..9184f29f88 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -240,6 +240,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 6e1341983f..7cb1074425 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -257,6 +257,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail From 1ab9db02ffbcb07a71b85aa077faee97137612f0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:32:10 +0000 Subject: [PATCH 06/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index b774370519..9184f29f88 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -240,6 +240,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 6e1341983f..7cb1074425 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -257,6 +257,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index eb497a46d2..dc34e60cef 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -445,6 +445,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From c1b4ed1233526c1e581361f83168f9b3d4c3bd63 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:32:41 +0000 Subject: [PATCH 07/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index b774370519..9184f29f88 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -240,6 +240,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 1afdd4200d..cb6873a661 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -284,6 +284,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index eb497a46d2..dc34e60cef 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -445,6 +445,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From a8e87ac77ba30e19cdb5f6af072d9bdb5160218c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:33:16 +0000 Subject: [PATCH 08/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 62bab8dcba..208cb8b513 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -249,6 +249,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 1afdd4200d..cb6873a661 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -284,6 +284,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index baaa8696f9..d03c48b477 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -445,6 +445,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From ffe5b697732e1018a1708c28847d6c3ebae74935 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:33:47 +0000 Subject: [PATCH 09/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 62bab8dcba..208cb8b513 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -249,6 +249,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 94be813a94..9e2e56a2a5 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -313,6 +313,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index baaa8696f9..d03c48b477 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -445,6 +445,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From db081d67deaa8c67a41dc15b6c022d16806a4d53 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:34:18 +0000 Subject: [PATCH 10/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 62bab8dcba..208cb8b513 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -249,6 +249,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 94be813a94..9e2e56a2a5 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -313,6 +313,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index baaa8696f9..d03c48b477 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -445,6 +445,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From 44beb54df5ea0fc79cc7faf9dbd68518a84317c9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:36:04 +0000 Subject: [PATCH 11/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 62bab8dcba..208cb8b513 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -249,6 +249,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 94be813a94..9e2e56a2a5 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -313,6 +313,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index c37ca3ef14..d0b6997d07 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -465,6 +465,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From 5728670f6e441d0cf82522ecba36696e8b34909e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 11:57:18 +0000 Subject: [PATCH 12/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion_attention.py | 1 - studio/backend/core/inference/diffusion_precision.py | 5 ++++- studio/backend/core/inference/diffusion_speed.py | 2 ++ studio/backend/core/inference/diffusion_transformer_quant.py | 1 + studio/backend/tests/test_diffusion_routes.py | 2 ++ 5 files changed, 9 insertions(+), 2 deletions(-) diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 662fff74dd..2738691e58 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -282,6 +282,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 94be813a94..9e2e56a2a5 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -313,6 +313,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index b00af9cfd6..b7fbb3f50c 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -487,6 +487,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots From a927f4dc1e2343c02b34e96a902d5080505adc5e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 13:58:10 +0000 Subject: [PATCH 13/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- studio/backend/core/inference/diffusion.py | 4 +++- .../core/inference/diffusion_attention.py | 1 - .../core/inference/diffusion_precision.py | 5 ++++- .../backend/core/inference/diffusion_speed.py | 2 ++ .../inference/diffusion_transformer_quant.py | 1 + .../backend/tests/test_diffusion_backend.py | 4 +--- studio/backend/tests/test_diffusion_routes.py | 2 ++ studio/backend/tests/test_diffusion_sdxl.py | 22 +++++++++---------- 8 files changed, 24 insertions(+), 17 deletions(-) diff --git a/studio/backend/core/inference/diffusion.py b/studio/backend/core/inference/diffusion.py index 816e3a0e01..eaaa93dede 100644 --- a/studio/backend/core/inference/diffusion.py +++ b/studio/backend/core/inference/diffusion.py @@ -1538,7 +1538,9 @@ class DiffusionBackend: mask_pil = mask_pil.resize(init_pil.size, _PILImage.NEAREST) if init_pil is not None: # Keep the VAE encode dtype consistent with the input image. - self._align_vae_dtype(pipe, getattr(state.family, "denoiser_attr", "transformer")) + self._align_vae_dtype( + pipe, getattr(state.family, "denoiser_attr", "transformer") + ) # Pipelines vary in which kwargs they accept (img2img derives size from the # input image and may reject width/height; a distilled pipe may take no diff --git a/studio/backend/core/inference/diffusion_attention.py b/studio/backend/core/inference/diffusion_attention.py index 04f812a13e..e652b0068c 100644 --- a/studio/backend/core/inference/diffusion_attention.py +++ b/studio/backend/core/inference/diffusion_attention.py @@ -225,7 +225,6 @@ def _reset_global_backend_to_native(logger: Any) -> None: AttentionBackendName, _AttentionBackendRegistry, ) - _AttentionBackendRegistry.set_active_backend(AttentionBackendName.NATIVE) except Exception: # noqa: BLE001 — best-effort; leave the global as-is on any change pass diff --git a/studio/backend/core/inference/diffusion_precision.py b/studio/backend/core/inference/diffusion_precision.py index 68036aae8d..030b6da7f2 100644 --- a/studio/backend/core/inference/diffusion_precision.py +++ b/studio/backend/core/inference/diffusion_precision.py @@ -120,7 +120,10 @@ def _cast_fp8(encoder: Any, target: Any) -> None: # gets cast to fp8 and, sharing one tensor, drags the embedding to fp8 with it. The # embedding then emits fp8 activations that crash the first RMSNorm. Skip the tied # projection so the shared tensor stays dense (lm_head is unused for prompt encoding). - get_out, get_in = getattr(encoder, "get_output_embeddings", None), getattr(encoder, "get_input_embeddings", None) + get_out, get_in = ( + getattr(encoder, "get_output_embeddings", None), + getattr(encoder, "get_input_embeddings", None), + ) out_emb = get_out() if callable(get_out) else None in_emb = get_in() if callable(get_in) else None if out_emb is not None and in_emb is not None and out_emb.weight is in_emb.weight: diff --git a/studio/backend/core/inference/diffusion_speed.py b/studio/backend/core/inference/diffusion_speed.py index 662fff74dd..2738691e58 100644 --- a/studio/backend/core/inference/diffusion_speed.py +++ b/studio/backend/core/inference/diffusion_speed.py @@ -282,6 +282,8 @@ def _enable_cudnn_benchmark(logger: Any) -> bool: except Exception as exc: # noqa: BLE001 — optimisation only _warn(logger, "cudnn_benchmark", exc) return False + + # The TF32 flag values from before the first max load flipped them, so a later # non-max load / unload can put the process back exactly as it found it (rather than # forcing a hardcoded default that might clobber another component's choice). diff --git a/studio/backend/core/inference/diffusion_transformer_quant.py b/studio/backend/core/inference/diffusion_transformer_quant.py index 94be813a94..9e2e56a2a5 100644 --- a/studio/backend/core/inference/diffusion_transformer_quant.py +++ b/studio/backend/core/inference/diffusion_transformer_quant.py @@ -313,6 +313,7 @@ def _make_quant_config(scheme: str, fast_accum: Optional[bool] = None) -> Any: return Float8DynamicActivationFloat8WeightConfig() if scheme == TQ_NVFP4: from torchao.prototype.mx_formats import NVFP4DynamicActivationNVFP4WeightConfig + # Select the CUTLASS FP4 path, not the default Triton kernel: torchao defaults # use_triton_kernel=True, which needs MSLK installed. On a Blackwell box with the # CUTLASS FP4 extension but no MSLK, the default would make the smoke probe fail diff --git a/studio/backend/tests/test_diffusion_backend.py b/studio/backend/tests/test_diffusion_backend.py index 0b0e1e11cb..b05d6a3940 100644 --- a/studio/backend/tests/test_diffusion_backend.py +++ b/studio/backend/tests/test_diffusion_backend.py @@ -889,9 +889,7 @@ def test_load_sdxl_single_file_uses_pipeline_from_single_file(fake_runtime, tmp_ assert status["loaded"] is True assert status["family"] == "sdxl" # The whole-pipeline single-file path was taken with the base repo as config. - assert _FakePipeline.last_single_file["path"] == str( - (tmp_path / "sdxl.safetensors").resolve() - ) + assert _FakePipeline.last_single_file["path"] == str((tmp_path / "sdxl.safetensors").resolve()) assert _FakePipeline.last_single_file["config"] == "stabilityai/stable-diffusion-xl-base-1.0" # The transformer-only single-file build was NOT taken. assert _FakeTransformer.last == {} diff --git a/studio/backend/tests/test_diffusion_routes.py b/studio/backend/tests/test_diffusion_routes.py index b00af9cfd6..b7fbb3f50c 100644 --- a/studio/backend/tests/test_diffusion_routes.py +++ b/studio/backend/tests/test_diffusion_routes.py @@ -487,6 +487,8 @@ def test_invalid_attention_backend_returns_422(client): json = {"model_path": "x/z-image", "gguf_filename": "q.gguf", "attention_backend": "bogus"}, ) assert resp.status_code == 422 + + def test_prequant_path_doc_describes_allowlist_not_toggle(): # The field help must match the code: UNSLOTH_ALLOW_LOCAL_PREQUANT_PATH is a # directory allowlist, not a =1 toggle (diffusion_prequant._allowed_prequant_roots diff --git a/studio/backend/tests/test_diffusion_sdxl.py b/studio/backend/tests/test_diffusion_sdxl.py index a0b3044952..41ee904b94 100644 --- a/studio/backend/tests/test_diffusion_sdxl.py +++ b/studio/backend/tests/test_diffusion_sdxl.py @@ -46,7 +46,7 @@ def test_sdxl_detection_by_repo_and_override(): assert detect_family("stabilityai/sdxl-turbo").name == "sdxl" assert detect_family("some-org/My-Cool-SDXL-Merge").name == "sdxl" assert detect_family("some-org/stable-diffusion-xl-anime").name == "sdxl" - assert detect_family("x", override="sdxl").name == "sdxl" + assert detect_family("x", override = "sdxl").name == "sdxl" # A GGUF DiT family must NOT be swallowed by the SDXL match. assert detect_family("unsloth/FLUX.1-schnell-GGUF").name == "flux.1" @@ -91,9 +91,9 @@ class _FakeVae: self.moved_to = None def parameters(self): - yield types.SimpleNamespace(dtype=self._dtype) + yield types.SimpleNamespace(dtype = self._dtype) - def to(self, dtype=None): + def to(self, dtype = None): self.moved_to = dtype self._dtype = dtype @@ -101,29 +101,29 @@ class _FakeVae: def test_align_vae_dtype_uses_unet_denoiser(): # For SDXL the denoiser lives at pipe.unet; _align_vae_dtype must read it (a pipe # with only .unet and no .transformer) and cast the VAE to the U-Net's dtype. - vae = _FakeVae(dtype="float32") - pipe = types.SimpleNamespace(unet=types.SimpleNamespace(dtype="bfloat16"), vae=vae) + vae = _FakeVae(dtype = "float32") + pipe = types.SimpleNamespace(unet = types.SimpleNamespace(dtype = "bfloat16"), vae = vae) DiffusionBackend._align_vae_dtype(pipe, "unet") assert vae.moved_to == "bfloat16" def test_align_vae_dtype_transformer_default_unchanged(): # DiT default: reads pipe.transformer; a pipe with no transformer is a safe no-op. - vae = _FakeVae(dtype="float32") - pipe = types.SimpleNamespace(transformer=types.SimpleNamespace(dtype="bfloat16"), vae=vae) + vae = _FakeVae(dtype = "float32") + pipe = types.SimpleNamespace(transformer = types.SimpleNamespace(dtype = "bfloat16"), vae = vae) DiffusionBackend._align_vae_dtype(pipe) assert vae.moved_to == "bfloat16" # No denoiser attribute -> no-op (does not raise, does not move the VAE). - vae2 = _FakeVae(dtype="float32") - DiffusionBackend._align_vae_dtype(types.SimpleNamespace(vae=vae2), "unet") + vae2 = _FakeVae(dtype = "float32") + DiffusionBackend._align_vae_dtype(types.SimpleNamespace(vae = vae2), "unet") assert vae2.moved_to is None def test_sdxl_lora_supported_on_diffusers(): # SDXL is bf16/bnb-4bit on diffusers -> LoRA is allowed (unlike GGUF-via-diffusers). assert diffusion_lora.supports_lora( - engine="diffusers", family="sdxl", model_kind="pipeline", transformer_quant=None + engine = "diffusers", family = "sdxl", model_kind = "pipeline", transformer_quant = None ) assert diffusion_lora.supports_lora( - engine="diffusers", family="sdxl", model_kind="single_file", transformer_quant=None + engine = "diffusers", family = "sdxl", model_kind = "single_file", transformer_quant = None ) From 0f1c4a35ba293986a163fc2944b6d366092bd4f0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:48:15 +0000 Subject: [PATCH 14/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../training/diffusion_training_service.py | 65 +++++++++-------- studio/backend/models/training.py | 32 +++++---- studio/backend/routes/training.py | 11 +-- .../backend/tests/test_diffusion_training.py | 69 +++++++++++++------ 4 files changed, 107 insertions(+), 70 deletions(-) diff --git a/studio/backend/core/training/diffusion_training_service.py b/studio/backend/core/training/diffusion_training_service.py index 166d2a9193..bdefcfe25b 100644 --- a/studio/backend/core/training/diffusion_training_service.py +++ b/studio/backend/core/training/diffusion_training_service.py @@ -34,8 +34,7 @@ _TERMINAL = ("complete", "error") def _default_target(*, event_queue: Any, stop_queue: Any, config: dict) -> None: # Imported lazily so this module (and the route layer) stays torch-free at import. from .diffusion_lora_trainer import run_diffusion_training_process - - run_diffusion_training_process(event_queue=event_queue, stop_queue=stop_queue, config=config) + run_diffusion_training_process(event_queue = event_queue, stop_queue = stop_queue, config = config) def _idle_state() -> dict[str, Any]: @@ -94,20 +93,23 @@ class DiffusionTrainingService: if self._proc is not None and self._proc.is_alive(): raise RuntimeError("A diffusion training job is already running.") if self._pump is not None and self._pump.is_alive(): - self._pump.join(timeout=5.0) + self._pump.join(timeout = 5.0) job_id = uuid.uuid4().hex event_queue = self._ctx.Queue() self._stop_queue = self._ctx.Queue() self._proc = self._ctx.Process( - target=self._target, - kwargs={"event_queue": event_queue, "stop_queue": self._stop_queue, "config": config}, - daemon=True, + target = self._target, + kwargs = { + "event_queue": event_queue, + "stop_queue": self._stop_queue, + "config": config, + }, + daemon = True, ) self._proc.start() try: from utils.process_lifetime import adopt_pid - adopt_pid(self._proc.pid) # bind to parent lifetime (no zombie on exit) except Exception: # noqa: BLE001 -- lifetime binding is best-effort pass @@ -115,11 +117,15 @@ class DiffusionTrainingService: now = time.time() self._state = _idle_state() self._state.update( - active=True, job_id=job_id, status="running", - message="Starting diffusion LoRA training...", started_at=now, updated_at=now, + active = True, + job_id = job_id, + status = "running", + message = "Starting diffusion LoRA training...", + started_at = now, + updated_at = now, ) self._pump = threading.Thread( - target=self._pump_loop, args=(event_queue, self._proc), daemon=True + target = self._pump_loop, args = (event_queue, self._proc), daemon = True ) self._pump.start() return job_id @@ -149,7 +155,7 @@ class DiffusionTrainingService: def _pump_loop(self, event_queue: Any, proc: Any) -> None: while True: try: - ev = event_queue.get(timeout=1.0) + ev = event_queue.get(timeout = 1.0) except Exception: # noqa: BLE001 -- Empty (timeout) or a closed queue if not proc.is_alive(): # Drain anything buffered, then decide if it exited cleanly. @@ -163,9 +169,10 @@ class DiffusionTrainingService: with self._lock: if self._state.get("status") not in ("completed", "stopped", "error"): self._state.update( - active=False, status="error", - message="Training process exited unexpectedly.", - updated_at=time.time(), + active = False, + status = "error", + message = "Training process exited unexpectedly.", + updated_at = time.time(), ) _ = drained return @@ -182,33 +189,33 @@ class DiffusionTrainingService: s = self._state s["updated_at"] = time.time() if etype == "model_load_started": - s.update(in_model_load=True, status="running", message="Loading base model...") + s.update(in_model_load = True, status = "running", message = "Loading base model...") if ev.get("num_images") is not None: s["num_images"] = ev.get("num_images") elif etype == "model_load_completed": - s.update(in_model_load=False, message="Training...") + s.update(in_model_load = False, message = "Training...") elif etype == "progress": s.update( - status="running", - step=ev.get("step", s["step"]), - total_steps=ev.get("total_steps", s["total_steps"]), - loss=ev.get("loss", s["loss"]), - avg_loss=ev.get("avg_loss", s["avg_loss"]), - learning_rate=ev.get("learning_rate", s["learning_rate"]), - message="Training...", + status = "running", + step = ev.get("step", s["step"]), + total_steps = ev.get("total_steps", s["total_steps"]), + loss = ev.get("loss", s["loss"]), + avg_loss = ev.get("avg_loss", s["avg_loss"]), + learning_rate = ev.get("learning_rate", s["learning_rate"]), + message = "Training...", ) elif etype == "complete": s.update( - active=False, - status="stopped" if ev.get("stopped") else "completed", - output_dir=ev.get("output_dir"), - lora_path=ev.get("lora_path"), - message="Stopped (partial adapter saved)." + active = False, + status = "stopped" if ev.get("stopped") else "completed", + output_dir = ev.get("output_dir"), + lora_path = ev.get("lora_path"), + message = "Stopped (partial adapter saved)." if ev.get("stopped") else "Training complete.", ) elif etype == "error": - s.update(active=False, status="error", message=str(ev.get("message", "error"))) + s.update(active = False, status = "error", message = str(ev.get("message", "error"))) _service: Optional[DiffusionTrainingService] = None diff --git a/studio/backend/models/training.py b/studio/backend/models/training.py index 85f0949810..9a7774d8be 100644 --- a/studio/backend/models/training.py +++ b/studio/backend/models/training.py @@ -675,28 +675,30 @@ class DiffusionTrainingStartRequest(BaseModel): rest carry the trainer's defaults. """ - model_config = ConfigDict(protected_namespaces=()) + model_config = ConfigDict(protected_namespaces = ()) - base_model: str = Field(..., description="HF repo id or local path to an SDXL pipeline") - data_dir: str = Field(..., description="Folder of training images (+ captions)") - output_dir: str = Field(..., description="Directory to write the LoRA .safetensors into") + base_model: str = Field(..., description = "HF repo id or local path to an SDXL pipeline") + data_dir: str = Field(..., description = "Folder of training images (+ captions)") + output_dir: str = Field(..., description = "Directory to write the LoRA .safetensors into") instance_prompt: Optional[str] = Field( - None, description="Dreambooth caption applied to images without their own caption" + None, description = "Dreambooth caption applied to images without their own caption" ) - resolution: int = Field(1024, ge=64, le=2048, description="Square training resolution (multiple of 8)") - train_steps: int = Field(500, ge=1, le=100000) - learning_rate: float = Field(1e-4, gt=0) - train_batch_size: int = Field(1, ge=1, le=64) - gradient_accumulation_steps: int = Field(1, ge=1, le=256) - lora_rank: int = Field(16, ge=1, le=320) - lora_alpha: Optional[int] = Field(None, ge=1, le=640, description="Defaults to lora_rank") - lora_dropout: float = Field(0.0, ge=0.0, le=1.0) + resolution: int = Field( + 1024, ge = 64, le = 2048, description = "Square training resolution (multiple of 8)" + ) + train_steps: int = Field(500, ge = 1, le = 100000) + learning_rate: float = Field(1e-4, gt = 0) + train_batch_size: int = Field(1, ge = 1, le = 64) + gradient_accumulation_steps: int = Field(1, ge = 1, le = 256) + lora_rank: int = Field(16, ge = 1, le = 320) + lora_alpha: Optional[int] = Field(None, ge = 1, le = 640, description = "Defaults to lora_rank") + lora_dropout: float = Field(0.0, ge = 0.0, le = 1.0) seed: int = Field(42) mixed_precision: Literal["bf16", "fp16", "no"] = Field("bf16") - snr_gamma: Optional[float] = Field(5.0, description="Min-SNR loss weighting; null disables") + snr_gamma: Optional[float] = Field(5.0, description = "Min-SNR loss weighting; null disables") gradient_checkpointing: bool = Field(True) lr_scheduler: str = Field("constant") - lr_warmup_steps: int = Field(0, ge=0) + lr_warmup_steps: int = Field(0, ge = 0) center_crop: bool = Field(False) random_flip: bool = Field(True) caption_column: str = Field("text") diff --git a/studio/backend/routes/training.py b/studio/backend/routes/training.py index b2ea6dfaa4..c96189bef2 100644 --- a/studio/backend/routes/training.py +++ b/studio/backend/routes/training.py @@ -1031,8 +1031,7 @@ async def stream_training_progress( @router.post("/diffusion/start", response_model = DiffusionTrainingStartResponse) async def start_diffusion_training( - body: DiffusionTrainingStartRequest, - current_subject: str = Depends(get_current_subject), + body: DiffusionTrainingStartRequest, current_subject: str = Depends(get_current_subject) ): """Start an SDXL LoRA training job from an image + caption dataset.""" from core.training.diffusion_training_service import get_diffusion_training_service @@ -1047,8 +1046,11 @@ async def start_diffusion_training( raise HTTPException(status_code = 409, detail = str(e)) except Exception as e: raise log_and_http_error( - e, 500, "Failed to start diffusion training", - event = "diffusion_training.start_failed", log = logger, + e, + 500, + "Failed to start diffusion training", + event = "diffusion_training.start_failed", + log = logger, ) return DiffusionTrainingStartResponse(job_id = job_id, status = "running") @@ -1066,5 +1068,4 @@ async def stop_diffusion_training(current_subject: str = Depends(get_current_sub async def diffusion_training_status(current_subject: str = Depends(get_current_subject)): """Poll the current diffusion training job's status/progress (JSON).""" from core.training.diffusion_training_service import get_diffusion_training_service - return DiffusionTrainingStatusResponse(**get_diffusion_training_service().status()) diff --git a/studio/backend/tests/test_diffusion_training.py b/studio/backend/tests/test_diffusion_training.py index ae95e6ce7e..ffc231673c 100644 --- a/studio/backend/tests/test_diffusion_training.py +++ b/studio/backend/tests/test_diffusion_training.py @@ -32,8 +32,8 @@ class _FakeQueue: def put(self, x): self._q.put(x) - def get(self, timeout=None): - return self._q.get(timeout=timeout) # raises queue.Empty on timeout + def get(self, timeout = None): + return self._q.get(timeout = timeout) # raises queue.Empty on timeout def get_nowait(self): return self._q.get_nowait() @@ -50,7 +50,7 @@ class _FakeProc: self.pid = 4321 def start(self): - self._thread = threading.Thread(target=self._target, kwargs=self._kwargs, daemon=True) + self._thread = threading.Thread(target = self._target, kwargs = self._kwargs, daemon = True) self._thread.start() def is_alive(self): @@ -69,10 +69,24 @@ def _happy_target(*, event_queue, stop_queue, config): event_queue.put({"type": "model_load_started", "num_images": 3}) event_queue.put({"type": "model_load_completed"}) event_queue.put( - {"type": "progress", "step": 1, "total_steps": 2, "loss": 0.5, "avg_loss": 0.5, "learning_rate": 1e-4} + { + "type": "progress", + "step": 1, + "total_steps": 2, + "loss": 0.5, + "avg_loss": 0.5, + "learning_rate": 1e-4, + } ) event_queue.put( - {"type": "progress", "step": 2, "total_steps": 2, "loss": 0.4, "avg_loss": 0.45, "learning_rate": 1e-4} + { + "type": "progress", + "step": 2, + "total_steps": 2, + "loss": 0.4, + "avg_loss": 0.45, + "learning_rate": 1e-4, + } ) event_queue.put( { @@ -86,8 +100,10 @@ def _happy_target(*, event_queue, stop_queue, config): def _stoppable_target(*, event_queue, stop_queue, config): event_queue.put({"type": "model_load_completed"}) - stop_queue.get(timeout=5.0) # block until stop() signals - event_queue.put({"type": "complete", "output_dir": config["output_dir"], "lora_path": "x", "stopped": True}) + stop_queue.get(timeout = 5.0) # block until stop() signals + event_queue.put( + {"type": "complete", "output_dir": config["output_dir"], "lora_path": "x", "stopped": True} + ) def _crashing_target(*, event_queue, stop_queue, config): @@ -98,7 +114,11 @@ def _crashing_target(*, event_queue, stop_queue, config): _CFG = {"base_model": "b", "data_dir": "d", "output_dir": "/tmp/out", "train_steps": 2} -def _wait_status(svc, *terminal, timeout=3.0): +def _wait_status( + svc, + *terminal, + timeout = 3.0, +): end = time.time() + timeout while time.time() < end: st = svc.status() @@ -109,7 +129,7 @@ def _wait_status(svc, *terminal, timeout=3.0): def test_service_happy_path(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_happy_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _happy_target) job_id = svc.start(dict(_CFG)) assert job_id st = _wait_status(svc, "completed") @@ -122,7 +142,7 @@ def test_service_happy_path(): def test_service_rejects_bad_config_before_spawn(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_happy_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _happy_target) with pytest.raises(ValueError): svc.start({**_CFG, "train_steps": 0}) # Nothing was spawned; still idle. @@ -130,7 +150,7 @@ def test_service_rejects_bad_config_before_spawn(): def test_service_rejects_second_concurrent_job(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_stoppable_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _stoppable_target) svc.start(dict(_CFG)) _wait_status(svc, "running") with pytest.raises(RuntimeError): @@ -140,7 +160,7 @@ def test_service_rejects_second_concurrent_job(): def test_service_stop_marks_stopped(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_stoppable_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _stoppable_target) svc.start(dict(_CFG)) _wait_status(svc, "running") assert svc.stop() is True @@ -152,7 +172,7 @@ def test_service_stop_marks_stopped(): def test_service_crash_without_terminal_event_is_error(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_crashing_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _crashing_target) svc.start(dict(_CFG)) st = _wait_status(svc, "error") assert st["status"] == "error" @@ -160,7 +180,7 @@ def test_service_crash_without_terminal_event_is_error(): def test_apply_event_transitions(): - svc = DiffusionTrainingService(ctx=_FakeCtx(), target=_happy_target) + svc = DiffusionTrainingService(ctx = _FakeCtx(), target = _happy_target) svc._apply_event({"type": "model_load_started", "num_images": 5}) assert svc.status()["in_model_load"] is True and svc.status()["num_images"] == 5 svc._apply_event({"type": "model_load_completed"}) @@ -212,25 +232,32 @@ def client(monkeypatch): "core.training.diffusion_training_service.get_diffusion_training_service", lambda: fake ) app = FastAPI() - app.include_router(training_router, prefix="/api/train") + app.include_router(training_router, prefix = "/api/train") app.dependency_overrides[get_current_subject] = lambda: "test-user" c = TestClient(app) c._fake = fake # type: ignore[attr-defined] return c -_BODY = {"base_model": "stabilityai/sdxl-turbo", "data_dir": "/data", "output_dir": "/out", "train_steps": 10} +_BODY = { + "base_model": "stabilityai/sdxl-turbo", + "data_dir": "/data", + "output_dir": "/out", + "train_steps": 10, +} def test_route_start_ok(client): - r = client.post("/api/train/diffusion/start", json=_BODY) + r = client.post("/api/train/diffusion/start", json = _BODY) assert r.status_code == 200, r.text assert r.json() == {"job_id": "job-123", "status": "running"} assert client._fake.started_with["base_model"] == "stabilityai/sdxl-turbo" def test_route_start_missing_required_is_422(client): - r = client.post("/api/train/diffusion/start", json={"base_model": "x"}) # no data_dir/output_dir + r = client.post( + "/api/train/diffusion/start", json = {"base_model": "x"} + ) # no data_dir/output_dir assert r.status_code == 422 @@ -239,7 +266,7 @@ def test_route_start_bad_config_maps_to_400(client, monkeypatch): raise ValueError("resolution must be a multiple of 8") client._fake.start = _raise # type: ignore[assignment] - r = client.post("/api/train/diffusion/start", json=_BODY) + r = client.post("/api/train/diffusion/start", json = _BODY) assert r.status_code == 400 assert "multiple of 8" in r.json()["detail"] @@ -249,12 +276,12 @@ def test_route_start_conflict_maps_to_409(client): raise RuntimeError("A diffusion training job is already running.") client._fake.start = _raise # type: ignore[assignment] - r = client.post("/api/train/diffusion/start", json=_BODY) + r = client.post("/api/train/diffusion/start", json = _BODY) assert r.status_code == 409 def test_route_status_and_stop(client): - client.post("/api/train/diffusion/start", json=_BODY) + client.post("/api/train/diffusion/start", json = _BODY) s = client.get("/api/train/diffusion/status") assert s.status_code == 200 and s.json()["status"] == "running" st = client.post("/api/train/diffusion/stop")