Architektur auf Separation of Concerns umgestellt – ohne Build-Schritt,
ohne neues Framework, ohne DB (KISS bleibt). Endpoint-URLs unveraendert,
daher 1:1-kompatibel zum bisherigen Stand.
Backend (Top-Level-Helfer + ein Router je Bereich):
- app.py auf duennen Einstieg reduziert (FastAPI + include_router + static)
- config/auth/jobengine/llamaswap als getrennte Helfer-Module
- Endpoints in routers/{models,jobs,maintenance}.py
Frontend (native ES-Module statt Single-File):
- index.html = Huelle: Sidebar-Nav, Topbar, Alert-Banner, Hash-Routing
- css/{base,components}.css – Tokens + Komponenten
- js/core/{api,ui,nav}.js + js/panels/{overview,models,maintenance,jobs}.js + main.js
- Panel-Vertrag: { id, mount?(), onStatus?(s), onJobs?(jobs) }
- Optik an docs/mission-control-overview.png angelehnt (Hero, KPI-Kacheln,
Listen, Aktivitaets-Stream, getoente Karten)
Doku: CLAUDE.md + README auf die neue Struktur aktualisiert.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.7 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: bautFastAPI, hängt die Router ein, liefert das statische UI aus, registriert den Exception-Handler. Sonst nichts.config.py— alle Env-Vars + die gemeinsameruamel.yaml-Instanz.auth.py— optionale Token-Auth (X-MC-Token).jobengine.py— In-Memory-Job-System (Threads + Subprocess) mit Live-Log; fährt Downloads/Updates.llamaswap.py— sprichtllama-swapan (/running,/v1/models, unload) und liest/schreibt dessenconfig.yamlperruamel.yaml(Kommentare bleiben erhalten).routers/*.py— ein Router je Bereich. Aktuell:models.py(status,download,register,unload,chat),jobs.py(jobs),maintenance.py(update). Alle Endpoints 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ädtcss/*undjs/main.jsals Modul. -
css/base.css— Design-Tokens (:root), Reset, App-Layout (Sidebar/Topbar/Content).css/components.css— Karten, KPI-Kacheln, Listen, Forms, Log, Toast. -
js/core/*—api.js(Fetch + Token),ui.js(DOM-Helfer, Toast, Icons),nav.js(View-Switch). -
js/panels/*— ein Panel je Bereich (overview,models,maintenance,jobs). Panel-Vertrag:{ id, mount?(), onStatus?(s), onJobs?(jobs) }. -
js/main.js— bootet Panels, pflegt Topbar/Alert, fährt das Polling (/api/status3 s,/api/jobs1.5 s) und verteilt an die Panels. -
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.
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-swapauf:8080(läuft mit-watch-config). - 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
- Niemals direkt in
/optarbeiten. Dev-Instanz aus der Source auf einem anderen Port starten:Danncd ~/mission-control && python3 -m venv .venv && . .venv/bin/activate pip install -r requirements.txt uvicorn app:app --host 0.0.0.0 --port 9001 --reload:9001= Spielwiese,:9000= stabil. - Ausrollen:
sudo cp -r ~/mission-control/. /opt/mission-control/ && sudo systemctl restart mission-control - Logs:
journalctl -u mission-control -f
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) + einstatic/js/panels/<bereich>.js(ES-Modul nach Panel-Vertrag) + ein Nav-Eintrag inindex.html. Gemeinsame Backend-Logik inconfig/auth/jobengine/llamaswap, gemeinsame Frontend-Logik injs/core/*. Keine schweren Libs, kein CDN — nur relativeimports. - 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
localStorageabhä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 generiertenllama-swap-Config muss literal stehen bleiben → beim Bauen des cmd-Stringsstr.replacebenutzen, NICHT.format(sonst KeyError aufPORT).llama-swapmuss mit-watch-configlaufen, 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.
Roadmap
Siehe @ROADMAP.md für die v2-Planung. Nordstern: den Server nie wieder via SSH/Putty anfassen müssen — 100 % Automatisierung / Klicki-Bunti.