ulysses/routes
Leo 6fca7e86b7
Cookbook serve profiles and engine filter
* Cookbook: Engine filter + intelligent hardware-computed serve profiles

Two related Cookbook serving improvements for accurate, hardware-aware model
serving (especially on consumer GPUs that can only run GGUF/llama.cpp).

Engine filter
- New "Engine" dropdown (All / llama.cpp / vLLM / SGLang) beside the quant
  picker. Pure client-side view filter over the fetched list via the same
  _detectBackend() the serve commands use, so what you filter to is exactly what
  would launch. Re-renders from cache (no refetch). Empty-state message + the
  instant-cache-paint path account for it too.

Intelligent serve profiles (Quality / Balanced / Speed)
- services/hwfit/profiles.py: compute_serve_profiles() turns detected VRAM +
  model size into concrete llama.cpp flags (n_gpu_layers, n_cpu_moe, cache-type,
  context). Encodes the by-hand tuning: a too-big MoE offloads experts to CPU
  instead of failing; a model that fits stays fully on GPU; quant tracks profile
  intent; vision models keep image-encoder headroom. Reuses models.py VRAM math
  so filtering and serving agree on what fits. Pure/deterministic (no t/s claims
  — partial-offload speed isn't reliably predictable; fit is what's computed).
- /api/hwfit/profiles endpoint returns the profiles + the model's trained
  context limit, with loose name matching (strips org/ prefix, -GGUF suffix,
  quant tag) so a local GGUF folder name resolves to its catalog entry.
- _buildServeCmd (llama.cpp) now emits --n-cpu-moe / --flash-attn /
  --cache-type-k/v when set, with llama-cpp-python fallback equivalents. It
  previously only set -ngl/-c, which is why it OOM'd or ran slow.
- Serve panel: profile chips that fill the fields on click, plus CPU-MoE / KV
  Cache / Flash Attn fields. Context is clamped to the model's trained limit
  (and an absolute 1M sanity ceiling) on type/blur/profile-load and at launch —
  fixes a crash where a stale 256k/16M preset + quantized KV cache caused an
  amdgpu ErrorDeviceLost.

Tests: tests/test_serve_profiles.py (7) — offload vs full-GPU fit, never exceed
VRAM, context cap, launchable flags, vision headroom, no-GPU empty.
Checks: py_compile + node --check pass; pytest test_serve_profiles + test_hwfit_amd
green; verified live on an RDNA4 box (gfx1200) — Balanced lands ~ncm18 q4 128k,
matching hand-tuning.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Cookbook: make column-header sorting discoverable (incl. Newest)

Sorting in Cookbook is via clickable column headers (pewds' design), but the
headers had no visual cue that they're interactive — so sorting in general, and
the Newest sort on the Model header specifically, was undiscoverable.

- Style sortable headers as interactive: pointer cursor, hover underline, and
  the active sort column bolded/highlighted. There was no CSS for
  .hwfit-sortable / .hwfit-sort-active at all; this helps every existing sort,
  not just Newest.
- The Model column header sorts by release_date (newest first), reusing the
  existing header-click sort wiring and the "newest" SORT_KEY.

No new sort control — uses the existing column-header paradigm.

Checks: node --check passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Cookbook serve profiles: keep the on-disk file's quant fixed (don't propose Q6/Q2)

In the Serve tab the model is a specific GGUF file already on disk, so its quant
can't change — but the profiles were suggesting "Quality · Q6_K" / "Speed · Q2_K"
as if you could re-quantize it. That's meaningless when serving a fixed file.

- compute_serve_profiles gains serve_weights_gb / serve_quant. When set (SERVE
  mode), the quant is locked to the file's and profiles differ only in the real
  serving knobs — n_cpu_moe, KV-cache type, context. _weights_gb / _cpu_moe_for_budget
  use the file's actual size instead of a quant-derived estimate. DOWNLOAD mode
  (no override) still varies the quant to show download options.
- /api/hwfit/profiles accepts serve_weights_gb & serve_quant.
- The Serve panel parses the file's size (from m.size "20.6 GB") and quant (from
  the repo/file name) and passes them, so profiles match what's actually served.

Result for a 20.6 GB Q4_K_M file: all three profiles stay Q4_K_M and differ by
KV/ctx/offload (Quality q8 KV 128k ncm21, Balanced q4 128k ncm17, Speed q4 32k
ncm15) — no nonsensical quant changes.

Tests: test_serve_mode_keeps_fixed_quant. Full serve-profile suite green (9).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Cookbook serve: Vision toggle (auto-find mmproj) + live VRAM/RAM-spillover monitor

Two serve-panel additions:

1. **Vision toggle.** A "Vision" checkbox that serves the model with its
   multimodal projector so it can read images. The mmproj path is resolved at
   runtime (find mmproj-*.gguf next to the model), so dropping an mmproj file in
   the model folder makes the toggle just work; `--mmproj … --image-max-tokens
   1024` (native) / `--clip_model_path` (llama-cpp-python) only when on + found.

