From b28aa1f2c4c307289d4f51c6d4335834b5ddf6e9 Mon Sep 17 00:00:00 2001 From: Dividesbyzer0 <54127744+zoomdbz@users.noreply.github.com> Date: Mon, 15 Jun 2026 02:21:01 -0400 Subject: [PATCH] fix(cookbook): allow local Windows Diffusers serving (#4077) --- routes/shell_routes.py | 8 ++++++++ static/js/cookbook.js | 5 +++-- static/js/cookbookServe.js | 4 ++-- tests/test_cookbook_cpu_only_serve.py | 23 +++++++++++++++++++++++ tests/test_cookbook_package_detection.py | 9 +++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/routes/shell_routes.py b/routes/shell_routes.py index 0eca092d4..b4e52325d 100644 --- a/routes/shell_routes.py +++ b/routes/shell_routes.py @@ -1063,6 +1063,13 @@ def setup_shell_routes() -> APIRouter: "category": "Image", "target": "remote", }, + { + "name": "transformers", + "pip": "transformers", + "desc": "Hugging Face model components used by SD/Flux pipelines and image tools", + "category": "Image", + "target": "remote", + }, { "name": "rembg", "pip": "rembg[gpu]", @@ -1257,6 +1264,7 @@ def setup_shell_routes() -> APIRouter: "sglang[all]", "diffusers", "diffusers[torch]", + "transformers", "TTS", "bark", "faster-whisper", diff --git a/static/js/cookbook.js b/static/js/cookbook.js index 2abb263ba..cd1ae1b1f 100644 --- a/static/js/cookbook.js +++ b/static/js/cookbook.js @@ -597,7 +597,8 @@ export function _buildServeCmd(f, modelName, backend) { } else if (backend === 'diffusers') { const gpuStr = f.gpus?.trim(); if (gpuStr) cmd += `CUDA_VISIBLE_DEVICES=${gpuStr} `; - cmd += `python3 scripts/diffusion_server.py --model ${modelName} --port ${f.port || '8100'}`; + const diffusersPy = _isWindows() ? 'python' : _py3Bin; + cmd += `${diffusersPy} scripts/diffusion_server.py --model ${modelName} --port ${f.port || '8100'}`; if (f.diff_dtype && f.diff_dtype !== 'bfloat16') cmd += ` --dtype ${f.diff_dtype}`; if (f.diff_device_map && f.diff_device_map !== 'balanced') cmd += ` --device-map ${f.diff_device_map}`; if (f.diff_steps) cmd += ` --steps ${f.diff_steps}`; @@ -718,7 +719,7 @@ async function _fetchDependencies() { const data = await resp.json(); const pkgs = data.packages || []; if (!pkgs.length) { list.innerHTML = '
No packages found
'; return; } - const _winUnsupported = new Set(['diffusers', 'hf_transfer', 'vllm', 'rembg', 'gfpgan']); + const _winUnsupported = new Set(['hf_transfer', 'vllm', 'rembg', 'gfpgan']); const _statusTag = (pkg, isLocal, isSystemDep, winBlocked) => { if (winBlocked) return `N/A`; diff --git a/static/js/cookbookServe.js b/static/js/cookbookServe.js index 2a5cc5b5b..fe792554f 100644 --- a/static/js/cookbookServe.js +++ b/static/js/cookbookServe.js @@ -530,7 +530,7 @@ function _rerenderCachedModels() { : (_lastUsed || (_isLegacyFlat ? _allSs : {})); const detectedBackend = _detectBackend(m).backend; const _allowedBackends = new Set(_isWindows() - ? ['llamacpp'] + ? ['llamacpp', 'diffusers'] : (_isMetal() ? ['llamacpp', 'ollama'] : ['vllm', 'sglang', 'llamacpp', 'ollama', 'diffusers'])); const defaultBackend = (ss._forceBackend && ss.backend && _allowedBackends.has(ss.backend)) ? ss.backend @@ -590,7 +590,7 @@ function _rerenderCachedModels() { // Row 1: Backend + Server + Env panelHtml += `
`; const _backendChoices = _isWindows() - ? [['llamacpp','llama.cpp']] + ? [['llamacpp','llama.cpp'],['diffusers','Diffusers']] : _isMetal() // Diffusers (diffusion_server.py) is CUDA-only — omit it on Metal. ? [['llamacpp','llama.cpp'],['ollama','Ollama']] diff --git a/tests/test_cookbook_cpu_only_serve.py b/tests/test_cookbook_cpu_only_serve.py index ad4b795f8..b46c3e080 100644 --- a/tests/test_cookbook_cpu_only_serve.py +++ b/tests/test_cookbook_cpu_only_serve.py @@ -15,6 +15,7 @@ import re from pathlib import Path SRC = Path(__file__).resolve().parent.parent / "static/js/cookbook.js" +SERVE_SRC = Path(__file__).resolve().parent.parent / "static/js/cookbookServe.js" def test_cpu_only_drops_gpu_only_flags(): @@ -28,3 +29,25 @@ def test_cpu_only_drops_gpu_only_flags(): # The CUDA unified-memory env must be suppressed for CPU-only too. assert "f.unified_mem && !_cpuOnly" in text, \ "GGML_CUDA_ENABLE_UNIFIED_MEMORY must be gated on !_cpuOnly" + + +def test_diffusers_is_not_blocked_on_windows_dependencies_panel(): + text = SRC.read_text(encoding="utf-8") + + assert "const _winUnsupported = new Set(['hf_transfer', 'vllm', 'rembg', 'gfpgan']);" in text + assert "new Set(['diffusers'" not in text + + +def test_diffusers_is_available_on_windows_serve_panel(): + text = SERVE_SRC.read_text(encoding="utf-8") + + assert "? ['llamacpp', 'diffusers']" in text + assert "? [['llamacpp','llama.cpp'],['diffusers','Diffusers']]" in text + + +def test_windows_diffusers_uses_python_not_python3(): + text = SRC.read_text(encoding="utf-8") + + assert "const diffusersPy = _isWindows() ? 'python' : _py3Bin;" in text + assert "cmd += `${diffusersPy} scripts/diffusion_server.py" in text + assert "cmd += `python3 scripts/diffusion_server.py" not in text diff --git a/tests/test_cookbook_package_detection.py b/tests/test_cookbook_package_detection.py index 32aa7c93f..bf4378d07 100644 --- a/tests/test_cookbook_package_detection.py +++ b/tests/test_cookbook_package_detection.py @@ -23,6 +23,7 @@ def test_llama_cpp_maps_to_llama_cpp_python_distribution(): def test_extras_and_version_markers_are_stripped(): assert _pip_dist_name({"name": "diffusers", "pip": "diffusers[torch]"}) == "diffusers" + assert _pip_dist_name({"name": "transformers", "pip": "transformers"}) == "transformers" assert _pip_dist_name({"name": "sglang", "pip": "sglang[all]"}) == "sglang" assert _pip_dist_name({"name": "rembg", "pip": "rembg[gpu]"}) == "rembg" assert _pip_dist_name({"name": "x", "pip": "foo>=1.2,<2"}) == "foo" @@ -48,3 +49,11 @@ def test_route_uses_dist_name_helper_not_munged_import_name(): src = (Path(__file__).resolve().parents[1] / "routes" / "shell_routes.py").read_text(encoding="utf-8") assert "importlib_metadata.version(_pip_dist_name(pkg))" in src assert 'importlib_metadata.version(pkg["name"].replace("_", "-"))' not in src + + +def test_transformers_is_listed_as_image_dependency(): + src = (Path(__file__).resolve().parents[1] / "routes" / "shell_routes.py").read_text(encoding="utf-8") + + assert '"name": "transformers"' in src + assert '"pip": "transformers"' in src + assert '"transformers",' in src