From ffb8fd16bc16db337b38671ac1b80d64a22fad75 Mon Sep 17 00:00:00 2001 From: lekt8 Date: Wed, 3 Jun 2026 13:23:49 +0800 Subject: [PATCH] Disable pip cache for Cookbook dependency installs (off the home disk) (#1477) Cookbook dependency installs (vLLM and friends) build large wheels; pip's default cache lives under $HOME/.cache/pip, so on a small home filesystem the build dies mid-way with "[Errno 28] No space left on device" (issue #1219) and the dependency ends up "installed" but unusable (issue #1459). Add `--no-cache-dir` to the dependency pip-install command (the maintainer's suggested PIP_CACHE_DIR= workaround, made the default) via a small _pip_install_no_cache() helper applied at the install chokepoint. Consistent with the existing --no-cache-dir on the llama-cpp-python build. Idempotent; non-pip-install serve commands are untouched. Co-authored-by: Claude Opus 4.8 (1M context) --- routes/cookbook_helpers.py | 15 +++++++++++++++ routes/cookbook_routes.py | 6 +++++- tests/test_cookbook_helpers.py | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/routes/cookbook_helpers.py b/routes/cookbook_helpers.py index 7ff2b70..ece63ee 100644 --- a/routes/cookbook_helpers.py +++ b/routes/cookbook_helpers.py @@ -148,6 +148,21 @@ def _local_tooling_path_export(executable: str) -> str: return f'export PATH="{esc}:$PATH"' +def _pip_install_no_cache(cmd: str) -> str: + """Add ``--no-cache-dir`` to a pip install command. + + Cookbook dependency installs (vLLM, llama-cpp-python, …) build large wheels; + pip's default cache lives under ``$HOME/.cache/pip`` and these builds can fill + a small home filesystem with ``[Errno 28] No space left on device`` mid-build + (issue #1219), leaving the dependency "installed" but unusable (#1459). + Disabling the cache for these one-off installs keeps them off the home disk + (the maintainer's suggested ``PIP_CACHE_DIR=`` workaround, made the default). + Idempotent; leaves non-pip-install commands untouched.""" + if not cmd or "pip install" not in cmd or "--no-cache-dir" in cmd: + return cmd + return cmd.replace("pip install", "pip install --no-cache-dir", 1) + + def _pip_install_attempt(pip_cmd: str) -> str: """Wrap a single pip install command so its exit status survives the fallback chain and its stderr is visible in the tmux log on failure. diff --git a/routes/cookbook_routes.py b/routes/cookbook_routes.py index e1e4972..5aa8ed0 100644 --- a/routes/cookbook_routes.py +++ b/routes/cookbook_routes.py @@ -38,7 +38,7 @@ from routes.cookbook_helpers import ( _ps_squote, _bash_squote, _validate_serve_cmd, _parse_serve_phase, _safe_env_prefix, _local_tooling_path_export, _append_serve_preflight_exit_lines, _append_serve_exit_code_lines, _append_llama_cpp_linux_accel_build_lines, _cached_model_scan_script, - _ollama_bind_from_cmd, _pip_install_fallback_chain, _venv_safe_local_pip_install_cmd, + _ollama_bind_from_cmd, _pip_install_fallback_chain, _pip_install_no_cache, _venv_safe_local_pip_install_cmd, ModelDownloadRequest, ServeRequest, ) @@ -841,6 +841,10 @@ def setup_cookbook_routes() -> APIRouter: ) is_pip_install = bool(req.cmd and "pip install" in req.cmd) if is_pip_install: + # Keep big dependency wheel builds (vLLM, …) off the home filesystem's + # pip cache so they don't fail mid-build with "No space left" (#1219) + # and leave the dep installed-but-unusable (#1459). + req.cmd = _pip_install_no_cache(req.cmd) # PEP-508-style package spec — letters, digits, `.-_` for the # name; `[` `]` for extras; `<>=!~,` for version specifiers. # v2 review HIGH-14: tightened from the previous regex which diff --git a/tests/test_cookbook_helpers.py b/tests/test_cookbook_helpers.py index ae73e92..2421201 100644 --- a/tests/test_cookbook_helpers.py +++ b/tests/test_cookbook_helpers.py @@ -415,3 +415,23 @@ def test_cached_model_scan_reports_plain_dir_gguf(tmp_path): assert ggufs[1]["size_bytes"] == len(b"part1part2part3") assert ggufs[2]["quant"] == "Q6_K_XL" assert ggufs[3]["quant"] == "BF16" + + +# ── #1219 / #1459: keep big dependency wheel builds off the home pip cache ── + +def test_pip_install_no_cache_injects_flag(): + from routes.cookbook_helpers import _pip_install_no_cache + assert _pip_install_no_cache("python -m pip install vllm") == \ + "python -m pip install --no-cache-dir vllm" + assert _pip_install_no_cache("pip install -q huggingface-hub") == \ + "pip install --no-cache-dir -q huggingface-hub" + + +def test_pip_install_no_cache_is_idempotent_and_scoped(): + from routes.cookbook_helpers import _pip_install_no_cache + # already present -> unchanged + already = "pip install --no-cache-dir vllm" + assert _pip_install_no_cache(already) == already + # not a pip install -> unchanged + assert _pip_install_no_cache("vllm serve --model x") == "vllm serve --model x" + assert _pip_install_no_cache("") == ""