From 573d43139914229f92e6c2f4951ec36cbdf77f55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o?= Date: Sun, 7 Jun 2026 13:20:05 +0200 Subject: [PATCH] fix(cookbook): don't infer server OS from the browser's user-agent (#3223) _getPlatform('local') fell back to navigator.userAgent to decide the *server's* platform. On a Mac/Linux homeserver opened from a Windows browser this returned 'windows', so the GGUF serve builder emitted the Windows python-only shape (`python -m llama_cpp.server`, no `llama-server ||` fallback). That command fails on the Unix host with "No module named llama_cpp" even though native llama-server is installed, and the diagnosis then misleadingly tells the user to pip-install llama-cpp-python. Trust the server-side hardware probe over the user-agent: a non-empty probe backend (metal/cuda/rocm/cpu_*) means a Unix server; local Windows instead carries platform:"windows" which already sets _envState.platform and short-circuits. Only fall back to the browser hint when there is no server-side signal at all. Keeps #1389/#2961's local-Windows path intact. Fixes #3221 Co-authored-by: Claude Opus 4.8 --- static/js/cookbook.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/static/js/cookbook.js b/static/js/cookbook.js index 6e710c1bd..e12f56941 100644 --- a/static/js/cookbook.js +++ b/static/js/cookbook.js @@ -162,13 +162,27 @@ function _getPort(hostOrTask) { /** Get platform for a given host (or task object). Returns 'windows', 'termux', 'linux', or '' */ export function _getPlatform(hostOrTask) { const isWinBrowser = (window.navigator.userAgent || window.navigator.platform || '').toLowerCase().includes('win'); + // The browser's OS is NOT the server's OS when the UI is opened remotely — + // e.g. a Windows browser driving a Mac/Linux homeserver. Trusting the + // user-agent there makes the serve builder emit the Windows python-only + // shape (`python -m llama_cpp.server`, no `llama-server ||` fallback), which + // then fails on the actual Unix server. The local hardware probe is + // authoritative: it reports a backend (metal/cuda/rocm/cpu_*) for any Unix + // server and carries platform:"windows" for local Windows (which sets + // _envState.platform, short-circuiting below). So only fall back to the + // browser hint when we have no server-side signal at all. + const localPlatform = () => { + if (_envState.platform) return _envState.platform; + if (String(_hwfitCache?.system?.backend || '')) return ''; + return isWinBrowser ? 'windows' : ''; + }; if (!hostOrTask || hostOrTask === 'local') { - return _envState.platform || (isWinBrowser ? 'windows' : ''); + return localPlatform(); } if (typeof hostOrTask === 'object') { const h = hostOrTask.remoteHost; if (!h || h === 'local') { - return hostOrTask.platform || _envState.platform || (isWinBrowser ? 'windows' : ''); + return hostOrTask.platform || localPlatform(); } return hostOrTask.platform || _getPlatform(h); }