fix: report serve dependency readiness (#412)

This commit is contained in:
spooky
2026-06-01 23:39:36 +10:00
committed by GitHub
parent 39cec53284
commit 4b72dd407b
3 changed files with 193 additions and 11 deletions

View File

@@ -92,6 +92,115 @@ def _docker_row_status(*, on_remote, in_container, installed, 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):
"""Find next line terminator in buffer. Returns (index, separator_length) or (-1, 0)."""
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.
"""
_require_admin(request)
import importlib, shlex, json as _json
import importlib, importlib.metadata as importlib_metadata, shlex, json as _json
port_arg = ""
if ssh_port and str(ssh_port).strip() not in ("", "22"):
_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
# venv over SSH so a remote `pip install` actually reflects here.
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_system_names = [p["name"] for p in packages if p.get("target") == "remote" and p.get("kind") == "system"]
if host and remote_names:
try:
names_lit = ",".join(repr(n) for n in 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))"
)
py = _package_probe_script(remote_names)
src = ""
if venv:
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()):
line = line.strip()
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
except Exception:
remote_status = {}
@@ -736,16 +844,29 @@ def setup_shell_routes() -> APIRouter:
on_remote = bool(host and pkg.get("target") == "remote")
if on_remote:
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":
pkg["installed"] = shutil.which(pkg["name"]) is not None
elif pkg["name"] == "llama_cpp" and shutil.which("llama-server"):
pkg["installed"] = True
pkg["status_note"] = f"native llama-server: {shutil.which('llama-server')}"
else:
try:
importlib.import_module(pkg["name"])
pkg["installed"] = True
if pkg["name"] == "vllm":
pkg["installed"] = shutil.which("vllm") is not None
else:
importlib_metadata.version(pkg["name"].replace("_", "-"))
pkg["installed"] = True
except ImportError:
pkg["installed"] = False
except importlib_metadata.PackageNotFoundError:
pkg["installed"] = False
if pkg["name"] == "docker":
status = _docker_row_status(

View File

@@ -554,10 +554,12 @@ async function _fetchDependencies() {
const isLocal = pkg.target === 'local';
const isSystemDep = pkg.kind === 'system';
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')}">`
+ `<div class="cookbook-dep-info">`
+ `<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>`
+ note
+ `</div>`
+ `<span class="cookbook-dep-tag cookbook-dep-cat">${esc(pkg.category)}</span>`
+ _statusTag(pkg, isLocal, isSystemDep, winBlocked)

View File

@@ -11,6 +11,8 @@ from routes.shell_routes import (
_find_line_break,
_running_in_container,
_docker_row_status,
_package_installed_from_probe,
_package_status_note,
DOCKER_IN_CONTAINER_HINT,
)
@@ -182,3 +184,60 @@ class TestDockerRowStatus:
assert "remote" in lowered
assert "socket" 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