2. **Live GPU-memory monitor.** A readout that polls /api/cookbook/gpus every 4s
   while the panel is open and shows VRAM used/total/%, free, and — crucially on
   a discrete card — **RAM spillover** (AMD gtt_used_mb), with a plain-language
   health hint: green/healthy, amber/tight, red/"spilled to RAM — slow (raise
   CPU MoE or lower context)". Surfaces gtt_used_mb from the gpus endpoint
   (previously read for total only and discarded for 'used').

Lets you see at a glance whether a config fits VRAM (fast) or is paging to system
RAM over PCIe (slow) instead of guessing.

Checks: node --check + py_compile pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:34:42 +09:00
..
__init__.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
admin_wipe_routes.py Add native Windows compatibility layer 2026-06-01 15:09:47 +09:00
api_token_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
assistant_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
auth_routes.py Revoke stale sessions after password change 2026-06-02 05:59:22 +09:00
backup_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
calendar_routes.py Fix YEARLY recurring CalDAV events only showing on DTSTART year (#179) 2026-06-01 13:42:44 +09:00
chat_helpers.py feat(ai): add OpenRouter and Ollama Cloud providers (#231) 2026-06-01 14:26:10 +09:00
chat_routes.py fix: scope chat active-document lookup to the session owner (#569) 2026-06-02 11:46:40 +09:00
cleanup_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
compare_routes.py feat(ai): add OpenRouter and Ollama Cloud providers (#231) 2026-06-01 14:26:10 +09:00
contacts_routes.py Add native Windows compatibility layer 2026-06-01 15:09:47 +09:00
cookbook_helpers.py feat: select cached gguf artifacts for serve (#891) 2026-06-02 12:32:40 +09:00
cookbook_routes.py Cookbook serve profiles and engine filter 2026-06-02 12:34:42 +09:00
diagnostics_routes.py Fix email-thread HTML injection, attachment path traversal, and missing authz (#475) 2026-06-01 22:20:17 +09:00
document_helpers.py Harden PDF document markers against cross-owner upload access (#445) 2026-06-01 22:38:14 +09:00
document_routes.py Harden PDF document markers against cross-owner upload access (#445) 2026-06-01 22:38:14 +09:00
editor_draft_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
email_helpers.py fix(auth): honor AUTH_ENABLED=false on owner-scoped endpoints (no /login loop) (#880) 2026-06-02 12:26:26 +09:00
email_pollers.py Scope email calendar extraction to account owner 2026-06-01 23:12:32 +09:00
email_routes.py Fix email-thread HTML injection, attachment path traversal, and missing authz (#475) 2026-06-01 22:20:17 +09:00
embedding_routes.py chore: use running event loop in async helpers (#821) 2026-06-02 12:28:05 +09:00
emoji_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
font_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
gallery_helpers.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
gallery_routes.py Gate image editor AI endpoints by privilege (#447) 2026-06-01 22:35:24 +09:00
history_routes.py fix(history): scope topic analysis to authenticated owner only (#744) 2026-06-02 11:36:01 +09:00
hwfit_routes.py Cookbook serve profiles and engine filter 2026-06-02 12:34:42 +09:00
mcp_routes.py Fix email-thread HTML injection, attachment path traversal, and missing authz (#475) 2026-06-01 22:20:17 +09:00
memory_routes.py feat: allow memory import without session (#493) 2026-06-01 22:32:17 +09:00
model_routes.py fix(auth): honor AUTH_ENABLED=false on owner-scoped endpoints (no /login loop) (#880) 2026-06-02 12:26:26 +09:00
note_routes.py Add native Windows compatibility layer 2026-06-01 15:09:47 +09:00
personal_routes.py Scope personal RAG uploads by owner (#446) 2026-06-01 22:36:53 +09:00
prefs_routes.py Add native Windows compatibility layer 2026-06-01 15:09:47 +09:00
preset_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
research_routes.py fix(auth): honor AUTH_ENABLED=false on owner-scoped endpoints (no /login loop) (#880) 2026-06-02 12:26:26 +09:00
search_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
session_routes.py Attribute API-token sessions to the token owner (effective_user) (#871) 2026-06-02 11:39:01 +09:00
shell_routes.py chore: use running event loop in async helpers (#821) 2026-06-02 12:28:05 +09:00
signature_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
skills_routes.py fix(skills): scope skill reads to caller owner (#777) 2026-06-02 11:21:27 +09:00
stt_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
task_routes.py Polish email tasks and window controls 2026-06-01 20:56:46 +09:00
tts_routes.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
upload_routes.py Add native Windows compatibility layer 2026-06-01 15:09:47 +09:00
vault_routes.py fix(security): stop leaking the vault master password via process argv (#879) 2026-06-02 12:25:43 +09:00
webhook_routes.py fix(security): fail closed on null-owner session in sync-chat endpoint (#870) 2026-06-02 11:38:05 +09:00