This repository has been archived on 2026-07-22. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mission-control/CLAUDE.md
T
Hitonabi 3ccdee339a docs: v6 als umgesetzt markiert
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 14:48:25 +02:00

10 KiB

Mission Control

Web-Dashboard zur Verwaltung eines lokalen LLM-Stacks (llama-swap) auf dem Bosgame M5. FastAPI-Backend + Vanilla-JS-Dashboard. Leitprinzip: KISS — kein Build-Schritt, kein Frontend-Framework, keine Datenbank. Concerns sind getrennt (SoC) — aber ohne Build: native ES-Module im Frontend, FastAPI-APIRouter im Backend.

Architektur

Backend (Top-Level-Helfer + ein Router je Bereich):

  • app.py — dünner Einstieg: FastAPI, hängt die Router ein, liefert das statische UI aus, Exception-Handler + eine Cache-Control: no-cache-Middleware für / und /static (UI wirkt sofort nach rsync, kein Stale-JS im Browser).
  • config.py — alle Env-Vars + die gemeinsame ruamel.yaml-Instanz. Auch SOURCE_DIR/PROD_DIR fürs Self-Update.
  • auth.py — optionale Token-Auth: Header X-MC-Token oder Query ?token= (Query ist Pflicht für WebSockets, da Browser dort keine Header senden).
  • jobengine.py — In-Memory-Job-System (Threads + Subprocess) mit Live-Log. start_job(..., stdin_data=, log_cmd=): Secrets (sudo-PW) gehen über stdin, die Log-Zeile wird sanitisiert (kein Passwort im Log/ps).
  • llamaswap.py — spricht llama-swap an (/running, /v1/models, unload) und liest/schreibt dessen config.yaml per ruamel.yaml (Kommentare bleiben erhalten).
  • hw_math.py — Odysseus-Fit-Mathe: VRAM/RAM-Bedarf, tps-Schätzung, max_ctx_for (optimaler Kontext aus Hardware), extract_params_b. Genutzt von cookbook + models.
  • recipes.py — kuratierte Use-Case-Stacks (Daten, kein Code) fürs Cookbook 2.0.
  • routers/*.py — ein Router je Bereich: models.py (status mit Meta/Caps/optimal_ctx, download, register, update_model, unload, chat), jobs.py (jobs), maintenance.py (update = llama.cpp, self-update = MC selbst, os-update, service/{name}/restart, reboot, logs-WebSocket), system.py (status + stream-WebSocket, Live-Metriken via psutil/sysfs), cookbook.py (analyze/evaluate/recipes/install-recipe), integration.py (test — Engine-Verbindung), news.py (news — RSS-Aggregation via stdlib). Alle unter /api/*.

Frontend (static/, dünne Hülle + ES-Module, kein Build):

  • index.html — nur Gerüst: Sidebar-Nav, Topbar, Alert-Banner, ein .view-Container je Bereich (Hash-Routing). Lädt css/* und js/main.js als Modul.

  • Design-System (v3): EINE Akzentfarbe (Teal #2dd4bf) für alles Klickbare; Grün/Gelb/Rot nur für Status. Dichtes Control-Plane-Layout, Monospace-Zahlen, Bento. Keine Inline-Styles in Panels — alles über Klassen aus components.css (.card, .tile, .qa, .fit-badge, .modal-*, .badge, .bar/.meter, .chip …). Tokens in css/base.css (:root).

  • js/core/*api.js (Fetch + Token), ui.js (DOM-Helfer, Toast, Inline-Icon-Set, confirmModal/promptModal für Beginner-UX, fmtBytes/fmtPct), nav.js (beschrifteter View-Switch).

  • js/panels/* — ein Panel je Bereich (overview, models, server, cookbook, connect=Verbinden, news, jobs=Aktivität, guides). Panel-Vertrag: { id, mount?(), onStatus?(s), onJobs?(jobs), onSystem?(sys) }.

  • js/main.js — bootet Panels, pflegt Topbar/Alert + ehrlichen Security-Chip (status.secured), WebSocket für Live-Metriken (/api/system/stream, 2 Hz), Polling (/api/status 3 s, /api/jobs 1.5 s).

  • Leitprinzip UX: verständlich/idiotensicher — Klartext-Microcopy, Fachbegriffe übersetzt (CPU→Prozessor, VRAM→Grafikspeicher), geführte Aktionen, heikle Aktionen mit confirmModal (Klartext-Konsequenz).

  • mission-control.service — systemd-Unit (uvicorn auf Port 9000).

  • Konfiguration rein über Env-Vars: MC_LLAMA_SWAP_URL, MC_CONFIG_PATH, MC_MODELS_DIR, MC_CMD_TEMPLATE, MC_UPDATE_CMD, MC_DEFAULT_TTL, MC_TOKEN, MC_SOURCE_DIR (Self-Update-Quelle, Default ~/mission-control).

Der Stack drumherum (Kontext)

  • Bosgame M5: AMD Strix Halo (gfx1151), Ubuntu 26.04, Kernel 7.0, ~124 GB GTT-Speicher. LAN-IP 192.168.178.151.
  • Inferenz: vorgebaute llama.cpp-ROCm-Binaries → llama-swap auf *:8080 (LAN-offen, mit -watch-config) → die Guides-Integrationen funktionieren von anderen Geräten. Engine-Update via MC_UPDATE_CMD=/usr/local/bin/update-llamacpp (lädt llama.cpp-ROCm für gfx1151).
  • 3 Modelle / 5 Rollen: coder, scout, vision (Qwen3-Familie). Modelle werden geswappt, nicht parallel geladen.
  • Mission Control: Produktiv unter /opt/mission-control (als Dienst), Source-Repo unter ~/mission-control.

Entwickeln & Deployen

Wo entwickelt wird: primär auf einem Windows-PC (Repo z. B. F:\Coding Stuff\mission-control), Zielsystem ist der Linux-Bosgame. Lokal nur Smoke-Test (rendert/bootet sauber?), echter Funktionstest nur auf dem Bosgame (dort läuft llama-swap).

Lokaler Smoke-Test (Windows):

python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt
.venv/Scripts/python -m uvicorn app:app --port 9001

Ohne llama-swap ist alles im Offline-Zustand (rote Pill, Warn-Banner) — das ist erwartet.

Bosgame-Zugang (SSH, key-basiert, passwortlos):

ssh -i ~/.ssh/id_ed25519_hermes -o IdentitiesOnly=yes hitonabi@192.168.178.151

User ist hitonabi (klein!). sudo braucht generell ein Passwort — außer einer engen NOPASSWD-Whitelist für genau systemctl restart mission-control|llama-swap und journalctl (dadurch laufen Self-Update, Dienst-Restarts und Log-Stream passwortlos). OS-Update/Reboot fragen das Passwort weiterhin ab.

Self-Update statt manuellem Deploy: Der Button „Mission Control aktualisieren" (POST /api/self-update) macht git pull (Source) → rsync → systemctl restart — der dokumentierte manuelle Weg unten ist nur noch Fallback/Erstinstallation.

Deploy-Kette (Windows → Gitea → Bosgame):

  1. Lokal bauen + Smoke-Test → git push (origin = Gitea http://192.168.178.153:3000/Hitonabi/mission-control.git).
  2. Bosgame: cd ~/mission-control && git pull --ff-only (Source-Repo).
  3. Nach /opt ausrollen — ohne sudo (hitonabi besitzt /opt/mission-control), .venv ausnehmen:
    rsync -a --exclude='.git' --exclude='.venv' --exclude='__pycache__' --exclude='*.pyc' ~/mission-control/ /opt/mission-control/
    
  4. sudo systemctl restart mission-control (Passwort nötig). Prod = :9000, Dev-Spielwiese = :9001.
  5. Niemals direkt in /opt arbeiten. Logs: journalctl -u mission-control -f.

Statische Dateien werden je Request frisch von Platte gelesen → UI-Änderungen wirken schon nach rsync; Python-Code-Änderungen brauchen den Restart.

Konventionen

  • KISS über alles: kein schweres Framework, kein Build-Schritt, solange es ohne geht.
  • SoC ohne Build: neuer Bereich = ein routers/<bereich>.py (FastAPI-Router) + ein static/js/panels/<bereich>.js (ES-Modul nach Panel-Vertrag) + ein Nav-Eintrag in index.html. Gemeinsame Backend-Logik in config/auth/jobengine/llamaswap, gemeinsame Frontend-Logik in js/core/*. Keine schweren Libs, kein CDN — nur relative imports.
  • Endpoint-URLs bleiben unter /api/*; neue Bereiche degradieren sauber, wenn ihre Quelle (sysfs, amd-smi, systemctl) fehlt (z. B. beim Entwickeln auf Windows).
  • Funktion darf nicht von localStorage abhängen (nur das Token-Feld nutzt es, das ist ok).
  • Sicherheit: Das Backend führt Shell-Befehle aus → ausschließlich im vertrauenswürdigen LAN betreiben, niemals offen ins Internet.

Gotchas (wichtig!)

  • ${PORT} in der generierten llama-swap-Config muss literal stehen bleiben → beim Bauen des cmd-Strings str.replace benutzen, NICHT .format (sonst KeyError auf PORT).
  • llama-swap muss mit -watch-config laufen, sonst greift das Auto-Einpflegen neuer Modelle nicht.
  • HuggingFace-Downloads mit HF_HUB_DISABLE_XET=1 (sonst reproduzierbarer Hänger bei ~6 MB).
  • Vision-Modelle in llama.cpp brauchen zusätzlich --mmproj <projektor> und --jinja.

Gotchas v3 (zusätzlich)

  • .qa ist ein <button> → die globale button.danger-Regel (Vollrot) schlägt durch. Für gefährliche Aktionszeilen .qa-danger nutzen (rotes Icon), NICHT .danger.
  • CSS rückwärtskompatibel halten: Panels werden schrittweise migriert; beim Token-/Klassen-Umbau alte Klassennamen + Vars (--hi, --red, --red-dim) bedienen, sonst brechen noch nicht migrierte Panels.
  • Browser-ESM-Cache: Beim lokalen Testen bustet ein Soft-Reload den Modul-Cache nicht zuverlässig → Preview-Server neu starten oder cache-bustend dynamisch importieren. In Prod erledigt das die no-cache-Middleware (einmal Strg+Shift+R nach dem ersten Deploy).
  • Gitea-Push kann transient mit „Failed to authenticate user" fehlschlagen (Server kurz weg) → einfach erneut versuchen.

Projektstatus & Roadmap

v3 ist umgesetzt & live (siehe ROADMAP.md): einheitliches Design-System, Beginner-UX (Klartext/Führung), Security-Härtung (Passwort-Leak dicht, ehrlicher Chip), Self-Update-Button.

v4 ist umgesetzt & live („Der Lotse" — siehe ROADMAP.md): (1) optimale Kontextfenster auto-ermittelt (hw_math.max_ctx_for, in Cookbook + Modelle-Konfig) → (2) Cookbook 2.0 use-case-getrieben mit Stack-Empfehlungen (recipes.py, „Komplettes Setup installieren") → (3) „Verbinden"-Tab mit Connection-Test + Bild-/Vision-Flow → (4) News-Board (RSS via stdlib). Alles automatisch aus Modellen + Hardware; KISS/SoC blieb (keine DB, keine neue Lib).

v5 ist umgesetzt & live („Anfänger-Lotse"): Klartext-Erklärungen (Swapping/Spitzenbedarf, Kontext-ⓘ, Guides-Neubau), aktuelle Juni-2026-Modelle (Rezepte + dynamische GGUF-Auflösung _pick_gguf), GGUF/MCP erklärt, empfohlene Tools + HF-Token (Einstellungen), News aus Qualitätsquellen + Upgrade-Vorschläge (/api/cookbook/upgrades + /install-model, UPGRADES in recipes.py).

v6 ist umgesetzt & live: Stack zeigt echtes Modell; Top-News + Toolbar-Update-Badges (/api/updates); News-Magazin-Layout; Guide neu mit Tutorials/Workflows + Konzept-Karten; Cookbook-Modelle diversifiziert best-in-class (Qwen3/Gemma/Mistral/DeepSeek, neue Kategorie „Nachdenken & Logik") + „Beste Wahl für dein System" (recommended_id). Wir sind im Feinschliff- und Wartungsmodus.

Nordstern: den Server nie wieder via SSH/Putty anfassen müssen — 100 % Automatisierung / Klicki-Bunti.