Fix local Cookbook dependency installs in venvs (#1082)
This commit is contained in:
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user