diff --git a/routes/codex_routes.py b/routes/codex_routes.py index e11965c35..52ff2949a 100644 --- a/routes/codex_routes.py +++ b/routes/codex_routes.py @@ -46,8 +46,12 @@ def _ssh_prefix_for_task(task: dict) -> tuple[str, str]: shell metacharacters in ``remoteHost`` is rejected with 400 rather than injected. """ - host = validate_remote_host((task.get("remoteHost") or "").strip() or None) or "" - ssh_port = validate_ssh_port((task.get("sshPort") or "").strip() or None) or "" + raw_host = task.get("remoteHost") + raw_port = task.get("sshPort") + host_value = str(raw_host).strip() if raw_host is not None else None + port_value = str(raw_port).strip() if raw_port is not None else None + host = validate_remote_host(host_value or None) or "" + ssh_port = validate_ssh_port(port_value or None) or "" port_flag = f"-p {ssh_port} " if ssh_port and ssh_port != "22" else "" return host, port_flag diff --git a/routes/cookbook_routes.py b/routes/cookbook_routes.py index af25dd8e8..ea15a22c3 100644 --- a/routes/cookbook_routes.py +++ b/routes/cookbook_routes.py @@ -1284,6 +1284,11 @@ def setup_cookbook_routes() -> APIRouter: # LOCAL execution on a native-Windows host never uses tmux (detached # process path below), regardless of the UI-supplied platform. local_windows = IS_WINDOWS and not remote + if is_windows and remote and "diffusion_server.py" in req.cmd: + raise HTTPException( + 400, + "Remote Windows Diffusers serving is not supported yet; use local Windows or a Linux remote server.", + ) if not is_windows and not local_windows and not await _binary_available("tmux", remote, req.ssh_port): return { diff --git a/static/js/cookbookServe.js b/static/js/cookbookServe.js index f3b5842b2..33d56ef3c 100644 --- a/static/js/cookbookServe.js +++ b/static/js/cookbookServe.js @@ -116,13 +116,28 @@ function _selectedServeTarget(panel) { : (server?.name || 'local server'); return { host, - port: host ? (_getPort(host) || server?.port || '') : '', + port: host ? (server?.port || _getPort(host) || '') : '', + env: server?.env || '', venv, platform: server?.platform || _envState.platform || '', label, }; } +function _remoteWindowsDiffusersUnsupported(target) { + return !!(target?.host && target?.platform === 'windows'); +} + +function _backendChoicesForTarget(target) { + if (target?.platform === 'windows') { + if (_remoteWindowsDiffusersUnsupported(target)) return [['llamacpp','llama.cpp']]; + return [['llamacpp','llama.cpp'],['diffusers','Diffusers']]; + } + return _isMetal() + ? [['llamacpp','llama.cpp'],['ollama','Ollama']] + : [['vllm','vLLM'],['sglang','SGLang'],['llamacpp','llama.cpp'],['ollama','Ollama'],['diffusers','Diffusers']]; +} + async function _fetchServeRuntimePackage(panel, backend) { const packageByBackend = { vllm: 'vllm', @@ -529,13 +544,14 @@ function _rerenderCachedModels() { const ss = (_byRepo[repo] && typeof _byRepo[repo] === 'object') ? _byRepo[repo] : (_lastUsed || (_isLegacyFlat ? _allSs : {})); + const _serveTarget = _selectedServeTarget(); + const _backendChoices = _backendChoicesForTarget(_serveTarget); + const _allowedBackends = new Set(_backendChoices.map(([v]) => v)); const detectedBackend = _detectBackend(m).backend; - const _allowedBackends = new Set(_isWindows() - ? ['llamacpp', 'diffusers'] - : (_isMetal() ? ['llamacpp', 'ollama'] : ['vllm', 'sglang', 'llamacpp', 'ollama', 'diffusers'])); - const defaultBackend = (ss._forceBackend && ss.backend && _allowedBackends.has(ss.backend)) + let defaultBackend = (ss._forceBackend && ss.backend && _allowedBackends.has(ss.backend)) ? ss.backend : detectedBackend; + if (!_allowedBackends.has(defaultBackend)) defaultBackend = _backendChoices[0]?.[0] || detectedBackend; const savedMatchesBackend = !!ss._forceBackend || (ss.backend || 'vllm') === detectedBackend; const sv = (k, def) => (ss[k] !== undefined && savedMatchesBackend) ? ss[k] : def; const defaultTp = defaultBackend === 'llamacpp' ? '1' : sv('tp', '1'); @@ -607,12 +623,6 @@ function _rerenderCachedModels() { } // Row 1: Backend + Server + Env panelHtml += `
`; - const _backendChoices = _isWindows() - ? [['llamacpp','llama.cpp'],['diffusers','Diffusers']] - : _isMetal() - // Diffusers (diffusion_server.py) is CUDA-only — omit it on Metal. - ? [['llamacpp','llama.cpp'],['ollama','Ollama']] - : [['vllm','vLLM'],['sglang','SGLang'],['llamacpp','llama.cpp'],['ollama','Ollama'],['diffusers','Diffusers']]; const backendOpts = _backendChoices.map(([v,l]) => ``).join(''); // Custom Backend picker — native