Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
11 KiB
Mission Control
Web-Dashboard zur Verwaltung eines lokalen LLM-Stacks (llama-swap) auf dem Bosgame M5.
FastAPI-Backend + Svelte 5-Frontend (Vite-Build). Leitprinzip: KISS — SoC ohne Overhead.
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 +Cache-Control: no-cache-Middleware.config.py— alle Env-Vars + die gemeinsameruamel.yaml-Instanz. AuchSOURCE_DIR/PROD_DIRfürs Self-Update.auth.py— optionale Token-Auth: HeaderX-MC-Tokenoder Query?token=(Query ist Pflicht für WebSockets).jobengine.py— In-Memory-Job-System (Threads + Subprocess) mit Live-Log. Secrets gehen über stdin (kein Leak in Log/ps).llamaswap.py— sprichtllama-swapan und liest/schreibt dessenconfig.yamlperruamel.yaml.hw_math.py— Odysseus-Fit-Mathe: VRAM/RAM-Bedarf, tps-Schätzung,max_ctx_for,extract_params_b.recipes.py— kuratierte Use-Case-Stacks fürs Cookbook 2.0.mcp_memory.py— stdio-MCP-Server: Gedächtnis-Tools für Cline/OpenCode/Claude Code.routers/*.py— ein Router je Bereich:models.py— status/download/register/update_model/unload/chatjobs.py— Jobs-Listemaintenance.py— update/self-update/os-update/service-restart/reboot/logs-WebSocketsystem.py— status + stream-WebSocket (Live-Metriken psutil/sysfs)cookbook.py— analyze/evaluate/recipes/install-recipe/upgradesintegration.py— test (Engine-Verbindungstest)news.py— RSS-Aggregation via stdlibmemory.py— Gedächtnis-CRUD (SQLite, WAL-Mode), 5 Kategorienhermes.py— Chat-WS proxyt zum Hermes-Agent-Server (:8642) + transcribe(Whisper)/tts(Piper)/status/pubkey + Cockpit-Reads agent/cron/skills (Phase 4)hermes_ui.py— Reverse-Proxy auf das eingebettete Hermes-Web-Dashboard (/hermes-ui/, HTTP + WS-Bridgepty/ws/pub/events,X-Forwarded-Prefix). Der Chat lebt jetzt hier (iframe), nicht mehr im Eigenbau.
hermes_control.py— read-only Control-Plane: liest Agent-Status (gateway_state.json+ Cron-Heartbeat), Cron-Jobs & Skills viahermes-CLI (COLUMNS=400, Rich-Tabelle geparst). TTL-Cache.model_caps.py— Modell-Capability-Tags (Phase 10): dependency-freier GGUF-Header-Reader (architecture/expert_count/context_length/parameter_count, offline) + cmd-Flags (--jinja/--mmproj) + Familien-Fallback → MoE/Tools(yes|likely|no)/Vision/Coder/Reasoning/Embedding. Genutzt vonmodels.py(meta.capabilities).
Frontend (frontend/src/, Svelte 5 + Vite, Build → static/dist/):
index.html— Gerüst: Sidebar-Nav (10 Tabs), Topbar, Alert-Banner, View-Container je Bereich.frontend/src/main.ts— bootet alle Svelte-Panels, Topbar/Alert, WebSocket Live-Metriken, Polling.frontend/src/panels/— je ein Panel: Overview, Models, Jobs(Aktivität), Server, Cookbook, Connect(Verbinden), News, Guides, Memory(Gedächtnis), Hermes.frontend/src/stores/— Svelte 5 Runes:status.svelte.ts,jobs.svelte.ts,system.svelte.ts.- Design-System (v3): EINE Akzentfarbe Teal
#2dd4bf. Grün/Gelb/Rot nur für Status. Tokens incss/base.css.
Der Stack drumherum
- Bosgame M5: AMD Strix Halo (gfx1151), Ubuntu 26.04, Kernel 7.0, ~124 GB GTT. LAN-IP
192.168.178.151. - Inferenz: llama.cpp-ROCm-Binaries →
llama-swapauf*:8080(LAN-offen,-watch-config). - 3 Modelle / 5 Rollen:
coder(Qwen3-30B-A3B),scout(Qwen3-8B),vision(Qwen3-VL). - Mission Control: Produktiv unter
/opt/mission-control, Source unter~/mission-control. - Gedächtnis: SQLite unter
/srv/models/mission-control-memory.db(5 Kategorien: user/instruction/stable/versioned/ephemeral). - Hermes Agent: aktuell Eigenbau als Teil von Mission Control; ab v9 das Nous Hermes-Agent-Framework als eigener Dienst (
hermes-agent.service, OpenAI-API auf:8642, base_url → llama-swap, MCP-Client bindetmcp_memory.pyein). Voice (Whisper STT + Piper TTS) bleibt in Mission Control und wrappt :8642.
Entwickeln & Deployen
Wo entwickelt wird: Windows-PC (F:\Coding Stuff\mission-control), Ziel: Linux-Bosgame.
Lokal nur Smoke-Test (rendert/bootet?), echter Funktionstest auf dem Bosgame.
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
Bosgame-Zugang:
ssh -i ~/.ssh/id_ed25519_hermes -o IdentitiesOnly=yes hitonabi@192.168.178.151
User: hitonabi. sudo NOPASSWD-Whitelist für systemctl restart mission-control|llama-swap und journalctl.
Frontend-Build:
cd frontend && npm run build # vor jedem git push
Output: static/dist/main.js + static/dist/main.css — wird committet (kein Build-Schritt auf dem Server).
Deploy-Kette:
npm run build → git push (Gitea :3000) → Bosgame: git pull → rsync → systemctl restart
Oder über den Self-Update-Button in Mission Control (empfohlen).
Self-Update-Button (POST /api/self-update): git pull → rsync → restart — alles in einem Klick.
Konfiguration (Env-Vars)
| Variable | Default | Zweck |
|---|---|---|
MC_LLAMA_SWAP_URL |
http://127.0.0.1:8080 |
llama-swap URL |
MC_CONFIG_PATH |
/etc/llama-swap/config.yaml |
llama-swap Config |
MC_MODELS_DIR |
/srv/models |
GGUF-Ablageort |
MC_TOKEN |
leer | Auth-Token (optional, LAN-only) |
MC_UPDATE_CMD |
leer | Engine-Update-Befehl |
MC_DEFAULT_TTL |
300 |
Sekunden bis Auto-Unload |
MC_SOURCE_DIR |
~/mission-control |
Self-Update Quelle |
MC_MEMORY_DB |
{MODELS_DIR}/mission-control-memory.db |
SQLite Gedächtnis |
HERMES_API_URL |
http://127.0.0.1:8642/v1 |
Hermes-Agent-Server (OpenAI-API) |
HERMES_API_KEY |
aus ~/.hermes/.env |
API-Key des Hermes-Servers (sonst Env) |
HERMES_HOME |
~/.hermes |
Hermes-Agent State-Dir (Cockpit-Reads) |
HERMES_BIN |
~/.local/bin/hermes |
Hermes-CLI (Cron/Skills-Reads) |
HERMES_DASHBOARD_URL |
http://127.0.0.1:9119 |
Hermes-Web-Dashboard (eingebettet via /hermes-ui/) |
HERMES_WINDOWS_HOST |
leer | Windows-PC IP für SSH |
HERMES_WINDOWS_USER |
TobisPC |
Windows SSH-Username |
HERMES_SSH_KEY |
~/.ssh/id_ed25519_hermes_agent |
SSH-Key für Hermes |
PIPER_BIN |
/opt/mission-control/piper/piper |
Piper TTS Binary |
PIPER_VOICE |
.../de_DE-kerstin-low.onnx |
Piper Stimm-Modell |
WHISPER_MODEL |
medium |
Whisper Modell-Größe |
Konventionen
- Neuer Bereich:
routers/<bereich>.py+frontend/src/panels/<Bereich>Panel.svelte+ Nav/View inindex.html+ Import inmain.ts. Danachnpm run build. - Backend SoC: gemeinsame Logik in
config/auth/jobengine/llamaswap/hw_math. Alles unter/api/*. - Sicherheit: Shell-Befehle → nur LAN. Secrets via stdin. Token optional.
- SQLite: WAL-Mode aktiv,
check_same_thread=False— ausreichend für Single-User. asyncio.get_running_loop()stattget_event_loop()(Python 3.10+ kompatibel).- Memory-Kategorien:
user|instruction|stable|versioned|ephemeral— Enum inrouters/memory.py.
Gotchas
${PORT}in llama-swap-Config →str.replacestatt.format(KeyError sonst).-watch-configbei llama-swap nötig für Auto-Einpflegen.HF_HUB_DISABLE_XET=1bei HuggingFace-Downloads (Hänger bei ~6 MB).- Vision-Modelle: brauchen
--mmproj <projektor>und--jinja. - Gitea-Push kann transient mit „Failed to authenticate" fehlschlagen → einfach nochmal.
- Piper TTS: Binary + Stimm-Modell müssen separat installiert werden (nicht via pip).
- Whisper: lädt beim ersten Aufruf ~1.4 GB Modell herunter (einmalig, dann gecacht).
- Hermes SSH-Key: muss auf dem Bosgame generiert werden (
~/.ssh/id_ed25519_hermes_agent). - system_status-Keys:
/api/system/statusliefert verschachtelt (cpu.percent,ram.usedin Bytes) — nicht flach. Konsumenten müssen entsprechend lesen. - Hermes-Server-Key:
routers/hermes.pyproxyt zu:8642und liest den Key bei Bedarf aus~/.hermes/.env(API_SERVER_KEY) — MC muss als Userhitonabilaufen, sonst kein Lesezugriff. - Hermes-Dashboard (Phase 5): braucht Extras
uv pip install --python ~/.hermes/hermes-agent/venv/bin/python -e ".[web,pty]"und einen einmaligen UI-Build (ersterhermes dashboard-Start ohne--skip-build; Output →hermes_cli/web_dist). Dienst:~/.config/systemd/user/hermes-dashboard.service(an127.0.0.1:9119gebunden,--skip-build). Wird unter/hermes-ui/perX-Forwarded-Prefixgeproxyt — nie ohne diesen Header proxen, sonst absolute Asset-Pfade kaputt./hermes-ui/*ist bewusst ohne MC-Token (LAN-only; Dashboard self-auth auf Loopback). - OpenCode Config: Datei heißt
opencode.jsonc(nicht.json), Key ist"providers"(Plural). - ConnectPanel hinter NPM-Proxy:
location.hostnamezeigt Proxy-Domain → LAN-IP Override im Verbinden-Tab setzen (localStorage).
Projektstatus
v7 (Memory Layer): SQLite-Gedächtnis mit 5 Kategorien + MCP-Server für alle Tools live.
v8 (Hermes Agent): Chat-Agent mit Voice (Whisper STT + Piper TTS), Tool Calling, WebSocket-Streaming live.
v8.1 (Bug-Fix): system_status-Keys, Model-Routing, asyncio, sudo-PW für Engine-Update, Chat-History in sessionStorage.
v8.2 (UX): Guide-Tabs + Begriffe oben, Mobile Bottom-Nav (≤520px), Download-Links, ConnectPanel-Fixes.
v8.3 (Memory Import): Import aus Cloud-KIs (Claude/Gemini/ChatGPT) — zeilenweiser Batch-POST.
v9 (Hermes-Agent-Adoption, Entscheidung 2026-06-23): Eigenbau-Agent → Nous Hermes-Agent-Framework (MIT). MC wird Control-Plane. Eigenes Memory bleibt via MCP erhalten. Phasen 0–3 live (harter Cutover: hermes_agent.py entfernt) + Phase 4: Agent-Cockpit (Status/Cron/Skills) + Phase 5 (Kurswechsel): Eigenbau-Chat ersetzt durch das eingebettete Hermes-Web-Dashboard (hermes-dashboard-Dienst :9119, MC-Reverse-Proxy /hermes-ui/, iframe) + Phase 6: volle lokale Rechte (approvals.mode:auto, hooks_auto_accept, subagent_auto_approve) + Phase 7: Agent-Modell Hermes 4 14B Q6_K (llama-swap-Alias hermes, --jinja) + Phase 8: Cockpit zeigt Lernen (USER.md) + Aktivität (hermes insights) + Phase 9: mcp_memory.py-Tool-Beschreibungen proaktiv → geteiltes Memory wächst autonom mit. Offen: Kurator/Dedup fürs MC-SQLite, Self-Update für hermes-gateway+hermes-dashboard. Siehe ROADMAP v9.
Memory-Architektur (entschieden 2026-06-24): Zwei komplementäre Schichten, keine Doppelung-Panik. MC-SQLite (via MCP) = geteilte „Verfassung" für ALLE Tools (Hermes, Cline, OpenCode, Zed) — kuratierbare, deterministische Fakten/Regeln; wächst autonom durch proaktives add_memory. Hermes-nativ (~/.hermes/memories/USER.md + state.db FTS5) = Hermes' privates Profil-/Gesprächsgedächtnis (nur Hermes). Linie: „soll das jedes Tool wissen?" → MC-SQLite; „Hermes' Eindruck/Gesprächsfaden" → nativ. Dokumenten-„Second Brain" (Obsidian/RAG) ist davon getrennt und nur bei Bedarf zu bauen.
Offene v8-Einrichtungsschritte (kein Code, nur Setup):
- Piper Binary + Kerstin-Stimme installieren (Befehle im Hermes-Tab → ⚙ Setup)
- Windows OpenSSH Server aktivieren + SSH-Key einrichten
Nordstern: Den Bosgame nie wieder via SSH/Putty anfassen — 100 % Automatisierung / Klicki-Bunti.