fix+docs: Bug-Fixes, vollstaendige Dokumentation v8

Bug-Fixes:
- hermes_agent.py: asyncio.get_event_loop() → get_running_loop() (Python 3.10+)
- routers/hermes.py: Thread-Lock fuer Whisper-Init, HERMES_WINDOWS_USER Import,
  Piper stderr logging, __import__ Anti-Pattern entfernt
- routers/memory.py: SQLite WAL-Mode, Kategorie-Enum-Validierung (user/instruction/stable/versioned/ephemeral)
- HermesPanel.svelte: findLast() → reverse().find() (Browser-Kompatibilitaet)
- ConnectPanel.svelte: Hardcoded Username durch Platzhalter ersetzt

Docs:
- CLAUDE.md: komplett aktualisiert (v7+v8, Hermes, Memory, alle Env-Vars)
- ROADMAP.md: v7+v8 als erledigt, naechste Features (v8.1-v9.1)
- README.md: komplett neu geschrieben (Agentic OS Konzept, alle Features)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-23 14:48:22 +02:00
parent 5881a21d9a
commit 8539999627
9 changed files with 269 additions and 313 deletions
+86 -90
View File
@@ -1,127 +1,123 @@
# Mission Control # Mission Control
Web-Dashboard zur Verwaltung eines **lokalen LLM-Stacks (`llama-swap`)** auf dem Bosgame M5. 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. FastAPI-Backend + Svelte 5-Frontend (Vite-Build). **Leitprinzip: KISS — SoC ohne Overhead.**
## Architektur ## Architektur
**Backend** (Top-Level-Helfer + ein Router je Bereich): **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). - **`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 gemeinsame `ruamel.yaml`-Instanz. Auch `SOURCE_DIR`/`PROD_DIR` fürs Self-Update. - **`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). - **`auth.py`** — optionale Token-Auth: Header `X-MC-Token` **oder** Query `?token=` (Query ist Pflicht für WebSockets).
- **`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`). - **`jobengine.py`** — In-Memory-Job-System (Threads + Subprocess) mit Live-Log. Secrets gehen über **stdin** (kein Leak in Log/`ps`).
- **`llamaswap.py`** — spricht `llama-swap` an (`/running`, `/v1/models`, unload) und liest/schreibt dessen `config.yaml` per `ruamel.yaml` (Kommentare bleiben erhalten). - **`llamaswap.py`** — spricht `llama-swap` an und liest/schreibt dessen `config.yaml` per `ruamel.yaml`.
- **`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. - **`hw_math.py`** — Odysseus-Fit-Mathe: VRAM/RAM-Bedarf, tps-Schätzung, `max_ctx_for`, `extract_params_b`.
- **`recipes.py`** — kuratierte Use-Case-Stacks (Daten, kein Code) fürs Cookbook 2.0. - **`recipes.py`** — kuratierte Use-Case-Stacks 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/*`. - **`hermes_agent.py`** — Kern des Hermes-Agenten: Tool-Definitionen, Tool-Dispatcher, LLM-Loop (ReAct), Modell-Routing.
- **`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/chat
- `jobs.py` — Jobs-Liste
- `maintenance.py` — update/self-update/os-update/service-restart/reboot/logs-WebSocket
- `system.py` — status + stream-WebSocket (Live-Metriken psutil/sysfs)
- `cookbook.py` — analyze/evaluate/recipes/install-recipe/upgrades
- `integration.py` — test (Engine-Verbindungstest)
- `news.py` — RSS-Aggregation via stdlib
- `memory.py` — Gedächtnis-CRUD (SQLite, WAL-Mode), 5 Kategorien
- `hermes.py` — Chat-WebSocket/transcribe(Whisper)/tts(Piper)/status/pubkey
**Frontend** (`static/`, dünne Hülle + ES-Module, kein Build): **Frontend** (`frontend/src/`, Svelte 5 + Vite, Build → `static/dist/`):
- **`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. - **`index.html`** — Gerüst: Sidebar-Nav (10 Tabs), Topbar, Alert-Banner, View-Container je Bereich.
- **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`). - **`frontend/src/main.ts`** — bootet alle Svelte-Panels, Topbar/Alert, WebSocket Live-Metriken, Polling.
- **`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). - **`frontend/src/panels/`** — je ein Panel: Overview, Models, Jobs(Aktivität), Server, Cookbook, Connect(Verbinden), News, Guides, Memory(Gedächtnis), Hermes.
- **`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) }`. - **`frontend/src/stores/`** — Svelte 5 Runes: `status.svelte.ts`, `jobs.svelte.ts`, `system.svelte.ts`.
- **`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). - **Design-System (v3):** EINE Akzentfarbe Teal `#2dd4bf`. Grün/Gelb/Rot nur für Status. Tokens in `css/base.css`.
- **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). ## Der Stack drumherum
- **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. LAN-IP `192.168.178.151`.
- **Inferenz**: llama.cpp-ROCm-Binaries → `llama-swap` auf `*:8080` (LAN-offen, `-watch-config`).
- **Bosgame M5**: AMD Strix Halo (gfx1151), Ubuntu 26.04, Kernel 7.0, ~124 GB GTT-Speicher. LAN-IP `192.168.178.151`. - **3 Modelle / 5 Rollen**: `coder` (Qwen3-30B-A3B), `scout` (Qwen3-8B), `vision` (Qwen3-VL).
- **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). - **Mission Control**: Produktiv unter `/opt/mission-control`, Source unter `~/mission-control`.
- **3 Modelle / 5 Rollen**: `coder`, `scout`, `vision` (Qwen3-Familie). Modelle werden geswappt, nicht parallel geladen. - **Gedächtnis**: SQLite unter `/srv/models/mission-control-memory.db` (5 Kategorien: user/instruction/stable/versioned/ephemeral).
- **Mission Control**: Produktiv unter `/opt/mission-control` (als Dienst), Source-Repo unter `~/mission-control`. - **Hermes Agent**: läuft als Teil von Mission Control (kein separater Dienst), Voice via Whisper (CPU) + Piper TTS.
## Entwickeln & Deployen ## Entwickeln & Deployen
**Wo entwickelt wird:** primär auf einem **Windows-PC** (Repo z. B. `F:\Coding Stuff\mission-control`), **Wo entwickelt wird:** Windows-PC (`F:\Coding Stuff\mission-control`), Ziel: Linux-Bosgame.
Zielsystem ist der **Linux-Bosgame**. Lokal nur Smoke-Test (rendert/bootet sauber?), echter Lokal nur Smoke-Test (rendert/bootet?), echter Funktionstest auf dem Bosgame.
Funktionstest nur auf dem Bosgame (dort läuft llama-swap).
**Lokaler Smoke-Test (Windows):** **Lokaler Smoke-Test (Windows):**
```bash ```bash
python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt python -m venv .venv && .venv/Scripts/python -m pip install -r requirements.txt
.venv/Scripts/python -m uvicorn app:app --port 9001 .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):** **Bosgame-Zugang:**
```bash ```bash
ssh -i ~/.ssh/id_ed25519_hermes -o IdentitiesOnly=yes hitonabi@192.168.178.151 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. User: `hitonabi`. sudo NOPASSWD-Whitelist für `systemctl restart mission-control|llama-swap` und `journalctl`.
**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. **Frontend-Build:**
**Frontend-Build (Vite + Svelte 5, ab Phase C):**
```bash ```bash
cd frontend && npm install # einmalig cd frontend && npm run build # vor jedem git push
npm run build # vor jedem git push — Output: static/dist/main.js
``` ```
Das Build-Ergebnis (`static/dist/`) wird committet kein Build-Schritt auf dem Server. Output: `static/dist/main.js` + `static/dist/main.css` wird committet (kein Build-Schritt auf dem Server).
Beim Entwickeln: `npm run dev` in `frontend/` startet den Vite Dev-Server (Port 5173) für HMR.
Neue Svelte-Panels kommen in `frontend/src/panels/`. Sobald ein Panel migriert ist,
wird es in `frontend/src/main.ts` importiert und das alte `static/js/panels/<panel>.js` nicht mehr gebraucht.
**Deploy-Kette (Windows → Gitea → Bosgame):** **Deploy-Kette:**
1. Frontend bauen: `cd frontend && npm run build``static/dist/main.js` aktualisiert. ```
2. Lokal Smoke-Test`git push` (origin = Gitea `http://192.168.178.153:3000/Hitonabi/mission-control.git`). npm run build → git push (Gitea :3000) → Bosgame: git pull → rsync → systemctl restart
3. Bosgame: `cd ~/mission-control && git pull --ff-only` (Source-Repo). ```
4. Nach `/opt` ausrollen — **ohne sudo**, `node_modules` ausnehmen: Oder über den Self-Update-Button in Mission Control (empfohlen).
```bash
rsync -a --exclude='.git' --exclude='.venv' --exclude='__pycache__' --exclude='*.pyc' --exclude='frontend/node_modules' ~/mission-control/ /opt/mission-control/
```
5. `sudo systemctl restart mission-control` (Passwort nötig). Prod = `:9000`, Dev-Spielwiese = `:9001`.
6. **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 **Self-Update-Button** (`POST /api/self-update`): git pull → rsync → restart — alles in einem Klick.
rsync; **Python-Code-Änderungen brauchen den Restart**.
## 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_SIMPLE_MODEL` | `scout` | Modell für einfache Tasks |
| `HERMES_COMPLEX_MODEL` | `coder` | Modell für komplexe Tasks |
| `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 ## Konventionen
- **Frontend: Vite + Svelte 5** (Phase C). Neuer Bereich = `frontend/src/panels/<Bereich>Panel.svelte` + `routers/<bereich>.py` + `initNav`-Eintrag in `index.html`. Danach `npm run build` in `frontend/` → `static/dist/main.js` wird committet. - **Neuer Bereich**: `routers/<bereich>.py` + `frontend/src/panels/<Bereich>Panel.svelte` + Nav/View in `index.html` + Import in `main.ts`. Danach `npm run build`.
- Stores: `frontend/src/stores/status.svelte.ts`, `jobs.svelte.ts`, `system.svelte.ts` — Svelte 5 Runes (`$state`). - **Backend SoC**: gemeinsame Logik in `config/auth/jobengine/llamaswap/hw_math`. Alles unter `/api/*`.
- Statische Hilfsfunktionen in `static/js/core/` (api.js, ui.js, nav.js) bleiben als gemeinsame Basis — werden von Vite gebündelt, nicht direkt geladen. - **Sicherheit**: Shell-Befehle → nur LAN. Secrets via stdin. Token optional.
- **Backend SoC**: ein `routers/<bereich>.py` je Bereich, gemeinsame Logik in `config/auth/jobengine/llamaswap/hw_math`. - **SQLite**: WAL-Mode aktiv, `check_same_thread=False` — ausreichend für Single-User.
- Endpoint-URLs bleiben unter `/api/*`; neue Bereiche degradieren sauber, wenn ihre Quelle (sysfs, `amd-smi`, `systemctl`) fehlt (z. B. beim Entwickeln auf Windows). - **`asyncio.get_running_loop()`** statt `get_event_loop()` (Python 3.10+ kompatibel).
- Funktion darf **nicht** von `localStorage` abhängen (nur das Token-Feld nutzt es, das ist ok). - **Memory-Kategorien**: `user` | `instruction` | `stable` | `versioned` | `ephemeral` — Enum in `routers/memory.py`.
- **Sicherheit**: Das Backend führt Shell-Befehle aus → ausschließlich im vertrauenswürdigen LAN betreiben, niemals offen ins Internet.
## Gotchas (wichtig!) ## Gotchas
- **`${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`). - **`${PORT}`** in llama-swap-Config `str.replace` statt `.format` (KeyError sonst).
- `llama-swap` muss mit **`-watch-config`** laufen, sonst greift das Auto-Einpflegen neuer Modelle nicht. - **`-watch-config`** bei llama-swap nötig für Auto-Einpflegen.
- HuggingFace-Downloads mit **`HF_HUB_DISABLE_XET=1`** (sonst reproduzierbarer Hänger bei ~6 MB). - **`HF_HUB_DISABLE_XET=1`** bei HuggingFace-Downloads (Hänger bei ~6 MB).
- Vision-Modelle in llama.cpp brauchen zusätzlich **`--mmproj <projektor>` und `--jinja`**. - **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`).
## Gotchas v3 (zusätzlich) ## Projektstatus
- **`.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`. **v7 (Memory Layer)**: SQLite-Gedächtnis mit 5 Kategorien + MCP-Server für alle Tools live.
- **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. **v8 (Hermes Agent)**: Chat-Agent mit Voice (Whisper STT + Piper TTS), Tool Calling, WebSocket-Streaming live.
- **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). Offene v8-Schritte: Piper-Binary installieren, Windows OpenSSH Server + SSH-Key einrichten.
- **Gitea-Push** kann transient mit „Failed to authenticate user" fehlschlagen (Server kurz weg) → einfach erneut versuchen.
## Projektstatus & Roadmap **Nordstern:** Den Bosgame nie wieder via SSH/Putty anfassen — 100 % Automatisierung / Klicki-Bunti.
**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.
+83 -57
View File
@@ -1,85 +1,111 @@
# Mission Control # Mission Control
Eine schlanke Steuerzentrale für deinen lokalen `llama-swap`-Stack auf dem Bosgame M5. Persönliche Steuerzentrale für einen lokalen LLM-Stack auf dem Bosgame M5.
Ein FastAPI-Backend + ein HTML-Dashboard. Kein Build-Schritt, keine Datenbank. FastAPI-Backend + Svelte 5-Frontend. Kein Cloud-Zwang, keine Datenbank-Abhängigkeit.
## Was sie kann ## Was es kann
- **Modelle & Ports** sehen — liest `llama-swap` (`/running`, `/v1/models`) + deine `config.yaml` **LLM-Stack verwalten**
- **Modell holen** — lädt eine GGUF-Datei von HuggingFace (`hf download`) als Hintergrund-Job mit Live-Log - Modelle & Status sehen (llama-swap `/running`, `/v1/models`, `config.yaml`)
- **Einpflegen** — schreibt das Modell automatisch in deine `config.yaml`; `llama-swap` lädt mit `-watch-config` neu - Modelle von HuggingFace laden — als Hintergrund-Job mit Live-Log
- **Wartung** — Container/Toolbox aktualisieren, Modelle aus dem Speicher werfen - Automatisch in llama-swap einpflegen (`-watch-config`)
- **Schnelltest** — Chat-Box, um ein Modell zu wecken und zu prüfen - Optimale Kontextfenster berechnen (hardware-aware, `hw_math`)
Was sie **bewusst nicht** macht: Chat-Logs, Inferenz-Monitoring im Detail — dafür hat `llama-swap` **Cookbook**
schon `/ui` und `/log`. Mission Control ergänzt nur, was fehlt. - Use-Case-getrieben: Coding · Chat · Vision · Agenten · Schnell & sparsam
- Hardware-Ampel pro Setup, „Beste Wahl für dein System"
- 1-Klick „Komplettes Setup installieren"
**Wartung & Server**
- OS-Updates, Dienste steuern (llama-swap, Mission Control), Reboot
- Live-Logs (journalctl via WebSocket)
- Self-Update-Button (git pull → rsync → restart)
**Verbinden**
- Copy-Paste-Configs für Cline, OpenCode, Zed, Continue, Jan AI, OpenWebUI
- Gedächtnis-MCP: alle Tools teilen dasselbe persistente Gedächtnis
**Gedächtnis** *(v7)*
- SQLite-Datenbank mit 5 Kategorien: `user` · `instruction` · `stable` · `versioned` · `ephemeral`
- MCP-Server (`mcp_memory.py`): Cline, OpenCode, Claude Code teilen dasselbe Gedächtnis
- Temporäre Einträge (ephemeral) nach 7 Tagen automatisch gelöscht
**Hermes Agent** *(v8)*
- Lokaler KI-Assistent direkt im Browser — Text und Sprache
- Whisper STT (lokal, ROCm-beschleunigt) + Piper TTS (weibliche Stimme Kerstin)
- Tool Calling: Dateien lesen, Befehle ausführen, System abfragen, Web suchen
- Gedächtnis-Integration: Hermes kennt dich und deinen Stack von Anfang an
- Setup-Wizard für Piper + Windows SSH-Zugriff
## Voraussetzungen ## Voraussetzungen
- Python 3.11+ - Python 3.11+
- `hf` CLI installiert (`pip install -U "huggingface_hub[cli]"`) - `llama-swap` laufend mit `-watch-config`
- ein laufendes `llama-swap` — gestartet **mit `-watch-config`**, sonst greift das Auto-Einpflegen nicht - `hf` CLI (`pip install -U "huggingface_hub[cli]"`)
- Für Voice: `faster-whisper` + Piper Binary (Anleitung im Hermes-Tab)
## Installation ## Schnellstart (Bosgame)
```bash ```bash
sudo mkdir -p /opt/mission-control && sudo chown $USER /opt/mission-control sudo mkdir -p /opt/mission-control && sudo chown $USER /opt/mission-control
cp -r *.py routers static /opt/mission-control/ git clone http://192.168.178.153:3000/Hitonabi/mission-control.git ~/mission-control
rsync -a --exclude='.git' ~/mission-control/ /opt/mission-control/
cd /opt/mission-control cd /opt/mission-control
python3 -m venv .venv && . .venv/bin/activate python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
pip install -r requirements.txt
uvicorn app:app --host 0.0.0.0 --port 9000
``` ```
Dann im Browser im LAN: `http://<bosgame-ip>:9000` Als Dienst:
### Als Dienst (Autostart)
Pfade in `mission-control.service` anpassen, dann:
```bash ```bash
sudo cp mission-control.service /etc/systemd/system/ sudo cp mission-control.service /etc/systemd/system/
sudo systemctl daemon-reload sudo systemctl daemon-reload && sudo systemctl enable --now mission-control
sudo systemctl enable --now mission-control
``` ```
## Konfiguration (Umgebungsvariablen) Browser im LAN: `http://192.168.178.151:9000`
## Konfiguration
Alle Einstellungen über Umgebungsvariablen in der systemd-Unit:
| Variable | Default | Zweck | | Variable | Default | Zweck |
|---|---|---| |---|---|---|
| `MC_LLAMA_SWAP_URL` | `http://127.0.0.1:8080` | wo `llama-swap` lauscht | | `MC_LLAMA_SWAP_URL` | `http://127.0.0.1:8080` | llama-swap URL |
| `MC_CONFIG_PATH` | `/etc/llama-swap/config.yaml` | die `llama-swap`-Config | | `MC_CONFIG_PATH` | `/etc/llama-swap/config.yaml` | llama-swap Config |
| `MC_MODELS_DIR` | `/srv/models` | wohin GGUFs geladen werden | | `MC_MODELS_DIR` | `/srv/models` | GGUF-Ablageort |
| `MC_CMD_TEMPLATE` | siehe unten | Startbefehl pro Modell | | `MC_TOKEN` | leer | Auth-Token (optional) |
| `MC_UPDATE_CMD` | _(leer)_ | Befehl für „Container aktualisieren" | | `MC_UPDATE_CMD` | leer | Engine-Update-Befehl |
| `MC_DEFAULT_TTL` | `300` | Sekunden bis Auto-Unload | | `MC_SOURCE_DIR` | `~/mission-control` | Self-Update Quelle |
| `MC_TOKEN` | _(leer)_ | optionales Zugriffs-Token | | `MC_MEMORY_DB` | `{MODELS_DIR}/mission-control-memory.db` | Gedächtnis-Datenbank |
| `HERMES_SIMPLE_MODEL` | `scout` | Modell für einfache Tasks |
| `HERMES_COMPLEX_MODEL` | `coder` | Modell für komplexe Tasks |
| `HERMES_WINDOWS_HOST` | leer | Windows-PC IP für SSH-Zugriff |
| `PIPER_BIN` | `/opt/mission-control/piper/piper` | Piper TTS Binary |
| `PIPER_VOICE` | `.../de_DE-kerstin-low.onnx` | Piper Stimme |
| `WHISPER_MODEL` | `medium` | Whisper Modell-Größe |
### Wichtig: `MC_CMD_TEMPLATE` an deinen Start anpassen ## Sicherheit
Das ist der Befehl, der pro Modell in die `config.yaml` geschrieben wird. `{model}` und `{ctx}` - Ausschließlich im vertrauenswürdigen LAN betreiben
werden von Mission Control ersetzt, **`${PORT}` bleibt stehen** (das ersetzt `llama-swap` selbst). - `MC_TOKEN` setzen für minimalen Schutz
- Für Remote-Zugriff: SSH-Tunnel oder Tailscale, niemals direktes Port-Forwarding
- Hermes-SSH-Key hat nur Zugriff auf explizit freigegebene Windows-Pfade
## Architektur
Direkt auf dem Host (llama-server im PATH):
``` ```
llama-server -m {model} --host 127.0.0.1 --port ${PORT} -c {ctx} -ngl 999 -fa 1 --no-mmap Browser (Windows PC)
├── Svelte 5 UI (static/dist/main.js)
└── WebSocket (Live-Metriken, Hermes-Chat, Logs)
FastAPI (Bosgame :9000)
├── routers/models.py LLM-Verwaltung
├── routers/system.py Live-Metriken (psutil/sysfs)
├── routers/maintenance.py Updates, Logs, Neustart
├── routers/cookbook.py Modell-Empfehlungen
├── routers/memory.py Gedächtnis (SQLite)
├── routers/hermes.py Agent-Chat, Voice (Whisper/Piper)
└── ...
llama-swap (:8080) Inference (Qwen3-Familie)
SQLite Gedächtnis (/srv/models/mission-control-memory.db)
mcp_memory.py MCP-Server für Cline/OpenCode/Claude Code
``` ```
Über den kyuz0-Container (distrobox) — Beispiel, an deinen Toolbox-Namen anpassen:
```
distrobox enter llama-vulkan-radv -- llama-server -m {model} --host 127.0.0.1 --port ${PORT} -c {ctx} -ngl 999 -fa 1 --no-mmap
```
> `-fa 1` (Flash Attention) und `--no-mmap` sind auf Strix Halo Pflicht, sonst drohen Crashes/Slowdowns.
## ⚠️ Sicherheit
Mission Control führt Shell-Befehle aus (Downloads, Updates) und schreibt deine Config.
**Niemals offen ins Internet hängen.** Betrieb nur im vertrauenswürdigen LAN. Wenn du
trotzdem etwas Schutz willst, setz `MC_TOKEN` und trag es oben rechts im Dashboard ein.
Für echten Remote-Zugriff: per SSH-Tunnel oder Tailscale, nicht per Portfreigabe.
## API (falls du es skripten willst)
`GET /api/status` · `POST /api/download` · `POST /api/register` · `POST /api/unload[?model=]`
· `POST /api/update` · `POST /api/chat` · `GET /api/jobs` · `GET /api/jobs/{id}`
+64 -147
View File
@@ -1,165 +1,82 @@
# Mission Control - Roadmap v2 # Mission Control Roadmap
**Nordstern:** Den Bosgame nie wieder via SSH/Putty bedienen müssen. **100 % Automatisierung oder Klicki-Bunti.** **Nordstern:** Den Bosgame nie wieder via SSH/Putty bedienen. **100 % Automatisierung oder Klicki-Bunti.**
**Größeres Ziel:** Mission Control als persönliches **Agentic OS** — lokaler, privater KI-Assistent der das gesamte Setup kennt und steuert.
---
## v8 — Hermes Agent / Agentic OS Kern (✅ UMGESETZT & live, Stand 2026-06-23)
**Hermes**: dauerhafter lokaler KI-Assistent auf dem Bosgame. Text + Voice, Tool Calling, Gedächtnis.
- **`hermes_agent.py`**: ReAct-Agent mit Tool-Set (read_file, list_dir, run_command, system_status, memory r/w, web_search). Modell-Routing: scout für einfache Tasks, coder für komplexe.
- **`routers/hermes.py`**: WS `/chat` (streaming), POST `/transcribe` (Whisper STT, CPU), POST `/tts` (Piper TTS, weibliche Stimme Kerstin), GET `/status`, GET `/pubkey`.
- **HermesPanel.svelte**: Chat-UI mit Token-Streaming, Tool-Anzeige, Mikrofon-Button (MediaRecorder), TTS-Wiedergabe, Setup-Wizard (Windows SSH + Piper-Anleitung).
- **Setup-Wizard**: zeigt Schritt-für-Schritt Einrichtung für Piper + Windows OpenSSH.
**Offene Schritte (nicht Code, sondern Einrichtung):**
- [ ] Piper Binary + Kerstin-Stimme auf dem Bosgame installieren (Befehle im Setup-Wizard)
- [ ] Windows OpenSSH Server aktivieren (5 PowerShell-Befehle im Setup-Wizard)
- [ ] SSH-Key auf Bosgame generieren: `ssh-keygen -t ed25519 -C "hermes-agent@bosgame" -f ~/.ssh/id_ed25519_hermes_agent -N ""`
- [ ] `HERMES_WINDOWS_HOST` + `HERMES_WINDOWS_USER` in `mission-control.service` setzen
---
## v7 — Memory Layer / Gedächtnis (✅ UMGESETZT & live, Stand 2026-06-23)
Persistentes, tool-übergreifendes Gedächtnis für alle KI-Tools via MCP.
- **`routers/memory.py`**: SQLite CRUD + Export, WAL-Mode, 5 Kategorien mit Enum-Validierung.
- **`mcp_memory.py`**: stdio-MCP-Server — Tools: get/add/search/update/delete_memory. Einmalig in Cline/OpenCode/Claude Code einbinden.
- **MemoryPanel.svelte**: Gedächtnis-Tab mit Kategorie-Filter, Inline-Edit, Add-Formular.
- **ConnectPanel.svelte**: Gedächtnis-MCP Setup-Guide mit Config-Snippets.
- **5 Kategorien**: `user` (wer du bist) · `instruction` (Verhaltensregeln für alle Tools) · `stable` (Projektfakten) · `versioned` (Tech-Versionen) · `ephemeral` (7 Tage, dann weg).
--- ---
## v6 — Workflow & Politur (✅ UMGESETZT & live, Stand 2026-06-21) ## v6 — Workflow & Politur (✅ UMGESETZT & live, Stand 2026-06-21)
- **Dashboard/Toolbar:** „Dein Stack" zeigt echtes Modell hinter dem Alias; Top-News-Karte; - Dashboard zeigt echtes Modell hinter dem Alias; Toolbar-Update-Badges (OS + Modell-Upgrades).
Toolbar-Update-Badges (`/api/updates`: OS via apt + Modell-Upgrades). - News als Magazin-Layout; Guide neu mit Tutorials/Workflows + Konzept-Karten.
- **News & Guide entklobigt:** News als Magazin-Layout; Guide neu mit **Tutorials/Workflows** - Cookbook: Modelle diversifiziert (Qwen3/Gemma/Mistral/DeepSeek), „Beste Wahl für dein System".
(erster Workflow, Kontext leeren, „brauche ich einen Agenten?", Auto-Swap, Tools nutzen) +
kompakten Konzept-Karten.
- **Cookbook smart:** Modelle **diversifiziert** best-in-class (Qwen3-Coder, Gemma 3, Mistral Small,
DeepSeek-R1, Qwen3-VL) + neue Kategorie „Nachdenken & Logik"; „**Beste Wahl für dein System**"
hervorgehoben (`recommended_id`); GGUF beim Install dynamisch aufgelöst.
- **Erklärt:** Swapping/Spitzenbedarf-Klartext, „nur eins läuft gleichzeitig, kein Summieren".
--- ---
## v5 — „Anfänger-Lotse" (✅ UMGESETZT & live, Stand 2026-06-21) ## v5 — „Anfänger-Lotse" (✅ UMGESETZT & live)
## v4 — „Der Lotse" (✅ UMGESETZT & live)
## v3 — Redesign + Beginner-UX + Security (✅ UMGESETZT & live)
Wasserdicht erklären + aktualisieren + smart empfehlen (für blutige Anfänger). Details in Git-History.
- **Erklären:** Swapping-Klartext + Spitzenbedarf pro Modell (nur eins läuft gleichzeitig, kein Summieren);
Kontext-ⓘ-Tooltips; **Guides-Tab neu** (LLM/GGUF/Quant/Kontext/Swapping/MCP/Skills/Agenten).
- **Aktualisieren:** Rezepte auf Juni-2026-Modelle (Qwen3-Coder-30B-A3B, Qwen3-8B/30B, Qwen2.5-VL-7B);
GGUF-Datei beim Install **dynamisch aufgelöst** (`_pick_gguf`); „kein GGUF" entschärft + erklärt;
**Empfohlene Tools** in Verbinden (OpenCode/Cline/Continue + MCP); **HF-Token** in Einstellungen.
- **Smart:** News-Quellen aufgeräumt (Qualitäts-RSS statt Git-Tags); **Upgrade-Vorschläge**
(„Modell X ersetzt dein Y, weil …" → 1-Klick-Tausch via `/install-model`, `UPGRADES`-Map in recipes.py).
--- ---
## v4 — „Der Lotse" (✅ UMGESETZT & live, Stand 2026-06-21) ## Nächste Features (Vorschläge)
Sprung vom Modell-*Manager* zum geführten *Berater*. Alles **automatisch aus Modellen + Hardware**. ### v8.1 — Hermes Windows-SSH (Phase C)
KISS/SoC blieb: je Bereich ein `routers/<x>.py` + `js/panels/<x>.js` + Nav-Eintrag, keine DB, keine - [ ] Hermes kann Dateien auf Windows-PC lesen/schreiben via paramiko SSH
schwere Lib. **Alle 4 Schritte sind live** — Checklisten unten erledigt. - [ ] Tool: `read_file_windows(path)`, `write_file_windows(path, content)`, `run_command_windows(cmd)`
- [ ] Hermes updated opencode.json / Zed-Settings automatisch wenn neue Modelle verfügbar
- [ ] Mission Control Setup-Wizard: SSH-Test-Button mit grünem Haken
### 1. Optimale Kontextfenster (Fundament) ### v8.2 — `.well-known/opencode` Remote Config
- [x] `hw_math`: `max_ctx_for(params_b, quant, ram_gb)` — Umkehrfrage „welcher ctx passt noch?" - [ ] `GET /.well-known/opencode` — vollständige, dynamisch generierte OpenCode-Config (provider + models + MCP)
(KV-Cache-Bedarf vs. freier Speicher). - [ ] OpenCode Desktop lädt beim Start automatisch alle Configs von Mission Control
- [x] Pro Modell empfohlener ctx + Klartext-Begründung; Ein-Klick „optimal setzen" (nutzt `update_model`). - [ ] Einmalig: `OPENCODE_REMOTE_CONFIG=http://192.168.178.151:9000/.well-known/opencode` setzen
- [x] Speist Cookbook (2.) + Modelle-Tab.
### 2. Cookbook 2.0 — Use-Case-getrieben ### v9 — Agentic OS Orchestrierung
- [x] Einstieg „Wofür?": Coding · Bilder verstehen · Allrounder/Chat · Lange Dokumente · Agenten/Tool-Use · Schnell & sparsam. - [ ] Hermes kann geplante Tasks ausführen (Cron-artig, z.B. täglich Modell-Updates prüfen)
- [x] Pro Use-Case ein kuratiertes **Setup/Stack** (mehrere Modelle/Rollen) statt Einzelmodell, mit - [ ] Task-Queue: Aufgaben übergeben, Ergebnis später abholen
Hardware-**Ampel pro Setup** (via `hw_math`) + vorgeschlagenem ctx. Rezepte als Daten (`recipes.py`). - [ ] Push-Notifications: Hermes meldet sich wenn er fertig ist
- [x] „Komplettes Setup installieren" — download + register aller Modelle in einem Rutsch. - [ ] Multi-Step-Workflows: "Recherchiere X, schreibe Zusammenfassung, speichere ins Gedächtnis"
- [x] Roh-Suche bleibt als „Profi-Modus".
### 3. Verbinden / Orchestrierung (neuer Tab, erweitert Guides) ### v9.1 — Guides & Connect aktualisieren
- [x] Geführter Assistent je Tool (OpenCode/Cline/Cursor/OpenWebUI) mit exakter Config. - [ ] Zed als primäre Editor-Empfehlung (Inline-Completion + Agent, lokal)
- [x] **„Verbindung testen"** — `routers/integration.py` `/api/integration/test` ruft llama-swap - [ ] Antigravity entfernen oder als "Cloud-first, lokal wackelig" markieren
`/v1/models` (LAN-URL) und meldet Erfolg + Modell-Liste zurück. - [ ] Continue als "unsichere Zukunft (Cursor-Akquisition)" markieren
- [x] Bild-Eingaben für Fehleranalyse: erkennt Vision-Modell (sonst Hinweis „Vision installieren") + - [ ] `.well-known/opencode`-Snippet in Connect-Tab ergänzen
Copy-Snippet im OpenAI-Vision-Format. Auto-Swapping erklären.
### 4. News-Board (neuer Tab) ### Langfristig
- [x] `routers/news.py`: kuratierte RSS/Atom-Feeds (HuggingFace-Blog, r/LocalLLaMA, llama.cpp-Releases, - [ ] Hermes-Persönlichkeit konfigurierbar (Name, Tonalität) über Gedächtnis `instruction`-Einträge
Ollama) via `httpx` + stdlib-`xml.etree` (kein neuer Dep), In-memory-Cache (~30 min TTL). - [ ] Voice-to-Voice latency optimieren (Whisper ROCm wenn stable)
- [x] `panels/news.js`: Karten (Titel/Quelle/Datum/Link), sanitisiert, LAN-only; Hardware-relevante - [ ] Mehrsprachige TTS-Stimmen (EN + DE umschaltbar)
Releases (gfx1151/ROCm) hervorheben. - [ ] Hermes kann Mission Control selbst weiterentwickeln (Agentic self-improvement)
---
## v3 — Redesign + Beginner-UX + Security (Stand 2026-06-21, live)
Nach der funktional kompletten v2 (unten) umgesetzt — Fokus: **schick, selbsterklärend, wasserdicht.**
- **Design-System:** EINE Akzentfarbe (Teal), Bento, Monospace-Zahlen, beschriftete Sidebar; Inline-Styles
raus → alles über `components.css`. Mockup-getrieben (Übersicht + Cookbook).
- **Beginner-UX:** Klartext-Microcopy, Fachbegriffe übersetzt, menschliches Hero-Urteil, geführte Aktionen,
`confirmModal`/`promptModal` für heikle Aktionen, Fit-Ampel + Sortierung im Cookbook, Guides mit Kopier-Buttons.
- **Security:** sudo-Passwort über **stdin** (kein Leak mehr im Job-Log/`ps`), ehrlicher Security-Chip
(`status.secured`), WS-Token (Query). Bind bleibt `0.0.0.0` (LAN-only), Token optional.
- **Self-Update:** Button „Mission Control aktualisieren" (`/api/self-update`: git pull → rsync → restart) —
end-to-end verifiziert. `no-cache`-Middleware für sofortige UI-Wirkung nach Deploy.
- **Infra geklärt:** llama-swap lauscht `*:8080` (LAN); `MC_UPDATE_CMD` = llama.cpp-Engine-Update (≠ MC selbst).
Details/Konventionen: **CLAUDE.md**. Modus jetzt: Feinschliff & Bug-Hunting.
---
## Projektstand v2 (Stand 2026-06-20)
**✅ Schritt 1 erledigt & live auf `:9000`** — *SoC-Refactor + Design 2.0 als Fundament* (Commit `3649394`).
Backend in Helfer + `routers/*` zerlegt, Frontend in ES-Module (`js/core` + `js/panels`) + ausgelagertes
CSS, neues Dashboard-Layout (Sidebar-Nav, Topbar, Alert-Banner, Hero, KPI-Kacheln, Health-Signale,
Modell-Listen, Aktivitäts-Stream) angelehnt an `docs/mission-control-overview.png`. Bestehende Funktionen
1:1 migriert, Endpoint-URLs unverändert. Architektur + Deploy-Weg stehen in **CLAUDE.md**.
**✅ Schritt 2 erledigt & live** — *Feature 3: Live-Auslastung*. `system.py` Router mit `psutil` und sysfs für CPU/RAM/Disk/GPU/Temp.
**Reihenfolge (abgestimmt):** Design/Architektur zuerst (✅), dann Quick Wins, Security-Brocken zuletzt:
`1 (✅) Fundament → 2 (✅) Feature 3 Live-Auslastung → 3 (✅) Feature 6 Mehr LLM-Metriken → 4 (✅) Feature 1 Server-Management → 5 (✅) Feature 4 Cookbook → 6 (✅) Feature 7 Integrations-Anleitungen → 7 (✅) Feature 2 Live-Terminal`.
**Arbeitsweise je Schritt:** neuer `routers/<x>.py` + `js/panels/<x>.js` + Nav-Eintrag, sauber degradierend.
Bauen + Smoke-Test auf Windows, dann push→pull→rsync→restart auf den Bosgame (CLAUDE.md „Entwickeln & Deployen").
Ein Commit je Schritt. **Ich (KI) habe key-basierten SSH-Zugang zum Bosgame und kann selbst deployen+restarten.**
**→ Letzte Errungenschaften:**
- **WebSockets:** `/api/system/stream` für super-fluide 2Hz System-Metriken (CPU/RAM/GPU) ohne HTTP-Overhead.
- **Cookbook 2.0:** Suchergebnisse laden Metriken asynchron ("Lazy Loading"), inklusive Hardware-Fit-Berechnung. UI ist auf Premium-Niveau angehoben (Hover-Effekte, Badge-Colors).
- **Log-Streaming:** `GET /running`-Polls werden herausgefiltert, Live-Konsolen sind sauber.
**→ Aktueller Modus = Wartung & Feinschliff.**
- Die v2 Roadmap ist damit zu 100% umgesetzt.
- Fokus liegt ab sofort auf Stabilität, Bug-Hunting und dem finalen "Polishing".
---
## Features
### 1. Server-Management ("Update-Panel 2.0") (✅ Erledigt)
Aktuell gibt es nur "Container aktualisieren" + "Alles aus dem Speicher". Ziel: den kompletten Server aus der UI verwalten.
- [x] OS-/Core-Updates (`apt update/upgrade`) per Knopf, mit Live-Output
- [x] Dienste steuern (`llama-swap`, `mission-control`: Status, Restart)
- [x] Reboot / Health-Übersicht
- [x] Referenz: altes `ai-control`-Skript als Funktionsvorlage
- ⚠️ **Scope-/Security-Sprung**: macht MC zum Server-Admin-Panel. Rechte minimal halten (sudoers-Whitelist für genau die erlaubten Befehle, statt Vollzugriff).
### 2. Live-Terminal / Log via SSH (✅ Erledigt)
- [x] Unter "Server" -> "Console" kann man das Journal verfolgen.
- [x] Simpler "Log" Endpunkt in `maintenance.py` (`journalctl -u llama-swap -n 100 -f` über Websockets).
- [x] UI: Schwarze Konsole, umschaltbar zwischen `llama-swap` und `mission-control`.
- ⚠️ **Security-kritisch**: Authentifizierung via Token als Query-Parameter, da Browser keine Custom Websocket-Header senden können.
### 3. Live-Auslastung im Dashboard (✅ Erledigt)
- [x] CPU / RAM / GPU-VRAM+GTT / Temperatur live anzeigen
- [x] Quellen: **sysfs + psutil**`amd-smi`/`rocm-smi` sind auf dem Bosgame NICHT installiert!
GPU-Mem: `/sys/class/drm/card1/device/mem_info_*`; Temp: hwmon-`name` `amdgpu`/`k10temp`.
### 4. Cookbook + "Modell holen" verschmelzen (✅ Erledigt)
- Bisher: Textfelder für HuggingFace-Repo + Pfad unter "Modelle". Das ist super für Custom-Zeug.
- [x] Neu: Ein Klick-Cookbook (Sidebar-Tab "Cookbook") mit kuratierter Liste (z.B. Qwen2.5-Coder 32B, Llama3 Vision, etc.) inkl. Hardware-Aware "What Fits" Logik (wie bei Odysseus).
- [x] Klick auf Modellkarte im Cookbook triggert den Download via `/api/download`.
- [x] UI-Aufräumen: "Modell holen" Panel wandert ins Cookbook, unter "Modelle" bleibt nur die Tabelle & Chat.
### 5. Design 2.0
- [x] **Grundgerüst + Design-Sprache in Schritt 1 umgesetzt** (Sidebar-Nav, Topbar, Hero, getönte
KPI-Kacheln, Health-Signale, Listen, Aktivitäts-Stream, Alert-Banner) — Tokens in `css/base.css`.
- [x] Feinschliff pro Feature durchgeführt.
### 6. Mehr LLM-Metriken (✅ Erledigt)
- [x] Fähigkeiten pro Modell anzeigen (Text / Bild / Code)
- [x] Tokens/Sek, Kontextgröße, Quant, Dateigröße auf Platte
- [x] Status pro Modell: geladen / idle / Ladezeit
### 7. Integrations-Anleitungen (Copy-Paste) (✅ Erledigt)
- [x] Unter "Guides" (neuer Tab links) gibt es Copy-Paste Templates.
- [x] Templates für die Integration von llama-swap/mission-control in:
- Cline / Cursor
- N8N / Zapier
- OpenWebUI
- LangChain / Python Code
---
## Tech-Leitplanken
- **KISS beibehalten** — kein schweres Framework, solange es ohne geht.
- **Sicherheit zuerst** bei allem mit Shell-/SSH-Zugriff: LAN-only, Auth, minimale Rechte.
- Backend-Logik in `app.py`, UI in `static/index.html` — Trennung sauber halten.
+1 -1
View File
@@ -80,7 +80,7 @@
: 'python' : 'python'
const scriptPath = tool === 'bosgame' const scriptPath = tool === 'bosgame'
? '/opt/mission-control/mcp_memory.py' ? '/opt/mission-control/mcp_memory.py'
: 'C:\\\\Users\\\\TobisPC\\\\mission-control\\\\mcp_memory.py' : 'C:\\\\Users\\\\<DeinUsername>\\\\mission-control\\\\mcp_memory.py'
return `{\n "mcpServers": {\n "mission-control-memory": {\n "command": "${pyPath}",\n "args": ["${scriptPath}"],\n "env": {\n "MC_URL": "http://192.168.178.151:9000"\n }\n }\n }\n}` return `{\n "mcpServers": {\n "mission-control-memory": {\n "command": "${pyPath}",\n "args": ["${scriptPath}"],\n "env": {\n "MC_URL": "http://192.168.178.151:9000"\n }\n }\n }\n}`
} }
</script> </script>
+1 -1
View File
@@ -48,7 +48,7 @@
messages = [...messages, { role: 'tool', content: preview, tool: m.name }] messages = [...messages, { role: 'tool', content: preview, tool: m.name }]
} else if (m.type === 'tool_result') { } else if (m.type === 'tool_result') {
// Tool-Result in letzten Tool-Eintrag schreiben // Tool-Result in letzten Tool-Eintrag schreiben
const last = messages.findLast(x => x.role === 'tool' && x.tool === m.name) const last = [...messages].reverse().find(x => x.role === 'tool' && x.tool === m.name)
if (last) last.content = m.content if (last) last.content = m.content
messages = [...messages] messages = [...messages]
} else if (m.type === 'error') { } else if (m.type === 'error') {
+2 -2
View File
@@ -375,7 +375,7 @@ async def run_agent(message: str, send: SendFn) -> None:
await send({"type": "thinking"}) await send({"type": "thinking"})
try: try:
result = await asyncio.get_event_loop().run_in_executor( result = await asyncio.get_running_loop().run_in_executor(
None, lambda: _call_llm(model, messages) None, lambda: _call_llm(model, messages)
) )
except Exception as exc: except Exception as exc:
@@ -411,7 +411,7 @@ async def run_agent(message: str, send: SendFn) -> None:
await send({"type": "tool_call", "name": name, "args": args}) await send({"type": "tool_call", "name": name, "args": args})
tool_out = await asyncio.get_event_loop().run_in_executor( tool_out = await asyncio.get_running_loop().run_in_executor(
None, lambda n=name, a=args: execute_tool(n, a) None, lambda n=name, a=args: execute_tool(n, a)
) )
+18 -11
View File
@@ -14,6 +14,7 @@ import hashlib
import json import json
import subprocess import subprocess
import tempfile import tempfile
import threading
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -23,7 +24,7 @@ from fastapi.responses import Response, JSONResponse
from auth import auth from auth import auth
from config import ( from config import (
PIPER_BIN, PIPER_VOICE, WHISPER_MODEL_SIZE, PIPER_BIN, PIPER_VOICE, WHISPER_MODEL_SIZE,
HERMES_WINDOWS_HOST, HERMES_SSH_KEY, HERMES_WINDOWS_HOST, HERMES_WINDOWS_USER, HERMES_SSH_KEY,
) )
from hermes_agent import run_agent from hermes_agent import run_agent
@@ -35,23 +36,28 @@ router = APIRouter(prefix="/api")
_whisper_model = None _whisper_model = None
_whisper_loading = False _whisper_loading = False
_whisper_lock = threading.Lock()
def _get_whisper(): def _get_whisper():
global _whisper_model, _whisper_loading global _whisper_model, _whisper_loading
if _whisper_model is not None: with _whisper_lock:
return _whisper_model if _whisper_model is not None:
if _whisper_loading: return _whisper_model
return None if _whisper_loading:
_whisper_loading = True return None
_whisper_loading = True
try: try:
from faster_whisper import WhisperModel from faster_whisper import WhisperModel
_whisper_model = WhisperModel(WHISPER_MODEL_SIZE, device="cpu", compute_type="int8") model = WhisperModel(WHISPER_MODEL_SIZE, device="cpu", compute_type="int8")
with _whisper_lock:
_whisper_model = model
return model
except Exception: except Exception:
_whisper_model = None return None
finally: finally:
_whisper_loading = False with _whisper_lock:
return _whisper_model _whisper_loading = False
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -75,6 +81,7 @@ def _tts(text: str) -> Optional[bytes]:
input=text, capture_output=True, text=True, timeout=30 input=text, capture_output=True, text=True, timeout=30
) )
if result.returncode != 0: if result.returncode != 0:
print(f"[Piper] Fehler: {result.stderr or 'exit '+str(result.returncode)}")
return None return None
audio = Path(wav_path).read_bytes() audio = Path(wav_path).read_bytes()
if len(_tts_cache) >= 64: if len(_tts_cache) >= 64:
@@ -190,7 +197,7 @@ def hermes_status():
c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
c.connect( c.connect(
HERMES_WINDOWS_HOST, HERMES_WINDOWS_HOST,
username=__import__("config").HERMES_WINDOWS_USER, username=HERMES_WINDOWS_USER,
key_filename=str(HERMES_SSH_KEY), key_filename=str(HERMES_SSH_KEY),
timeout=3, timeout=3,
) )
+12 -2
View File
@@ -9,6 +9,7 @@ Alle MCP-Tools teilen denselben Speicher via mcp_memory.py-Wrapper.
import sqlite3 import sqlite3
import uuid import uuid
from datetime import datetime, timezone from datetime import datetime, timezone
from enum import Enum
from typing import Optional from typing import Optional
from fastapi import APIRouter, Depends, HTTPException from fastapi import APIRouter, Depends, HTTPException
@@ -28,6 +29,7 @@ def _db() -> sqlite3.Connection:
MEMORY_DB.parent.mkdir(parents=True, exist_ok=True) MEMORY_DB.parent.mkdir(parents=True, exist_ok=True)
_db_conn = sqlite3.connect(str(MEMORY_DB), check_same_thread=False) _db_conn = sqlite3.connect(str(MEMORY_DB), check_same_thread=False)
_db_conn.row_factory = sqlite3.Row _db_conn.row_factory = sqlite3.Row
_db_conn.execute("PRAGMA journal_mode=WAL") # sicherer bei concurrent FastAPI-Threads
_db_conn.execute(""" _db_conn.execute("""
CREATE TABLE IF NOT EXISTS memories ( CREATE TABLE IF NOT EXISTS memories (
id TEXT PRIMARY KEY, id TEXT PRIMARY KEY,
@@ -47,15 +49,23 @@ def _db() -> sqlite3.Connection:
return _db_conn return _db_conn
class _MemCategory(str, Enum):
user = "user"
instruction = "instruction"
stable = "stable"
versioned = "versioned"
ephemeral = "ephemeral"
class _MemIn(BaseModel): class _MemIn(BaseModel):
content: str content: str
category: str = "stable" # stable | versioned | ephemeral category: _MemCategory = _MemCategory.stable
source: str = "manual" source: str = "manual"
class _MemUp(BaseModel): class _MemUp(BaseModel):
content: Optional[str] = None content: Optional[str] = None
category: Optional[str] = None category: Optional[_MemCategory] = None
def _row(r: sqlite3.Row) -> dict: def _row(r: sqlite3.Row) -> dict:
+2 -2
View File
File diff suppressed because one or more lines are too long