mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 18:25:26 -04:00
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}; }}"
|
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:
|
def _cached_model_scan_script(model_dirs: list[str] | None = None) -> str:
|
||||||
"""Build the standalone Python scanner used by /api/model/cached."""
|
"""Build the standalone Python scanner used by /api/model/cached."""
|
||||||
lines = [
|
lines = [
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ from routes.cookbook_helpers import (
|
|||||||
_ps_squote, _bash_squote, _validate_serve_cmd, _parse_serve_phase,
|
_ps_squote, _bash_squote, _validate_serve_cmd, _parse_serve_phase,
|
||||||
_safe_env_prefix, _local_tooling_path_export, _append_serve_preflight_exit_lines,
|
_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,
|
_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 = (
|
_HF_TOKEN_STATUS_SNIPPET = (
|
||||||
@@ -819,6 +820,11 @@ def setup_cookbook_routes() -> APIRouter:
|
|||||||
# many downstream `"engine" in req.cmd` membership checks can't hit
|
# many downstream `"engine" in req.cmd` membership checks can't hit
|
||||||
# `TypeError: argument of type 'NoneType'` (a 500 instead of a clean 400).
|
# `TypeError: argument of type 'NoneType'` (a 500 instead of a clean 400).
|
||||||
req.cmd = _validate_serve_cmd(req.cmd) or ""
|
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)
|
is_pip_install = bool(req.cmd and "pip install" in req.cmd)
|
||||||
if is_pip_install:
|
if is_pip_install:
|
||||||
# PEP-508-style package spec — letters, digits, `.-_` for the
|
# PEP-508-style package spec — letters, digits, `.-_` for the
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from routes.cookbook_helpers import (
|
|||||||
_pip_install_fallback_chain,
|
_pip_install_fallback_chain,
|
||||||
_ollama_bind_from_cmd,
|
_ollama_bind_from_cmd,
|
||||||
_safe_env_prefix,
|
_safe_env_prefix,
|
||||||
|
_venv_safe_local_pip_install_cmd,
|
||||||
_validate_gpus,
|
_validate_gpus,
|
||||||
_validate_repo_id,
|
_validate_repo_id,
|
||||||
_validate_serve_cmd,
|
_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"
|
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():
|
def test_pip_install_attempt_wraps_in_status_preserving_subshell():
|
||||||
"""Each pip attempt must be a bash -c subshell that captures output,
|
"""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."""
|
prints tail, cleans up, and exits with pip's real status — not tail's."""
|
||||||
|
|||||||
Reference in New Issue
Block a user