Files
odysseus/specs/cookbook-hwfit.md
RaresKeYandStressTestor 7026cf40b5 docs: bootstrap specs ground truth (#5794)
* docs(specs): restore bootstrap after dev rewrite

* docs(specs): remove runtime inventory snapshot

* docs(specs): reconcile current dev truth

* docs(specs): document scheduled task actions as an owner-attribution source

Owner Attribution covered cookie, bearer-token and internal-loopback
requests. Scheduled task actions are a fourth source and behave
differently: _execute_action passes owner=task.owner off the stored
ScheduledTask row, so no request and no resolved principal are in
flight, and route-level require_user() never runs.

Webhook triggers are the sharp case. They are unauthenticated by
design with the token as the only credential and execute under the
stored task.owner.

Paths cite routes/task/task_routes.py, the canonical location after
the task subpackage move (#6081); routes/task_routes.py on current dev
is the backward-compat shim.

* docs(specs): add chained tasks to the trigger list, refresh dev stamp

Review feedback from RaresKeY on the previous commit.

"Every trigger path" was too broad: success-chained tasks are another
path into _execute_action. Added them with their own citation, and
noted that chaining additionally requires the target task to share
task.owner and rejects cycles, which is stricter than the trigger-side
checks. Softened the lead-in to "these trigger paths".

Line 56 still pointed at routes/task_routes.py for webhook credential
validation. That path is the backward-compat shim on current dev after
the task subpackage move (#6081); repointed to the canonical
routes/task/task_routes.py.

Stamp moved to dev@2a6b09b. Inspection backing that bump was scoped:
every file path cited in this spec was mechanically checked to resolve
on 2a6b09b, and every file:line in the Owner Attribution additions was
read against it. Behavioral claims elsewhere in the file were not
re-audited.

* docs(specs): correct SECURE_COOKIES description to match current behavior

Third of the stale details RaresKeY enumerated. The cookie section
described SECURE_COOKIES as purely opt-in, which stopped being true.

_secure_cookie() (routes/auth_routes.py:89) treats an explicit true or
false as authoritative and derives the Secure attribute from the
request otherwise, including when the variable is unset and when
docker-compose injects it present-but-empty. Either the connection
scheme or the first X-Forwarded-Proto hop being https is enough.

* docs(specs): refresh current dev truth

---------

Co-authored-by: StressTestor <212606152+StressTestor@users.noreply.github.com>
2026-08-25 14:18:44 +02:00

196 lines
14 KiB
Markdown

# Cookbook And Hardware Fit
Last updated: dev@e71f8ce | 2026-08-25
## Scope
This spec covers model setup/serving and hardware fit in:
- app route registration in `app.py`;
- `routes/cookbook_routes.py`;
- `src/cookbook_serve_lifecycle.py`;
- `src/host_docker_access.py`;
- Cookbook package/rebuild/shell integration in `routes/shell_routes.py`;
- `routes/cookbook_helpers.py`;
- `routes/hwfit_routes.py`;
- `services/hwfit/*` and `services/hwfit/data/hf_models.json`;
- durable Cookbook state through `routes.cookbook_helpers.COOKBOOK_STATE_FILE`;
- helper/CLI scripts `scripts/odysseus-cookbook`, `scripts/add_hwfit_models.py`, `scripts/hf_download.py`, and `scripts/diffusion_server.py`;
- Docker overlays `docker-compose.gpu-*.yml`, `docker/gpu.*.yml`, `docker/host-docker.yml`, `scripts/check-docker-gpu.sh`, and `scripts/check-docker-amd-gpu.sh`;
- frontend modules `static/js/cookbook*.js`, including Cookbook running, serve, download, diagnosis, progress, and HW Fit modules;
- tests covering Cookbook helpers, routes, CLI state, package detection, frontend progress, HW Fit services, serve profiles, Docker GPU overlays, and GPU diagnostic scripts.
## Current Call Sites Include
- Cookbook modal and state modules in `static/js/cookbook*.js`;
- package readiness/install and rebuild flows through `routes/shell_routes.py`;
- direct shell exec/stream integration used by Cookbook task controls;
- model endpoint setup and serve flows;
- hardware-fit recommendations for model choices;
- image-model recommendations for diffusion serving;
- APFEL/local platform dependency paths where supported;
- Docker GPU helper scripts and compose overlays;
- the `odysseus-cookbook` CLI using the same Cookbook state file.
## Cookbook Runtime
`routes.cookbook_routes` owns model download, setup, SSH key, cached model scan, serve, GPU state, kill-pid, state sync, Hugging Face latest lookup, vLLM recipe lookup, serve diagnosis, and task-status endpoints. `src.cookbook_serve_lifecycle` bridges scheduled `cookbook_serve` tasks into serve/stop behavior; task/calendar scheduling ownership stays in `calendar-tasks-notes.md`.
Access policy is split by surface:
- download/setup/SSH key/cache scan/serve/GPU/kill/state/task-status are admin/internal-tool surfaces;
- `/api/cookbook/hf-latest` is authenticated-user gated;
- HW Fit routes are authenticated read/probe routes through normal middleware, not admin-only operations;
- bearer API tokens do not satisfy Cookbook admin gates.
Runtime behavior:
- POSIX and most remote flows run detached through tmux;
- local Windows uses detached process/log/pid behavior under `%TEMP%\\odysseus-tmux`; Python first publishes a valid Win32 fallback PID, then Git Bash may replace it with `/proc/$$/winpid` after a ready-file handoff, so PowerShell `Stop-Tree` can terminate the actual serving shell and children instead of receiving an MSYS PID. Frontend PowerShell venv activation is quoted safely and the local Git Bash runner converts a valid `Scripts\\Activate.ps1` prefix into `source <git-bash-path>/Scripts/activate` so the selected environment actually supplies the serve binary;
- remote Windows uses PowerShell runner scripts;
- missing `tmux`, `docker`, or serve-engine binaries return shaped errors where possible;
- local Docker inside the Odysseus container is available only when the Docker CLI exists, `ODYSSEUS_ENABLE_HOST_DOCKER=true`, and `/var/run/docker.sock` is actually mounted as a socket; otherwise Cookbook should show the host-Docker access hint and prefer remote SSH Docker workflows;
- model serve auto-registers LLM or image `ModelEndpoint` rows immediately, then frontend readiness probing can repair/create fallback endpoints;
- diffusion-server serves are registered as image endpoints;
- MLX image serves use `scripts/mlx_image_server.py`, which pins generation/edit dispatch to the model chosen at process start and ignores OpenAI-compatible per-request model selectors;
- vLLM recipe routes fetch and cache model recipe manifests/YAML from `vllm-project/recipes`, normalize base args/env/dependencies/tool-calling/reasoning variants, and expose compatible strategy metadata for serve setup;
- Hugging Face download/setup paths can detect and persist encrypted HF tokens for later Cookbook/agent use;
- local and remote model paths can contain spaces or non-ASCII characters when helper validation/quoting accepts them;
- task status handles tmux, remote Windows logs, local Windows PID/log files, HF cache completion checks, stale browser-state download guards, pip dependency-install success sentinels, exit-code wrappers, serve diagnosis snapshots, and scheduled serve lifecycle hooks;
- scheduled serve lifecycle stop attempts only persist `status=stopped`, clear `_scheduledStopAtMs`, and delete auto-registered endpoints for sessions whose tmux/remote stop command succeeded or were already gone; failed stop attempts are logged without marking unrelated expired serves as stopped.
`routes.cookbook_helpers` owns validation and command construction:
- repository and model IDs;
- local directories, SSH hosts/ports, GPU selectors, and tokens;
- shell quoting for Bash and PowerShell;
- pip/install fallback chains;
- safe environment prefixes;
- serve command validation;
- user-shell PATH bootstrap, Git-Bash drive-path conversion, preflight, and exit-code helpers.
Cookbook routes request shell/SSH behavior; they do not relax shell security.
## Shell Dependencies
`routes.shell_routes.py` owns Cookbook-adjacent package readiness/install, shell execution/streaming, and llama.cpp rebuild endpoints. The Cookbook UI calls these routes for dependency diagnosis, install/update actions, engine rebuilds, and tmux/reconnect/stop/kill flows. Windows uses detached log/PID wrappers where POSIX tmux is unavailable.
These are admin-only code-execution surfaces and should be reviewed with Cookbook changes even though they are implemented outside `routes.cookbook_routes.py`.
## State, Secrets, And Provenance
Cookbook state lives under the shared data dir through the `COOKBOOK_STATE_FILE` constant, normally `data/cookbook_state.json`. Routes and the `odysseus-cookbook` CLI use the same state path.
State behavior:
- browser-facing state masks secrets;
- server-side `env.hfToken` is encrypted before storage;
- task payloads strip raw HF tokens;
- browser local storage strips HF token values;
- state POST has anti-wipe guards for server lists;
- state POST rejects stale `done` download state when the latest shard/cache markers still show an incomplete download;
- recent server-side tasks are preserved against stale browser overwrites;
- task-status validates saved shell-bound fields before SSH/tmux commands.
Cookbook auto-registered endpoints are currently shared/null-owner rows with no API key when created by backend serve registration. Browser fallback registration goes through the normal model-endpoint route. The desired ownership policy for Cookbook-created endpoints should remain explicit.
HW Fit is an MIT-licensed llmfit adaptation; attribution lives in project acknowledgments/licenses.
## Hardware Fit
`services/hwfit/hardware.py` owns hardware detection across NVIDIA, AMD, Apple Silicon, Windows, CPU, RAM, available RAM, remote SSH, container/native probe context, and cached host detections.
`services/hwfit/models.py`, `fit.py`, `profiles.py`, `image_models.py`, and
`hf_discovery.py` own model catalog loading, normalization, API-backed dynamic
catalog refresh, memory estimates, quantization labels, fit scoring, serve
profile computation, image model ranking, and backend/format servability
filtering.
`routes/hwfit_routes.py` owns the HTTP surface and manual hardware override application.
Runtime behavior:
- hardware detection uses a cache with `fresh=true` bypass;
- probe results include scope/container visibility metadata, and containerized no-GPU/low-RAM states can return user-facing visibility warnings with rescan/manual/copy-diagnostics actions;
- manual hardware replacement is a what-if simulator, not additive hardware;
- manual hardware accepts `cuda`, `rocm`, `metal`, `cpu_x86`, and `cpu_arm`
backends and must stay in lock-step with backend support in `fit.py`. Metal
simulation marks unified memory and filters toward locally servable GGUF/MLX
choices instead of CUDA/vLLM-only formats.
- ignore switches can drop detected GPU/RAM before ranking;
- homogeneous GPU grouping targets realistic multi-GPU pools;
- image model ranking normalizes to a single-GPU fit view;
- Metal/RDNA/backend restrictions can filter otherwise fit models.
- Apple Silicon bandwidth estimates use chip/core-specific tables for M-series Max/Pro/Ultra variants and avoid matching non-Apple GPU names.
- Windows and Apple/consumer-AMD paths filter toward GGUF/llama.cpp-compatible
choices. On multi-GPU systems, fixed GGUF target quantization that cannot be
served by the selected backend returns `no_fit` rather than `None`.
## Platform And Degraded Behavior
- Linux, Windows/PowerShell, macOS, Docker, NVIDIA, AMD, Apple Silicon, and CPU-only systems have different command paths.
- Remote hosts are accessed through SSH helpers; Cookbook host/port/path inputs must be validated before command construction.
- HW Fit remote host/port query values currently do not share all Cookbook route-level validation before SSH probing.
- Missing local tools or failed installs should surface command/output/error detail where possible.
- GPU overlays remain optional and do not break CPU-only deployments.
- Docker GPU overlays pass host devices/env; they do not install CUDA/ROCm engines by themselves.
- Default Docker Compose intentionally does not mount the host Docker socket. `docker/host-docker.yml` is an explicit high-trust overlay for operators who accept broad host-Docker control from inside the container.
- NVIDIA Docker diagnostics are read-only by default, and `.env` edits/install actions require explicit flags.
- AMD Docker diagnostics are read-only and do not mutate `.env`.
- vLLM is rejected on unsupported Windows/macOS paths.
- llama.cpp CPU-only and GPU fallback scripts should preserve usable CPU paths.
- SSH probe failures, GPU driver errors, and no-GPU states should be distinguishable.
- Remote SSH host/port validation is shared through route validators for Cookbook/HWFit paths.
- Windows launcher/runtime Git Bash discovery includes per-user installs under `%LocalAppData%\\Programs\\Git`, and WSL/Git Bash detection shapes PATH handling for NVIDIA/remote flows.
- macOS startup helpers start ChromaDB alongside the app path.
- Ollama serve can auto-pick an available port, and scheduled task stop paths
verify stop success before persisting a stopped state.
## Model Catalog And Latest Lookup
HW Fit model scoring depends on bundled `services/hwfit/data/hf_models.json`,
bundled `services/hwfit/data/mlx_community_models.json`, runtime dynamic caches
under `DATA_DIR/hwfit/`, catalog normalization, and assumptions about model
formats and quantization. `scripts/add_hwfit_models.py` updates the static HF
catalog.
Hugging Face latest lookup and HW Fit dynamic refresh use external Hub metadata
and can degrade to empty, unknown-size, partial, or malformed-result behavior.
`refresh_catalog=1` refreshes API-backed collection caches for MLX community
and selected HF organization collections, with a 24-hour freshness guard and
bundled JSON fallbacks when the network/cache is unavailable. HW Fit tolerates
non-numeric `gpu_count` values from callers. Model normalization also treats
non-string `parameter_count` and quantization fields as unknown rather than
calling string methods and aborting the ranking pass. Catalog drift and dynamic
latest-model metadata are separate sources of recommendation drift.
## Security Policy
Admin gates must stay in place for install, serve, kill, setup, state mutation, and shell-like actions. `/api/shell/exec` is an admin primitive used by Cookbook task control and must stay in this review boundary. Scheduled `cookbook_serve` tasks are admin-only action tasks; task create/update/manual run/webhook/scheduler execution must all reject or pause them for non-admin owners.
Kill-pid guardrails:
- admin-only;
- PID floor;
- signal allowlist;
- validated remote host/port;
- frontend confirmation for TERM/KILL cleanup.
Shell-bound Cookbook inputs must pass helper validation before command construction. HF tokens, Cookbook state secrets, and endpoint API keys must remain encrypted or masked and must not be written back to clients in raw form. Host Docker socket access must stay opt-in and clearly distinguished from merely having a Docker CLI in the container.
## Testing Coverage
Existing coverage is strongest for helper validation/quoting, SSH host validation, pip fallback and dependency-completion regressions, cached scan scripts, serve profile computation, scheduled serve lifecycle state persistence, hardware detection/ranking across AMD/NVIDIA/macOS/manual/container modes, MLX/Metal ranking and request-model pinning, manual backend simulation, Docker GPU compose overlays, Cookbook CLI state, package detection, Windows venv/path/task helpers, non-numeric GPU counts, non-string model catalog fields, and selected frontend progress regressions.
Route-level auth/security and degraded-return coverage is thinner for Cookbook admin routes, shell dependency routes, `/api/cookbook/hf-latest`, state/status edge cases, HW Fit routes, frontend JS behavior, and helper scripts such as `hf_download.py`, `add_hwfit_models.py`, and `diffusion_server.py`.
## Current Gaps
- Cookbook-created model endpoint ownership/shared/null-owner policy needs a deliberate decision.
- `/api/shell/exec` and Cookbook package/rebuild routes need to remain cross-referenced with shell/admin specs because they are Cookbook-critical code-execution surfaces.
- Cookbook route auth/security and degraded-return behavior need route-level tests.
- `/api/cookbook/hf-latest` needs tests locking its user-authenticated access policy and failure behavior.
- HW Fit routes need route-level tests around missing catalogs, manual overrides, `fit_only`, profiles, and image-model cases.
- Dependency install/serve diagnosis remains split across Cookbook routes, shell routes, frontend diagnosis, optional binaries, and platform-specific scripts, even though longer serve-output tails are centralized through `routes/cookbook_output.py`.
- Model catalog, quantization, backend, and Hugging Face metadata drift need ongoing maintenance.