fix: keep serve preflight errors visible (#398)

This commit is contained in:
spooky
2026-06-01 23:40:06 +10:00
committed by GitHub
parent 4b72dd407b
commit 15822e91ff
3 changed files with 44 additions and 6 deletions

View File

@@ -214,6 +214,17 @@ def _validate_serve_cmd(v: str | None) -> str | None:
return v
def _append_serve_preflight_exit_lines(runner_lines: list[str], *, keep_shell_open: bool) -> None:
"""Append serve-runner lines that surface preflight failures before exit."""
runner_lines.append('if [ -n "$ODYSSEUS_PREFLIGHT_EXIT" ]; then')
runner_lines.append(' echo ""; echo "=== Process exited with code $ODYSSEUS_PREFLIGHT_EXIT ==="')
if keep_shell_open:
runner_lines.append(' exec "${SHELL:-/bin/bash}"')
else:
runner_lines.append(' exit "$ODYSSEUS_PREFLIGHT_EXIT"')
runner_lines.append('fi')
class ModelDownloadRequest(BaseModel):
repo_id: str
include: str | None = None # glob pattern e.g. "*Q4_K_M*"

View File

@@ -36,7 +36,7 @@ from routes.cookbook_helpers import (
_validate_repo_id, _validate_include, _validate_remote_host, _validate_token,
_validate_local_dir, _validate_ssh_port, _validate_gpus, _shell_path,
_ps_squote, _bash_squote, _validate_serve_cmd, _parse_serve_phase,
_safe_env_prefix, _local_tooling_path_export,
_safe_env_prefix, _local_tooling_path_export, _append_serve_preflight_exit_lines,
ModelDownloadRequest, ServeRequest,
)
@@ -950,6 +950,7 @@ def setup_cookbook_routes() -> APIRouter:
# ── Linux/Termux: bash + tmux (existing flow) ──
runner_lines = ["#!/bin/bash"]
runner_lines.extend(_user_shell_path_bootstrap())
runner_lines.append('ODYSSEUS_PREFLIGHT_EXIT=""')
# Put Odysseus's own venv bin on PATH (local runs only) so the serve
# shell resolves the bundled python3/hf, mirroring the download flow.
if not remote:
@@ -1044,7 +1045,7 @@ def setup_cookbook_routes() -> APIRouter:
# command (the natural serving engine on Apple Silicon / Metal).
runner_lines.append('if ! command -v ollama &>/dev/null; then')
runner_lines.append(' echo "ERROR: Ollama not found. Install it (macOS: brew install ollama, or https://ollama.com/download), then launch again."')
runner_lines.append(' exit 127')
runner_lines.append(' ODYSSEUS_PREFLIGHT_EXIT=127')
runner_lines.append('fi')
runner_lines.append('if ! curl -sf http://localhost:11434/api/tags >/dev/null 2>&1; then')
runner_lines.append(' echo "Starting ollama server..."; (ollama serve >/dev/null 2>&1 &)')
@@ -1054,7 +1055,7 @@ def setup_cookbook_routes() -> APIRouter:
# vLLM is CUDA/ROCm-only and does not run on macOS at all.
runner_lines.append('if [ "$(uname -s)" = "Darwin" ]; then')
runner_lines.append(' echo "ERROR: vLLM does not run on macOS. Use Ollama or llama.cpp (Metal) instead."')
runner_lines.append(' exit 1')
runner_lines.append(' ODYSSEUS_PREFLIGHT_EXIT=1')
runner_lines.append('fi')
# Put ~/.local/bin on PATH first — without a venv, vllm installs
# there via --user and the non-login serve shell otherwise can't
@@ -1062,21 +1063,25 @@ def setup_cookbook_routes() -> APIRouter:
runner_lines.append('export PATH="$HOME/.local/bin:$PATH"')
runner_lines.append('if ! command -v vllm &>/dev/null; then')
runner_lines.append(' echo "ERROR: vLLM is not installed. Open Cookbook -> Dependencies and install vllm on this server, then launch again."')
runner_lines.append(' exit 127')
runner_lines.append(' ODYSSEUS_PREFLIGHT_EXIT=127')
runner_lines.append('fi')
elif "sglang.launch_server" in req.cmd:
runner_lines.append('export PATH="$HOME/.local/bin:$PATH"')
runner_lines.append('if ! python3 -c "import sglang" 2>/dev/null; then')
runner_lines.append(' echo "ERROR: SGLang is not installed. Open Cookbook -> Dependencies and install sglang on this server, then launch again."')
runner_lines.append(' exit 127')
runner_lines.append(' ODYSSEUS_PREFLIGHT_EXIT=127')
runner_lines.append('fi')
elif "scripts/diffusion_server.py" in req.cmd or ".diffusion_server.py" in req.cmd:
runner_lines.append('export PATH="$HOME/.local/bin:$PATH"')
runner_lines.append('if ! python3 -c "import torch, diffusers" 2>/dev/null; then')
runner_lines.append(' echo "ERROR: Diffusion serving requires PyTorch + diffusers. Open Cookbook -> Dependencies and install diffusers on this server, then launch again."')
runner_lines.append(' exit 127')
runner_lines.append(' ODYSSEUS_PREFLIGHT_EXIT=127')
runner_lines.append('fi')
_append_serve_preflight_exit_lines(
runner_lines,
keep_shell_open=not local_windows,
)
runner_lines.append(req.cmd)
if local_windows:
# Detached background process — no interactive shell to keep open.

View File

@@ -2,6 +2,7 @@ import pytest
from fastapi import HTTPException
from routes.cookbook_helpers import (
_append_serve_preflight_exit_lines,
_local_tooling_path_export,
_safe_env_prefix,
_validate_gpus,
@@ -58,3 +59,24 @@ def test_local_tooling_path_export_preserves_spaces_and_expands_path():
line = _local_tooling_path_export("/Users/John Smith/.venv/bin/python3")
assert line == 'export PATH="/Users/John Smith/.venv/bin:$PATH"'
assert line.endswith(':$PATH"') # $PATH stays expandable in double quotes
def test_serve_preflight_failure_keeps_tmux_pane_visible():
"""Dependency preflight failures should remain visible in tmux output.
A bare `exit 127` kills the tmux pane before the browser/status poller can
capture the helpful error, leaving users with a blank "crashed" card.
"""
runner_lines = [
'ODYSSEUS_PREFLIGHT_EXIT=""',
'echo "ERROR: vLLM is not installed. Open Cookbook -> Dependencies and install vllm on this server, then launch again."',
'ODYSSEUS_PREFLIGHT_EXIT=127',
]
_append_serve_preflight_exit_lines(runner_lines, keep_shell_open=True)
script = "\n".join(runner_lines)
assert "ERROR: vLLM is not installed" in script
assert 'ODYSSEUS_PREFLIGHT_EXIT=127' in script
assert 'echo "=== Process exited with code $ODYSSEUS_PREFLIGHT_EXIT ==="' in script
assert 'exec "${SHELL:-/bin/bash}"' in script
assert "exit 127" not in script