Fix local Cookbook dependency installs in venvs (#1082)

This commit is contained in:
red person
2026-06-02 16:39:02 +03:00
committed by GitHub
parent 68efa8ee53
commit 028a39b42c
3 changed files with 46 additions and 1 deletions

View File

@@ -202,6 +202,34 @@ def _pip_install_fallback_chain(package: str, *, python_cmd: str = "python3 -m p
return f"{base} || {{ ! {venv_check} && {user}; }}"
def _venv_safe_local_pip_install_cmd(cmd: str, *, local: bool, in_venv: bool) -> str:
"""Drop pip user-install flags that are invalid for local venv installs.
Cookbook dependency installs run through the model-serve task path so users
can watch progress in the same log UI. For local POSIX runs, that task
prepends Odysseus' own interpreter directory to PATH. If Odysseus itself is
running from a venv, `python3` resolves to the venv Python and pip rejects
`--user` with "User site-packages are not visible in this virtualenv".
Keep remote and non-venv installs unchanged: remotes may intentionally use
system Python, and Docker/non-venv installs still need user-site fallback.
"""
if not local or not in_venv:
return cmd
if "pip install" not in (cmd or ""):
return cmd
try:
parts = shlex.split(cmd)
except ValueError:
return cmd
stripped = [
part
for part in parts
if part not in {"--user", "--break-system-packages"}
]
return shlex.join(stripped)
def _cached_model_scan_script(model_dirs: list[str] | None = None) -> str:
"""Build the standalone Python scanner used by /api/model/cached."""
lines = [

View File

@@ -38,7 +38,8 @@ 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, ModelDownloadRequest, ServeRequest,
_ollama_bind_from_cmd, _pip_install_fallback_chain, _venv_safe_local_pip_install_cmd,
ModelDownloadRequest, ServeRequest,
)
_HF_TOKEN_STATUS_SNIPPET = (
@@ -819,6 +820,11 @@ def setup_cookbook_routes() -> APIRouter:
# many downstream `"engine" in req.cmd` membership checks can't hit
# `TypeError: argument of type 'NoneType'` (a 500 instead of a clean 400).
req.cmd = _validate_serve_cmd(req.cmd) or ""
req.cmd = _venv_safe_local_pip_install_cmd(
req.cmd,
local=not bool(req.remote_host),
in_venv=sys.prefix != sys.base_prefix,
)
is_pip_install = bool(req.cmd and "pip install" in req.cmd)
if is_pip_install:
# PEP-508-style package spec — letters, digits, `.-_` for the

View File

@@ -15,6 +15,7 @@ from routes.cookbook_helpers import (
_pip_install_fallback_chain,
_ollama_bind_from_cmd,
_safe_env_prefix,
_venv_safe_local_pip_install_cmd,
_validate_gpus,
_validate_repo_id,
_validate_serve_cmd,
@@ -157,6 +158,16 @@ def test_pip_install_fallback_chain_tries_user_outside_venv():
assert "user_attempt" in result.stdout, "Chain should try --user when not in venv and base fails"
def test_venv_safe_local_pip_install_strips_user_flags_only_for_local_venv():
cmd = 'python3 -m pip install -U --user --break-system-packages "vllm"'
cleaned = _venv_safe_local_pip_install_cmd(cmd, local=True, in_venv=True)
assert cleaned == "python3 -m pip install -U vllm"
assert _venv_safe_local_pip_install_cmd(cmd, local=False, in_venv=True) == cmd
assert _venv_safe_local_pip_install_cmd(cmd, local=True, in_venv=False) == cmd
def test_pip_install_attempt_wraps_in_status_preserving_subshell():
"""Each pip attempt must be a bash -c subshell that captures output,
prints tail, cleans up, and exits with pip's real status — not tail's."""