mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-11-03 19:52:20 +01:00
The recipe Run button sent repo_id "<backend> setup"; the space fails the pip package-spec check, so every Run returned "Invalid pip package name". It now sends "<backend>-setup". The vLLM/SGLang recipes start with `uv pip install ... --torch-backend auto`, but uv was not in the serve command allowlist. Allow uv only for the `pip install` subcommand. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
f814018098
commit
631c05603c
@@ -728,8 +728,13 @@ def _check_serve_binary(seg: str) -> None:
|
|||||||
if not tokens:
|
if not tokens:
|
||||||
return
|
return
|
||||||
env_re = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
|
env_re = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
|
||||||
first = next((t for t in tokens if not env_re.match(t)), "")
|
idx = next((i for i, t in enumerate(tokens) if not env_re.match(t)), len(tokens))
|
||||||
|
first = tokens[idx] if idx < len(tokens) else ""
|
||||||
base = os.path.basename(first)
|
base = os.path.basename(first)
|
||||||
|
# The vLLM/SGLang Dependencies recipes install with `uv pip install …
|
||||||
|
# --torch-backend auto`. Allow uv for that subcommand only, not `uv run`.
|
||||||
|
if base == "uv" and tokens[idx + 1:idx + 3] == ["pip", "install"]:
|
||||||
|
return
|
||||||
if base not in _SERVE_CMD_ALLOWLIST:
|
if base not in _SERVE_CMD_ALLOWLIST:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
400,
|
400,
|
||||||
|
|||||||
@@ -1762,7 +1762,9 @@ async function _fetchDependencies() {
|
|||||||
envPrefix = 'eval "$(conda shell.bash hook)" && conda activate ' + _shellQuote(_envState.envPath);
|
envPrefix = 'eval "$(conda shell.bash hook)" && conda activate ' + _shellQuote(_envState.envPath);
|
||||||
}
|
}
|
||||||
const reqBody = {
|
const reqBody = {
|
||||||
repo_id: `${backend} setup`,
|
// The server validates repo_id as a pip package spec for pip
|
||||||
|
// installs, so it can't contain a space.
|
||||||
|
repo_id: `${backend}-setup`,
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
remote_host: _envState.remoteHost || undefined,
|
remote_host: _envState.remoteHost || undefined,
|
||||||
ssh_port: _getPort(_envState.remoteHost) || undefined,
|
ssh_port: _getPort(_envState.remoteHost) || undefined,
|
||||||
@@ -1780,7 +1782,7 @@ async function _fetchDependencies() {
|
|||||||
uiModule.showToast('Run failed: ' + String(data.detail || data.error || `HTTP ${res.status}`).slice(0, 200));
|
uiModule.showToast('Run failed: ' + String(data.detail || data.error || `HTTP ${res.status}`).slice(0, 200));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const payload = { repo_id: `${backend} setup`, _cmd: cmd, remote_host: _envState.remoteHost || '', _dep: true };
|
const payload = { repo_id: reqBody.repo_id, _cmd: cmd, remote_host: _envState.remoteHost || '', _dep: true };
|
||||||
_addTask(data.session_id, `${backend} setup`, 'download', payload);
|
_addTask(data.session_id, `${backend} setup`, 'download', payload);
|
||||||
uiModule.showToast(`Running ${backend} setup on ${targetHost}…`);
|
uiModule.showToast(`Running ${backend} setup on ${targetHost}…`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -1026,3 +1027,36 @@ def test_validate_serve_cmd_rejects_unrelated_subshell_pipelines():
|
|||||||
]:
|
]:
|
||||||
with pytest.raises(HTTPException):
|
with pytest.raises(HTTPException):
|
||||||
_validate_serve_cmd(cmd)
|
_validate_serve_cmd(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def _dependency_recipe_pip_commands():
|
||||||
|
src = (Path(__file__).resolve().parents[1] / "static" / "js" / "cookbook-deps-recipes.js").read_text(encoding="utf-8")
|
||||||
|
return re.findall(r"pip:\s*\{\s*commands:\s*\['((?:[^'\\]|\\.)*)'\]\s*\}", src)
|
||||||
|
|
||||||
|
|
||||||
|
def test_validate_serve_cmd_accepts_single_line_dependency_recipes():
|
||||||
|
# The Dependencies recipe "Run" button posts these to /api/model/serve.
|
||||||
|
cmds = _dependency_recipe_pip_commands()
|
||||||
|
assert 'uv pip install -U vllm --torch-backend auto' in cmds
|
||||||
|
assert 'uv pip install -U "sglang[all]" --torch-backend auto' in cmds
|
||||||
|
for cmd in cmds:
|
||||||
|
assert _validate_serve_cmd(cmd) == cmd
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("cmd", [
|
||||||
|
"uv run python -c 'print(1)'",
|
||||||
|
"uv tool install ruff",
|
||||||
|
"uv pip uninstall vllm",
|
||||||
|
"uv",
|
||||||
|
])
|
||||||
|
def test_validate_serve_cmd_allows_uv_only_for_pip_install(cmd):
|
||||||
|
with pytest.raises(HTTPException):
|
||||||
|
_validate_serve_cmd(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dependency_recipe_run_sends_a_valid_pip_repo_id():
|
||||||
|
# A pip-install repo_id must match the server's package-spec regex; the old
|
||||||
|
# "<backend> setup" value had a space and every Run got "Invalid pip package name".
|
||||||
|
src = (Path(__file__).resolve().parents[1] / "static" / "js" / "cookbook.js").read_text(encoding="utf-8")
|
||||||
|
assert "repo_id: `${backend}-setup`," in src
|
||||||
|
assert "repo_id: `${backend} setup`" not in src
|
||||||
|
|||||||
Reference in New Issue
Block a user