mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-06-17 10:15:27 -04:00
fix: report serve dependency readiness (#412)
This commit is contained in:
+131
-10
@@ -92,6 +92,115 @@ def _docker_row_status(*, on_remote, in_container, installed, default_hint):
|
|||||||
return DockerRowStatus(applicable=True, install_hint=default_hint)
|
return DockerRowStatus(applicable=True, install_hint=default_hint)
|
||||||
|
|
||||||
|
|
||||||
|
def _package_installed_from_probe(name: str, probe: dict) -> bool:
|
||||||
|
"""Return whether an optional dependency is usable by Cookbook.
|
||||||
|
|
||||||
|
A Python import alone is not enough: namespace packages can be created by a
|
||||||
|
same-named directory, and vLLM serving needs the CLI on PATH. Keep this
|
||||||
|
aligned with the actual serve command each backend launches.
|
||||||
|
"""
|
||||||
|
binaries = probe.get("binaries") if isinstance(probe.get("binaries"), dict) else {}
|
||||||
|
dists = probe.get("dists") if isinstance(probe.get("dists"), dict) else {}
|
||||||
|
modules = probe.get("modules") if isinstance(probe.get("modules"), dict) else {}
|
||||||
|
|
||||||
|
if name == "vllm":
|
||||||
|
return bool(binaries.get("vllm"))
|
||||||
|
if name == "llama_cpp":
|
||||||
|
return bool(binaries.get("llama-server") or dists.get("llama-cpp-python"))
|
||||||
|
if name == "sglang":
|
||||||
|
return bool(dists.get("sglang") or modules.get("sglang", {}).get("real_module"))
|
||||||
|
if name == "diffusers":
|
||||||
|
return bool(
|
||||||
|
(dists.get("diffusers") or modules.get("diffusers", {}).get("real_module"))
|
||||||
|
and (dists.get("torch") or modules.get("torch", {}).get("real_module"))
|
||||||
|
)
|
||||||
|
if name == "hf_transfer":
|
||||||
|
return bool(dists.get("hf-transfer") or modules.get("hf_transfer", {}).get("real_module"))
|
||||||
|
return bool(dists.get(name) or modules.get(name, {}).get("real_module"))
|
||||||
|
|
||||||
|
|
||||||
|
def _package_status_note(name: str, probe: dict) -> str:
|
||||||
|
binaries = probe.get("binaries") if isinstance(probe.get("binaries"), dict) else {}
|
||||||
|
modules = probe.get("modules") if isinstance(probe.get("modules"), dict) else {}
|
||||||
|
dists = probe.get("dists") if isinstance(probe.get("dists"), dict) else {}
|
||||||
|
module = modules.get(name) if isinstance(modules.get(name), dict) else {}
|
||||||
|
locations = module.get("locations") or []
|
||||||
|
if name == "vllm":
|
||||||
|
if binaries.get("vllm"):
|
||||||
|
return f"vLLM CLI: {binaries['vllm']}"
|
||||||
|
if module.get("found") and not dists.get("vllm"):
|
||||||
|
loc = locations[0] if locations else module.get("origin") or "unknown path"
|
||||||
|
return f"Python sees a vllm namespace at {loc}, but no vLLM CLI is on PATH."
|
||||||
|
return "vLLM CLI not found on PATH."
|
||||||
|
if name == "llama_cpp":
|
||||||
|
parts = []
|
||||||
|
if binaries.get("llama-server"):
|
||||||
|
parts.append(f"native llama-server: {binaries['llama-server']}")
|
||||||
|
if dists.get("llama-cpp-python"):
|
||||||
|
parts.append(f"python package: llama-cpp-python {dists['llama-cpp-python']}")
|
||||||
|
return "; ".join(parts) if parts else "No native llama-server or llama-cpp-python server package found."
|
||||||
|
if name == "diffusers":
|
||||||
|
if _package_installed_from_probe(name, probe):
|
||||||
|
return f"diffusers {dists.get('diffusers', 'available')} with torch {dists.get('torch', 'available')}"
|
||||||
|
return "Diffusers serving needs both diffusers and torch."
|
||||||
|
if name in dists:
|
||||||
|
return f"{name} {dists[name]}"
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def _package_probe_script(names: list[str]) -> str:
|
||||||
|
names_lit = ",".join(repr(n) for n in names)
|
||||||
|
return f"""
|
||||||
|
import importlib.util
|
||||||
|
import importlib.metadata as md
|
||||||
|
import json
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
names=[{names_lit}]
|
||||||
|
dist_names={{
|
||||||
|
'vllm':['vllm'],
|
||||||
|
'llama_cpp':['llama-cpp-python'],
|
||||||
|
'sglang':['sglang'],
|
||||||
|
'diffusers':['diffusers','torch'],
|
||||||
|
'hf_transfer':['hf-transfer','hf_transfer'],
|
||||||
|
}}
|
||||||
|
bin_names={{
|
||||||
|
'vllm':['vllm'],
|
||||||
|
'llama_cpp':['llama-server'],
|
||||||
|
}}
|
||||||
|
|
||||||
|
def mod_status(n):
|
||||||
|
spec = importlib.util.find_spec(n)
|
||||||
|
loader = getattr(spec, 'loader', None) if spec else None
|
||||||
|
return {{
|
||||||
|
'found': bool(spec),
|
||||||
|
'origin': getattr(spec, 'origin', None) if spec else None,
|
||||||
|
'loader': type(loader).__name__ if loader else None,
|
||||||
|
'locations': list(getattr(spec, 'submodule_search_locations', []) or []),
|
||||||
|
'real_module': bool(spec and loader),
|
||||||
|
}}
|
||||||
|
|
||||||
|
def dist_status(ds):
|
||||||
|
out = {{}}
|
||||||
|
for d in ds:
|
||||||
|
try:
|
||||||
|
out[d] = md.version(d)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return out
|
||||||
|
|
||||||
|
def probe(n):
|
||||||
|
mods = {{n: mod_status(n)}}
|
||||||
|
if n == 'diffusers':
|
||||||
|
mods['torch'] = mod_status('torch')
|
||||||
|
dists = dist_status(dist_names.get(n, [n]))
|
||||||
|
bins = {{b: shutil.which(b) for b in bin_names.get(n, [])}}
|
||||||
|
return {{'modules': mods, 'dists': dists, 'binaries': bins}}
|
||||||
|
|
||||||
|
print(json.dumps({{n: probe(n) for n in names}}))
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
def _find_line_break(buf):
|
def _find_line_break(buf):
|
||||||
"""Find next line terminator in buffer. Returns (index, separator_length) or (-1, 0)."""
|
"""Find next line terminator in buffer. Returns (index, separator_length) or (-1, 0)."""
|
||||||
ni = buf.find(b"\n")
|
ni = buf.find(b"\n")
|
||||||
@@ -646,7 +755,7 @@ def setup_shell_routes() -> APIRouter:
|
|||||||
never reflected because the check only ever looked at the local host.
|
never reflected because the check only ever looked at the local host.
|
||||||
"""
|
"""
|
||||||
_require_admin(request)
|
_require_admin(request)
|
||||||
import importlib, shlex, json as _json
|
import importlib, importlib.metadata as importlib_metadata, shlex, json as _json
|
||||||
port_arg = ""
|
port_arg = ""
|
||||||
if ssh_port and str(ssh_port).strip() not in ("", "22"):
|
if ssh_port and str(ssh_port).strip() not in ("", "22"):
|
||||||
_port = str(ssh_port).strip()
|
_port = str(ssh_port).strip()
|
||||||
@@ -672,18 +781,12 @@ def setup_shell_routes() -> APIRouter:
|
|||||||
# Remote check: for remote-target packages, probe the selected server's
|
# Remote check: for remote-target packages, probe the selected server's
|
||||||
# venv over SSH so a remote `pip install` actually reflects here.
|
# venv over SSH so a remote `pip install` actually reflects here.
|
||||||
remote_status: dict = {}
|
remote_status: dict = {}
|
||||||
|
remote_details: dict = {}
|
||||||
remote_names = [p["name"] for p in packages if p.get("target") == "remote" and p.get("kind") != "system"]
|
remote_names = [p["name"] for p in packages if p.get("target") == "remote" and p.get("kind") != "system"]
|
||||||
remote_system_names = [p["name"] for p in packages if p.get("target") == "remote" and p.get("kind") == "system"]
|
remote_system_names = [p["name"] for p in packages if p.get("target") == "remote" and p.get("kind") == "system"]
|
||||||
if host and remote_names:
|
if host and remote_names:
|
||||||
try:
|
try:
|
||||||
names_lit = ",".join(repr(n) for n in remote_names)
|
py = _package_probe_script(remote_names)
|
||||||
py = (
|
|
||||||
"import importlib.util,json,shutil;"
|
|
||||||
f"names=[{names_lit}];"
|
|
||||||
"status={n:(importlib.util.find_spec(n) is not None) for n in names};"
|
|
||||||
"status['llama_cpp']=status.get('llama_cpp',False) or shutil.which('llama-server') is not None;"
|
|
||||||
"print(json.dumps(status))"
|
|
||||||
)
|
|
||||||
src = ""
|
src = ""
|
||||||
if venv:
|
if venv:
|
||||||
act = venv if venv.endswith("/bin/activate") else venv.rstrip("/") + "/bin/activate"
|
act = venv if venv.endswith("/bin/activate") else venv.rstrip("/") + "/bin/activate"
|
||||||
@@ -705,7 +808,12 @@ def setup_shell_routes() -> APIRouter:
|
|||||||
for line in reversed(txt.splitlines()):
|
for line in reversed(txt.splitlines()):
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if line.startswith("{"):
|
if line.startswith("{"):
|
||||||
remote_status = _json.loads(line)
|
remote_details = _json.loads(line)
|
||||||
|
remote_status = {
|
||||||
|
name: _package_installed_from_probe(name, probe)
|
||||||
|
for name, probe in remote_details.items()
|
||||||
|
if isinstance(probe, dict)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
remote_status = {}
|
remote_status = {}
|
||||||
@@ -736,16 +844,29 @@ def setup_shell_routes() -> APIRouter:
|
|||||||
on_remote = bool(host and pkg.get("target") == "remote")
|
on_remote = bool(host and pkg.get("target") == "remote")
|
||||||
if on_remote:
|
if on_remote:
|
||||||
pkg["installed"] = bool(remote_status.get(pkg["name"], False))
|
pkg["installed"] = bool(remote_status.get(pkg["name"], False))
|
||||||
|
probe = remote_details.get(pkg["name"])
|
||||||
|
if isinstance(probe, dict):
|
||||||
|
pkg["details"] = probe
|
||||||
|
note = _package_status_note(pkg["name"], probe)
|
||||||
|
if note:
|
||||||
|
pkg["status_note"] = note
|
||||||
elif pkg.get("kind") == "system":
|
elif pkg.get("kind") == "system":
|
||||||
pkg["installed"] = shutil.which(pkg["name"]) is not None
|
pkg["installed"] = shutil.which(pkg["name"]) is not None
|
||||||
elif pkg["name"] == "llama_cpp" and shutil.which("llama-server"):
|
elif pkg["name"] == "llama_cpp" and shutil.which("llama-server"):
|
||||||
pkg["installed"] = True
|
pkg["installed"] = True
|
||||||
|
pkg["status_note"] = f"native llama-server: {shutil.which('llama-server')}"
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
importlib.import_module(pkg["name"])
|
importlib.import_module(pkg["name"])
|
||||||
|
if pkg["name"] == "vllm":
|
||||||
|
pkg["installed"] = shutil.which("vllm") is not None
|
||||||
|
else:
|
||||||
|
importlib_metadata.version(pkg["name"].replace("_", "-"))
|
||||||
pkg["installed"] = True
|
pkg["installed"] = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pkg["installed"] = False
|
pkg["installed"] = False
|
||||||
|
except importlib_metadata.PackageNotFoundError:
|
||||||
|
pkg["installed"] = False
|
||||||
|
|
||||||
if pkg["name"] == "docker":
|
if pkg["name"] == "docker":
|
||||||
status = _docker_row_status(
|
status = _docker_row_status(
|
||||||
|
|||||||
@@ -554,10 +554,12 @@ async function _fetchDependencies() {
|
|||||||
const isLocal = pkg.target === 'local';
|
const isLocal = pkg.target === 'local';
|
||||||
const isSystemDep = pkg.kind === 'system';
|
const isSystemDep = pkg.kind === 'system';
|
||||||
const winBlocked = !isLocal && _isWindows() && _winUnsupported.has(pkg.name);
|
const winBlocked = !isLocal && _isWindows() && _winUnsupported.has(pkg.name);
|
||||||
|
const note = pkg.status_note ? `<div class="memory-item-meta" style="font-size:10px;opacity:0.65;margin-top:3px;">${esc(pkg.status_note)}</div>` : '';
|
||||||
return `<div class="cookbook-dep-row${winBlocked ? ' cookbook-dep-blocked' : ''}" data-pkg-name="${esc(pkg.name)}" data-dep-pip="${esc(pkg.pip || '')}" data-dep-target="${isLocal ? 'local' : 'remote'}" data-dep-kind="${esc(pkg.kind || 'python')}">`
|
return `<div class="cookbook-dep-row${winBlocked ? ' cookbook-dep-blocked' : ''}" data-pkg-name="${esc(pkg.name)}" data-dep-pip="${esc(pkg.pip || '')}" data-dep-target="${isLocal ? 'local' : 'remote'}" data-dep-kind="${esc(pkg.kind || 'python')}">`
|
||||||
+ `<div class="cookbook-dep-info">`
|
+ `<div class="cookbook-dep-info">`
|
||||||
+ `<div class="memory-item-title">${esc(pkg.name)}</div>`
|
+ `<div class="memory-item-title">${esc(pkg.name)}</div>`
|
||||||
+ `<div class="memory-item-meta" style="font-size:10px;opacity:0.5;margin-top:2px;">${esc(pkg.desc)}</div>`
|
+ `<div class="memory-item-meta" style="font-size:10px;opacity:0.5;margin-top:2px;">${esc(pkg.desc)}</div>`
|
||||||
|
+ note
|
||||||
+ `</div>`
|
+ `</div>`
|
||||||
+ `<span class="cookbook-dep-tag cookbook-dep-cat">${esc(pkg.category)}</span>`
|
+ `<span class="cookbook-dep-tag cookbook-dep-cat">${esc(pkg.category)}</span>`
|
||||||
+ _statusTag(pkg, isLocal, isSystemDep, winBlocked)
|
+ _statusTag(pkg, isLocal, isSystemDep, winBlocked)
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ from routes.shell_routes import (
|
|||||||
_find_line_break,
|
_find_line_break,
|
||||||
_running_in_container,
|
_running_in_container,
|
||||||
_docker_row_status,
|
_docker_row_status,
|
||||||
|
_package_installed_from_probe,
|
||||||
|
_package_status_note,
|
||||||
DOCKER_IN_CONTAINER_HINT,
|
DOCKER_IN_CONTAINER_HINT,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -182,3 +184,60 @@ class TestDockerRowStatus:
|
|||||||
assert "remote" in lowered
|
assert "remote" in lowered
|
||||||
assert "socket" in lowered
|
assert "socket" in lowered
|
||||||
assert "host-root" in lowered or "host root" in lowered
|
assert "host-root" in lowered or "host root" in lowered
|
||||||
|
|
||||||
|
|
||||||
|
class TestPackageProbeStatus:
|
||||||
|
"""Dependency rows should reflect serve readiness, not import coincidences."""
|
||||||
|
|
||||||
|
def test_vllm_namespace_without_cli_is_not_installed(self):
|
||||||
|
probe = {
|
||||||
|
"modules": {
|
||||||
|
"vllm": {
|
||||||
|
"found": True,
|
||||||
|
"origin": None,
|
||||||
|
"loader": None,
|
||||||
|
"locations": ["/root/vllm"],
|
||||||
|
"real_module": False,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dists": {},
|
||||||
|
"binaries": {"vllm": None},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert _package_installed_from_probe("vllm", probe) is False
|
||||||
|
assert "namespace" in _package_status_note("vllm", probe)
|
||||||
|
assert "no vLLM CLI" in _package_status_note("vllm", probe)
|
||||||
|
|
||||||
|
def test_vllm_requires_cli_for_current_serve_command(self):
|
||||||
|
probe = {
|
||||||
|
"modules": {"vllm": {"found": True, "real_module": True}},
|
||||||
|
"dists": {"vllm": "0.8.5"},
|
||||||
|
"binaries": {"vllm": "/home/user/venv/bin/vllm"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert _package_installed_from_probe("vllm", probe) is True
|
||||||
|
|
||||||
|
def test_llama_cpp_is_installed_when_native_llama_server_exists(self):
|
||||||
|
probe = {
|
||||||
|
"modules": {"llama_cpp": {"found": False, "real_module": False}},
|
||||||
|
"dists": {},
|
||||||
|
"binaries": {"llama-server": "/usr/local/bin/llama-server"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert _package_installed_from_probe("llama_cpp", probe) is True
|
||||||
|
assert "native llama-server" in _package_status_note("llama_cpp", probe)
|
||||||
|
|
||||||
|
def test_diffusers_requires_torch_too(self):
|
||||||
|
missing_torch = {
|
||||||
|
"modules": {"diffusers": {"found": True, "real_module": True}, "torch": {"found": False}},
|
||||||
|
"dists": {"diffusers": "0.37.0"},
|
||||||
|
"binaries": {},
|
||||||
|
}
|
||||||
|
ready = {
|
||||||
|
"modules": {"diffusers": {"found": True, "real_module": True}, "torch": {"found": True, "real_module": True}},
|
||||||
|
"dists": {"diffusers": "0.37.0", "torch": "2.10.0"},
|
||||||
|
"binaries": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert _package_installed_from_probe("diffusers", missing_torch) is False
|
||||||
|
assert _package_installed_from_probe("diffusers", ready) is True
|
||||||
|
|||||||
Reference in New Issue
Block a user