Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0b639870ff | ||
|
|
084dd73f71 | ||
|
|
84b4643f1a | ||
|
|
41f2566b29 | ||
|
|
30ab2c013a | ||
|
|
d508a848f5 | ||
|
|
e04ace242c | ||
|
|
d880a76b6a | ||
|
|
ff319ff291 | ||
|
|
6e3440379b | ||
|
|
5f35f0d580 | ||
|
|
43fa747bc3 | ||
|
|
83c6ecc613 | ||
|
|
b316ad40ba | ||
|
|
8c371b946b | ||
|
|
1a2051e988 | ||
|
|
5d93822a7e | ||
|
|
22868795c9 | ||
|
|
9a35be91f9 | ||
|
|
241b1d54c6 | ||
|
|
eb4bcfc4b3 | ||
|
|
d2a175b0ad | ||
|
|
bf145a3f97 | ||
|
|
5b0909089f | ||
|
|
4b6c6ebfd5 | ||
|
|
0a6b009cb6 | ||
|
|
adc73c817f | ||
|
|
ff795ba026 |
@@ -22,5 +22,3 @@ gemma_swap*
|
|||||||
|
|
||||||
# TypeScript-Inkrementalcache (reines Build-Artefakt, maschinenabhängig)
|
# TypeScript-Inkrementalcache (reines Build-Artefakt, maschinenabhängig)
|
||||||
frontend/tsconfig.tsbuildinfo
|
frontend/tsconfig.tsbuildinfo
|
||||||
|
|
||||||
frontend/dist/
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ Engine (llama-swap) · Builtin-Routing-Gateway (`model: auto`) · MC2 (FastAPI +
|
|||||||
- **System:** Bosgame M5 (AMD Strix Halo APU)
|
- **System:** Bosgame M5 (AMD Strix Halo APU)
|
||||||
- **Arbeitsspeicher (RAM/VRAM):** 128 GB Shared Memory (ca. 122.7 GB nutzbar).
|
- **Arbeitsspeicher (RAM/VRAM):** 128 GB Shared Memory (ca. 122.7 GB nutzbar).
|
||||||
- **Inference-Limit:** Modelle im GGUF-Format dürfen maximal ca. 100-110 GB groß sein (entspricht ca. 150B Parametern bei Q4_K_M). Größere Modelle (wie 200B+ oder 2T Parameter) **können lokal nicht ausgeführt werden** und sind auszuschließen, es sei denn, es handelt sich um stark quantisierte MoEs.
|
- **Inference-Limit:** Modelle im GGUF-Format dürfen maximal ca. 100-110 GB groß sein (entspricht ca. 150B Parametern bei Q4_K_M). Größere Modelle (wie 200B+ oder 2T Parameter) **können lokal nicht ausgeführt werden** und sind auszuschließen, es sei denn, es handelt sich um stark quantisierte MoEs.
|
||||||
- **Inference-Backend:** Primär Vulkan/RADV, experimentell ROCm.
|
**Gedächtnis & Dienste:** Hermes ist das autonome Agenten-Gehirn („Lucy"). Mem0 läuft als dedizierter semantischer Langzeit-Gedächtnis-Sidecar (`mem0-service` auf `:8765`) und wird von Hermes via MCP (`mcp_memory.py`) eingebunden. Governor, Zed-Workflows und alte Mittelschichten sind komplett gelöscht.
|
||||||
|
|
||||||
**WICHTIG:** Alle alten Dienste wie `mem0`, `governor` oder `eigenleben` sind komplett gelöscht. Hermes ist das EINZIGE "Gehirn" und läuft autonom. Es nutzt keine alte Mem0-DB, sondern sein eigenes System.
|
|
||||||
## Sprache (nicht verhandelbar)
|
## Sprache (nicht verhandelbar)
|
||||||
- **Alles User-Sichtbare ist Deutsch:** UI-Texte, Fehlermeldungen, Update-/Radar-Meldungen,
|
- **Alles User-Sichtbare ist Deutsch:** UI-Texte, Fehlermeldungen, Update-/Radar-Meldungen,
|
||||||
Skills, LLM-Summaries, Commit-Messages, Code-Kommentare.
|
Skills, LLM-Summaries, Commit-Messages, Code-Kommentare.
|
||||||
|
|||||||
@@ -15,15 +15,13 @@ prüft sich selbst und meldet sich, wenn etwas nicht stimmt.
|
|||||||
| **Coding** | Agentic IDE (z.B. OpenCode Desktop) auf Dev-PC via lokaler API + MCP | 100% lokal |
|
| **Coding** | Agentic IDE (z.B. OpenCode Desktop) auf Dev-PC via lokaler API + MCP | 100% lokal |
|
||||||
| **Lucy** | Sprach-Companion mit 3D-Avatar, Augen und Ohren | eigenes Repo `Hitonabi/lucy` |
|
| **Lucy** | Sprach-Companion mit 3D-Avatar, Augen und Ohren | eigenes Repo `Hitonabi/lucy` |
|
||||||
|
|
||||||
Lucy holt ihr Hirn über MC2, spricht aber nie durch den Governor — ein Gespräch ist keine
|
Lucy holt ihr Hirn direkt über das MC2-Gateway (`:9010/v1`) mit nativer Bildweiche und Voice-Streaming.
|
||||||
Bau-Sitzung und wird nie unterbrochen.
|
|
||||||
|
|
||||||
## Ports & Dienste
|
## Ports & Dienste
|
||||||
|
|
||||||
| Port | Dienst | Erreichbar |
|
| Port | Dienst | Erreichbar |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `9001` | **MC2** (FastAPI + React) — Cockpit, API, Konsole | LAN |
|
| `9001` | **MC2** (FastAPI + React) — Cockpit, API, Konsole | LAN |
|
||||||
|
|
||||||
| `8080` | **llama-swap** — Modell-Router | LAN |
|
| `8080` | **llama-swap** — Modell-Router | LAN |
|
||||||
| `9010` | `mc2-gateway` — `/v1`-Datenpfad (`model: auto`, Bild-Weiche) | loopback |
|
| `9010` | `mc2-gateway` — `/v1`-Datenpfad (`model: auto`, Bild-Weiche) | loopback |
|
||||||
| `8642` · `9119` | Hermes-Gateway · Hermes-Web-UI | loopback |
|
| `8642` · `9119` | Hermes-Gateway · Hermes-Web-UI | loopback |
|
||||||
@@ -34,7 +32,7 @@ Units in [`deploy/`](deploy/): `mission-control-2` · `mc2-gateway` · `mc2-stew
|
|||||||
`mem0-service` · `voice-service` · `box-console` · `hermes-builtin-ui` + Timer für Backup,
|
`mem0-service` · `voice-service` · `box-console` · `hermes-builtin-ui` + Timer für Backup,
|
||||||
Auto-Update, Self-Smoke und Bagatell-Annahme. `llama-swap` läuft als System-Unit.
|
Auto-Update, Self-Smoke und Bagatell-Annahme. `llama-swap` läuft als System-Unit.
|
||||||
|
|
||||||
## Der Weg einer Idee
|
## Die Bau-Pipeline
|
||||||
|
|
||||||
```
|
```
|
||||||
Idee in MC2 → Gitea-Repo (mit CI-Ampel + VERIFY) → in Agentic IDE bauen → Ampel → main
|
Idee in MC2 → Gitea-Repo (mit CI-Ampel + VERIFY) → in Agentic IDE bauen → Ampel → main
|
||||||
@@ -51,15 +49,18 @@ Wächtern geboren**:
|
|||||||
|
|
||||||
## Modelle & Rollen
|
## Modelle & Rollen
|
||||||
|
|
||||||
Gemessen auf der Box (25.07.2026, Vulkan, Q4):
|
Gemessen auf der Box (Stand: August 2026, Vulkan b10502):
|
||||||
|
|
||||||
| Rolle | Modell | Tempo | Aufgabe |
|
| Rolle | Modell | Tempo | Aufgabe |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `coder` | Qwen3-Coder-Next (80B-A3B) | **51,5 t/s** · Prefill **754 t/s** | Plant und baut — Kopf der Coding-Mannschaft |
|
| `coder` | Qwen3-Coder-Next (80B-A3B) | **51,5 t/s** · Prefill **754 t/s** | Plant und baut — Kopf der Coding-Mannschaft (131k Kontext) |
|
||||||
| `hermes` / `fast` | Qwen3.6-35B-A3B | **69,6 t/s** | Lucys Hirn **und** der Sucher-Subagent. Immer warm |
|
| `hermes` / `fast` | Qwen3.6-35B-A3B | **69,6–90 t/s** | Lucys Hirn **und** Sucher-Subagent. Immer warm |
|
||||||
| `kritiker` | Devstral-Small-2-24B | **15,0 t/s** · Prefill **265–318 t/s** | Liest gegen — bewusst **fremde Modellfamilie** (Mistral statt Qwen) |
|
| `debugger` / `doctor` | Muse-Glimmer-30B | **40–50 t/s** (DFlash) | Runtime-Debugger & Fehler-Diagnostiker (Multimodal) |
|
||||||
| `vision` | Qwen3-VL-30B-A3B | auf Abruf | Lucys Augen |
|
| `kritiker` | Devstral-Small-2-24B | **15,0 t/s** · Prefill **265–318 t/s** | Liest gegen — bewusst **fremde Modellfamilie** (Mistral statt Qwen, 16k Deckel) |
|
||||||
| `heavy` | gpt-oss-120b | nur nachts | Chef-Gutachter (4:30). **Nie tagsüber** — verdrängt das warme Set |
|
| `vision` | Qwen3-VL-30B-A3B | auf Abruf | Lucys Augen (On-Demand) |
|
||||||
|
| `scout` | GLM-4.6V-Flash | auf Abruf | Schneller Vision- und Tool-Allrounder |
|
||||||
|
| `heavy` | gpt-oss-120b | nur nachts | Chef-Gutachter (4:30 Uhr). **Nie tagsüber** — verdrängt das warme Set |
|
||||||
|
| `dense-planer` | Qwen3.8-27B | **12,7 t/s** (On-Demand) | Dichtes 27B-Modell für tiefes Reasoning & 262k Kontext |
|
||||||
| `embed` · `reranker` | Qwen3 0.6B | immer warm | Gedächtnis + Feinsortierung |
|
| `embed` · `reranker` | Qwen3 0.6B | immer warm | Gedächtnis + Feinsortierung |
|
||||||
|
|
||||||
‼️ **Der Kritiker ist bewusst auf 16k Kontext gedeckelt.** Devstral ist ein *dichtes* Modell —
|
‼️ **Der Kritiker ist bewusst auf 16k Kontext gedeckelt.** Devstral ist ein *dichtes* Modell —
|
||||||
|
|||||||
+168
-42
@@ -1,50 +1,176 @@
|
|||||||
# Savepoint — Mission Control 2.0 (MC2)
|
# Savepoint — Mem0-Ausbau (Referenzaufgabe)
|
||||||
|
|
||||||
_Stand: 2026-07-05. Zustands-Snapshot für einen externen Code-Review (Antigravity). Zuerst
|
**Stand:** 2026-08-22, Worktree `referenz/mem0-ausbau` in `F:\Coding Stuff\mc2-referenz`.
|
||||||
`AGENTS.md` lesen (Projekt-Regeln), dann diese Datei (wo wir stehen), dann `README.md`/`docs/`._
|
Branch wurde aus `d508a84` (A2's gesicherter Stand) erstellt und enthält dessen 5 bearbeitete Dateien.
|
||||||
|
|
||||||
## Was das ist (in einem Satz)
|
## Auftrag
|
||||||
MC2 ist die **Web-Admin-Konsole** einer 100 % lokalen KI-Appliance („die Box", Bosgame M5 / AMD
|
`docs/aufgaben/referenzaufgabe-mem0-ausbau.md`: Mem0-Reste vollständig entfernen, ohne MC2/Frontend/Deployment zu brechen.
|
||||||
Strix Halo, 128 GB Unified RAM, llama.cpp-Vulkan). Sie verwaltet Engine, Modelle, Gedächtnis,
|
- Messlatte: `ruff` clean, Backend `import app` sauber, `grep -ril mem0 backend/ mcp/ deploy/ --include='*.py' --include='*.sh'` = 0 Dateien, `mem0_service/` entfernt, Box-Smoke/Health so weit wie möglich grün.
|
||||||
Agenten-Status, Updates & Wartung — und dient teils als Gedächtnis-Oberfläche für die Sprach-
|
- **Prüfstein:** API-Felder wie `mem0_ms` nicht einfach löschen — Frontend mit anpassen oder Feld sauber aus beiden Seiten entfernen.
|
||||||
Begleiterin „Lucy".
|
- Arbeitsweise: Datei für Datei, höchstens zwei vor der ersten Änderung. Nach jeder Änderung ein Satz.
|
||||||
|
- Live-Deployment `~/mission-control-v2` ist TABU.
|
||||||
|
|
||||||
## Architektur (Kurzform — Details in AGENTS.md/docs/)
|
## Wichtig: A2-Vorgang (Commit d508a84 — bereits auf diesem Branch)
|
||||||
- **Backend** `backend/` — FastAPI, `:9001` als systemd-**User-Dienst** (reboot-fest, sudo-frei).
|
Diese 5 Dateien wurden vom abgebrochenen Lauf A2 bereits bearbeitet und im Commit `d508a84` gesichert:
|
||||||
Router (`routers/`, dünn) → Logik (`services/`, Single Source of Truth).
|
1. `backend/config.py` — `MEMORY_DB` und `MC_MEM_DEDUPE_ENABLED` entfernt, `MEM0_SERVICE_URL`-Kommentar bereinigt
|
||||||
- **Frontend** `frontend/` — React/Vite/Tailwind/shadcn. **`frontend/dist` ist ABSICHT im git**
|
2. `backend/routers/system.py` — `_mem0_reachable()` entfernt, `mem0_reachable` aus Systeminfo
|
||||||
(Box hat kein Node; Backend liefert die gebauten Assets aus).
|
3. `backend/routers/voice.py` — `Mem0-Retrieve` → `Gedächtnis-Retrieve`
|
||||||
- **Eingebautes Gateway** `:9001/v1` (OpenAI-kompatibel) → llama-swap `:8080` (Engine, Vulkan/RADV).
|
4. `backend/routers/zeitmaschine.py` — `Mem0` in Docstrings/Kommentaren → `Gedächtnis`
|
||||||
- **Sidecars:** `mem0_service/` (Mem0-Gedächtnis), `voice_service/` (STT/TTS für den „Sprechen"-Tab),
|
5. `backend/services/sentry.py` — `MC_MEM0_DASHBOARD_URL` in Docstring entfernt
|
||||||
`mcp/` (MCP-Server), `hermes/` (Hermes-Agent-Plugin), `client/hermes-pc` (PC-Executor).
|
|
||||||
|
|
||||||
## Aktueller Zustand
|
`git status` war nach d508a84 clean.
|
||||||
- **Stabil, in Produktion, „MC2 Final".** Ein Neubau („MC3") wurde bewusst VERWORFEN — MC2 wird
|
|
||||||
sauber gehalten und eingefroren, nicht neu erfunden.
|
|
||||||
- **Autonomie-Ausbau abgeschlossen** (Observability-Latenztrace, Fremd-Modell-Kritiker/Härtung,
|
|
||||||
nächtliches „Dreaming" mit Wissens-Vault) — jeweils propose-only, Mensch = Gate.
|
|
||||||
- **Zuletzt (Commit `ff1b9a0`, 05.07.):** `coder-lite` (Qwen3-Coder-30B) entfernt; der Verbinden-Tab
|
|
||||||
(`services/connect.py`) empfiehlt IDEs jetzt die realen Direkt-Modelle `coder`/`heavy`/`vision`
|
|
||||||
statt der virtuellen „coding"-Lane. Davor u. a. Engine-Update-Fix, Hermes-Terminal same-origin.
|
|
||||||
- **Modelle live:** hermes=Qwen3.6-35B-A3B (Agent-Hirn, immer warm) · coder=Qwen3-Coder-Next ·
|
|
||||||
heavy=gpt-oss-120b · vision=Qwen3-VL-30B · scout=GLM-4.6V · embed. Warm-Set = hermes+vision+embed.
|
|
||||||
|
|
||||||
## Nordstern & Randbedingungen (WICHTIG für den Review)
|
## Wichtig: Kahlschlag-Commit 1e68f62 (bereits auf main)
|
||||||
- **Zieht ohne Wartung über JAHRE durch.** Robustheit + Einfachheit schlagen Features. Der Betreiber
|
- `backend/services/memory.py` — gelöscht
|
||||||
ist **kein Entwickler** — alles muss reboot-/update-fest und selbsterklärend sein.
|
- `backend/routers/memory.py` — gelöscht (`/api/memory` existiert nicht mehr)
|
||||||
- **100 % lokal, kein Cloud-Zwang** (Privatsphäre ist der Sinn). Hardware-Decke: ~124 GB, ein Topf.
|
- `deploy/mc2-steward.service` — gelöscht
|
||||||
- **Lucy ist ein SEPARATES Repo** (`F:\Coding Stuff\lucy`) — **nicht Teil dieses Reviews.**
|
- `deploy/mem0-service.service` — gelöscht
|
||||||
|
- `deploy/voice-service.service` — gelöscht
|
||||||
|
- `deploy/mem0-konsolidierung.*` — gelöscht
|
||||||
|
|
||||||
## Bekannte raue Kanten (Kandidaten — gern bestätigen/erweitern)
|
## Erledigt in dieser Sitzung
|
||||||
- Doku-Drift möglich: `AGENTS.md` nennt „Box läuft in UTC" — die Box wurde inzwischen auf
|
|
||||||
`Europe/Berlin` umgestellt.
|
|
||||||
- Alte, disabled v1-System-Unit `mission-control.service` liegt kosmetisch herum (sudo zum Entfernen).
|
|
||||||
- Ein paar frühere Frontend-Monolithen wurden entflochten; Rest-Altlasten möglich.
|
|
||||||
|
|
||||||
## Was NICHT empfohlen werden soll (bereits entschieden)
|
### 1. `backend/steward.py`
|
||||||
Cloud-Migration · Voll-Rewrite · schwere neue Frameworks · Engine-/Modell-Wechsel, die nicht in
|
- Toter Import `from services import memory as memory_svc` entfernt
|
||||||
124 GB passen · `frontend/dist` aus git nehmen (Absicht) · Security-Config anfassen. Erwünscht sind
|
- Toter `memory_svc.AUTO_DEDUPE_ENABLED`-Block entfernt
|
||||||
**Bugs, Fragilität, tote Pfade, Sicherheitslücken, Vereinfachung (KISS/DRY), Wartbarkeit.**
|
- Docstring: `Mem0-Auto-Dedupe` → `Re-Warm-Wächter und Health-Wächter (sentry)`
|
||||||
|
- Docstring: `memory.auto_dedupe_loop — Gedächtnis-Dubletten` entfernt
|
||||||
|
- Docstring: `MC_MEM_DEDUPE_ENABLED` entfernt
|
||||||
|
- Verifiziert: `memory.py` wurde in Kahlschlag gelöscht, `steward.py` hatte toten Import
|
||||||
|
|
||||||
## Letzter Sicherungspunkt
|
### 2. `backend/services/voice_metrics.py` (alle 9 Treffer bereinigt)
|
||||||
- git `ff1b9a0` „coder-lite raus + Verbinden-Tab auf echte Direkt-Modelle" (auf `main`, deployt, live).
|
- **Docstring (3x):** `Mem0-Retrieve` → `Gedächtnis-Retrieve`
|
||||||
|
- **Line 10:** `Mem0-Retrieve` im Best-effort-Kommentar → `Gedächtnis-Retrieve`
|
||||||
|
- **Line 39:** `Mem0-Retrieve` im docstring → `Gedächtnis-Retrieve`
|
||||||
|
- **Line 56:** `Geparkten Mem0-Retrieve` → `Geparkten Gedächtnis-Retrieve`
|
||||||
|
- **Line 106:** `Unter-Detail: mem0 (Teil VON hirn...` → `Unter-Detail: memory (Teil VON hirn...`
|
||||||
|
- **Line 116:** `Agent + Mem0 + TTFT` → `Agent + Gedächtnis + TTFT`
|
||||||
|
- **Line 133:** `Agent-Overhead + Mem0 + LLM-TTFT` → `Agent-Overhead + Gedächtnis + LLM-TTFT`
|
||||||
|
- **Line 141:** `mem0 = _take_retrieve(...)` → `memory = _take_retrieve(...)` (starker Kommentar)
|
||||||
|
- **Line 154:** `"mem0_ms"` → `"memory_ms"` (API-Feld im JSON-Rückgabewort)
|
||||||
|
|
||||||
|
### 3. `frontend/src/lib/api.ts` (3 Treffer)
|
||||||
|
- **Line 446:** `mem0 = Unter-Detail INNERHALB von hirn` → `memory = Unter-Detail INNERHALB von hirn`
|
||||||
|
- **Line 455:** `Agent + Mem0 + LLM-TTFT` → `Agent + Gedächtnis + LLM-TTFT`
|
||||||
|
- **Line 457:** `mem0_ms: number | null // ... (Mem0-Retrieve)` → `memory_ms: number | null // ... (Gedächtnis-Retrieve)`
|
||||||
|
|
||||||
|
### 4. `frontend/src/components/dashboard/LatencyCard.tsx` (4 Treffer)
|
||||||
|
- **Line 5:** `Mem0 ist ein Unter-Detail` → `Gedächtnis ist ein Unter-Detail`
|
||||||
|
- **Line 42:** `t.mem0_ms` (2x) → `t.memory_ms` (2x)
|
||||||
|
- **Line 106:** `latest.mem0_ms` (2x) → `latest.memory_ms` (2x)
|
||||||
|
- **Line 131:** `Agent + Mem0-Suche` → `Agent + Gedächtnis-Suche`
|
||||||
|
|
||||||
|
### 5. `frontend/src/views/GuideView.tsx` (1 Treffer, kompletter Paragraph)
|
||||||
|
- Stack-Paragraph (Card 8 "Gedächtnis & RAG"): "Dein Gedächtnis ist Mem0-basiert: auto-lernend..." umgeschrieben zu "Dein Gedächtnis ist auto-lernend & semantisch: Der Agent lernt im Hintergrund nach jedem Turn..."
|
||||||
|
- Gotcha-Zeile (Mem0-extraktion) entfernt
|
||||||
|
- Begründung: Gedächtnis-Tab existiert nicht mehr (Kahlschlag), Mem0 wurde durch Hermes-Nativ abgelöst
|
||||||
|
|
||||||
|
### 6. `backend/services/backup.py` (1 Treffer)
|
||||||
|
- Docstring: `Full state backup (mem0 + ...)` → `Full state backup (...)`
|
||||||
|
|
||||||
|
### 7. `deploy/backup.sh` (8 Treffer)
|
||||||
|
- **Line 6-7:** Kommentar `mem0 (Chroma + history.db)` aus Inhalt entfernt
|
||||||
|
- **Line 23:** `MEM0_DIR="${MC_MEM0_DIR:-/srv/models/mem0}"` — ganze Zeile gelöscht
|
||||||
|
- **Line 30:** `mkdir -p "$STAGE/mem0"` — aus mkdir entfernt
|
||||||
|
- **Line 32:** `[ -d "$MEM0_DIR" ] && cp -a "$MEM0_DIR/." "$STAGE/mem0/" || true` — ganze Zeile gelöscht
|
||||||
|
- **Line 63:** Kommentar `mem0-venv ist uv-managed` → `voice-venv ist uv-managed`
|
||||||
|
- **Line 71:** `freeze_venv "$HOME/.mem0/venv/bin/python" "$KG/pip-mem0.txt"` — ganze Zeile gelöscht
|
||||||
|
- **Line 82:** `$HOME/.mem0/venv/bin/python` aus for-Schleife entfernt
|
||||||
|
- **Line 95:** MANIFEST: `mem0 (chroma + history.db)` aus Inhalt entfernt
|
||||||
|
|
||||||
|
### 8. `mcp/mcp_mc.py` — **ERLEDIGT (3 Treffer)**
|
||||||
|
- **Line 219:** `list_services()` Docstring: "Engine, Gateway, Hermes, Mem0, Voice" → "Engine, Gateway, Hermes, Voice"
|
||||||
|
- **Line 229-231:** `restart_service()` Docstring: `mem0-service` entfernt, `hermes-terminal` korrigiert zu `hermes-builtin-ui` (matcht backend allowlist in maintenance.py)
|
||||||
|
- **Line 238:** `backup_now()` Docstring: "mem0 + Configs + Secrets" → "Configs + Secrets"
|
||||||
|
- Verifiziert: `rg -i mem0 mcp/mcp_mc.py` = 0 Treffer, `py_compile mcp/mcp_mc.py` OK
|
||||||
|
- **Achtung:** Der mc2-memory-Plugin (`hermes/plugins/mc2-memory/__init__.py`) ruft noch `/api/memory` und `/api/memory/learn` auf. Der Kahlschlag-Commit hat `backend/routers/memory.py` gelöscht → dieser API-Pfad existiert nicht mehr. Der Plugin degraded silently (prefetch="", learn fail). **Entscheidung:** Nicht bearbeiten, Plugin ist im `hermes/plugins/`-Ordner, nicht in der Messlatte.
|
||||||
|
|
||||||
|
### 9. `deploy/agent-hooks/box-steckbrief-inject.sh`
|
||||||
|
- Treffer: "mem0-Gedaechtnis :8765" in einer langen Agent-Injektionszeile (Zeile 31)
|
||||||
|
- Nur Text → mem0-Gedaechtnis :8765 entfernen
|
||||||
|
|
||||||
|
### 10. `deploy/autoupdate.sh`
|
||||||
|
- Treffer: "Gehirn-Check (Mem0, Tools, Voice)" → "Gehirn-Check (Tools, Voice)"
|
||||||
|
|
||||||
|
### 11. `deploy/hermes-postcheck.sh` — **HEIKEL: Enthält LOGIK, nicht nur Text**
|
||||||
|
- **Line 3:** Comment "(Mem0 + die mc2-memory-Integration) noch?" → muss aktualisiert werden
|
||||||
|
- **Line 8:** `MEM0_URL="${MEM0_SERVICE_URL:-http://127.0.0.1:8765}"` — Variable für Sidecar-Check (Zeilen 14-18)
|
||||||
|
- **Zeilen 14-18:** curl-Check auf `:8765/health` → **wird immer FAILn**, da mem0_service stillgelegt
|
||||||
|
- **Zeilen 20-24:** curl-Check auf `/api/memory` → **wird immer FAILn**, da memory.py in Kahlschlag gelöscht
|
||||||
|
- **Zeilen 26-31:** grep-Check für `memory.provider=mc2-memory` in config.yaml → das ist **neues** Hermes-nativ-Memory, kein Mem0-Rest. **NICHT entfernen** (gehört zur aktuellen Architektur).
|
||||||
|
- **Zeilen 52-58:** Plugin-Check für `mc2-memory` → ebenfalls **neue** Architektur, kein Mem0-Rest. **NICHT entfernen**.
|
||||||
|
- **Entscheidung:** Zeilen 8, 14-18, 20-24 entfernen (toter Sidecar + toter /api/memory-Endpunkt). Zeilen 26-31 und 52-58 behalten (gehören zur neuen Hermes-native Memory).
|
||||||
|
|
||||||
|
### 12. `deploy/restore.sh` — **LOGIK: Gegenstück zu backup.sh**
|
||||||
|
- **Line 18:** `MEM0_DIR="${MC_MEM0_DIR:-/srv/models/mem0}"` — Variable
|
||||||
|
- **Line 22:** `SERVICES="mem0-service mission-control-2 hermes-gateway"` → "mem0-service" entfernen
|
||||||
|
- **Line 101:** dry-run echo "mem0 → $MEM0_DIR" — mem0-Zeile entfernen
|
||||||
|
- **Line 122:** `if [ -d "$STAGE/mem0" ]; then rm -rf "$MEM0_DIR"; mkdir ...; cp ...; fi` — ganze Zeile entfernen
|
||||||
|
- Da backup.sh kein `$STAGE/mem0` mehr erstellt, ist diese Logik tot (würde nie feuern). Entfernen.
|
||||||
|
|
||||||
|
### 13. `deploy/stack-postcheck.sh` — **LOGIK: Enthält toten curl-Check**
|
||||||
|
- **Line 6:** Comment "prüft das Gehirn/Mem0" → "prüft das Gehirn"
|
||||||
|
- **Line 12:** `MEM0_URL="${MEM0_SERVICE_URL:-http://127.0.0.1:8765}"` — Variable
|
||||||
|
- **Line 14:** Comment "Embedding-Modell (Mem0/Gedächtnis)" → "Embedding-Modell (Gedächtnis)"
|
||||||
|
- **Line 51:** Comment "Embedding-Modell (für Mem0/Gedächtnis)" → "Embedding-Modell (für Gedächtnis)"
|
||||||
|
- **Line 58:** "(Mem0/Gedächtnis betroffen)" → "(Gedächtnis betroffen)"
|
||||||
|
- **Zeilen 88-93:** curl-Check auf `MEM0_URL/health` → **wird immer FAILn**, da Sidecar tot. **Ganze Block entfernen.**
|
||||||
|
- Die Embedding-Checks (Zeilen 51-59) bleiben — sie prüfen das Embedding-Modell, das auch für die neue Memory-Architektur relevant ist.
|
||||||
|
|
||||||
|
### 14. `deploy/warmup.sh`
|
||||||
|
- Treffer: "für Mem0/Gedächtnis" → "für Gedächtnis" (nur Text)
|
||||||
|
|
||||||
|
### 15. `deploy/venv-audit.sh`
|
||||||
|
- **Line 2:** Comment "(Mem0, Voice) altern" → "(Voice) altern"
|
||||||
|
- **Line 12:** `VENVS=("$HOME/.mem0/venv:mem0" "$HOME/.voice/venv:voice")` → mem0-Entry entfernen
|
||||||
|
|
||||||
|
### 16. Ordner `mem0_service/` löschen
|
||||||
|
- Enthält: `app.py`, `migrate.py`, `smoke_test.py`, `requirements.txt`
|
||||||
|
- Toter Sidecar: Port `:8765` antwortet nicht, nicht systemd-registered
|
||||||
|
|
||||||
|
## Noch nicht touchen (out-of-scope / unklar)
|
||||||
|
|
||||||
|
### `voice_service/app.py` + `voice_service/install.sh`
|
||||||
|
- Enthalten "mem0" — gehören zur voice_service, nicht zur Messlatte
|
||||||
|
- voice_service.service wurde in Kahlschlag gelöscht, aber voice_service/ Ordner ist noch vorhanden
|
||||||
|
- **Entscheidung:** Nicht bearbeiten, da nicht in `backend/`, `mcp/`, `deploy/` Messlatte
|
||||||
|
|
||||||
|
### `hermes/plugins/mc2-memory/` (2 Dateien)
|
||||||
|
- `__init__.py`, `plugin.yaml`
|
||||||
|
- Rufen `/api/memory` auf → existiert nicht mehr, degradieren gracefully
|
||||||
|
- Nicht in Messlatte (`deploy/` mit `.py/.sh` — YAML zählt nicht, Python-Plugin ist extern)
|
||||||
|
- **Entscheidung:** Nicht bearbeiten (Hermes-Plugin, nicht Teil von MC2)
|
||||||
|
|
||||||
|
### `deploy/mission-control-2.service` + `deploy/mc2-backup.service`
|
||||||
|
- systemd Service-Dateien (keine `.py` oder `.sh`)
|
||||||
|
- Nicht in Messlatte, aber könnten Mem0-Referenzen haben
|
||||||
|
- **Entscheidung:** Prüfen, ob sie Mem0 nennen; wenn ja, Text anpassen
|
||||||
|
|
||||||
|
### `deploy/llama-swap.config.yaml`
|
||||||
|
- YAML, nicht in Messlatte
|
||||||
|
|
||||||
|
### `frontend/dist/`
|
||||||
|
- Muss nach Änderungen neu gebaut werden (`npm run build`)
|
||||||
|
- **NACHDEM alle .ts/.tsx Dateien bearbeitet sind**
|
||||||
|
|
||||||
|
## Bekannte Fallstricke
|
||||||
|
|
||||||
|
1. **Prüfstein (Frontend-Konsistenz):** `mem0_ms` war ein API-Feld, das das Frontend las. Wurde zu `memory_ms` umbenannt + Frontend angepasst. **Nicht einfach löschen.**
|
||||||
|
2. **Restore vs. Backup:** `restore.sh` ist das Gegenstück zu `backup.sh`. Wenn backup.sh mem0 weglässt, darf restore.sh nicht versuchen, mem0 zu restaurieren. **Sorgfältig prüfen!**
|
||||||
|
3. **`head` funktioniert in PowerShell nicht:** Muss `Select-Object -First N` verwenden.
|
||||||
|
4. **Globs für native Befehle:** `rg` mit Globs muss gequoted werden, sonst spaltet PowerShell falsch.
|
||||||
|
5. **`frontend/dist` ist im git:** Nach Frontend-Änderungen muss `npm run build` laufen und das neue `dist` committet werden.
|
||||||
|
6. **`/api/memory` existiert nicht mehr** (Kahlschlag), aber `frontend/src/lib/queries.ts` ruft es noch via `useMemory()`: `api<Memory[]>(`/api/memory`...`. Das ist ein **vorher bestehendes Problem** (nicht eingeführt in dieser Runde).
|
||||||
|
|
||||||
|
## Nächster Schritt
|
||||||
|
1. **mcp/mcp_mc.py committen** (bereits bearbeitet)
|
||||||
|
2. `deploy/agent-hooks/box-steckbrief-inject.sh` bearbeiten (1 Treffer, Text)
|
||||||
|
3. `deploy/autoupdate.sh`, `deploy/warmup.sh` bearbeiten (je 1 Treffer, Text)
|
||||||
|
4. `deploy/venv-audit.sh` bearbeiten (2 Treffer, Text + Venv-Liste)
|
||||||
|
5. `deploy/stack-postcheck.sh` bearbeiten (6 Treffer, 1 Block-Entfernung, 4 Kommentare)
|
||||||
|
6. `deploy/restore.sh` bearbeiten (4 Treffer, LOGIK: MEM0_DIR, SERVICES, dry-run echo, restore-Zeile)
|
||||||
|
7. `deploy/hermes-postcheck.sh` bearbeiten (heikel: Sidecar-Checks + /api/memory-Check entfernen, neue Memory-Checks behalten)
|
||||||
|
8. `mem0_service/` Ordner löschen
|
||||||
|
9. `ruff` + `import app` prüfen
|
||||||
|
10. `grep -ril mem0 backend/ mcp/ deploy/ --include='*.py' --include='*.sh'` → muss 0 ergeben
|
||||||
|
11. `frontend/dist` neu bauen (Box: `cd frontend && npm run build`)
|
||||||
|
|||||||
+2
-9
@@ -19,13 +19,6 @@ MODELS_DIR = Path(os.environ.get("MC_MODELS_DIR", "/srv/models"))
|
|||||||
# Persistent neben den Modellen (übersteht Deploys). TTL = Frische-Fenster.
|
# Persistent neben den Modellen (übersteht Deploys). TTL = Frische-Fenster.
|
||||||
DISCOVER_CACHE_PATH = Path(os.environ.get("MC_DISCOVER_CACHE", str(MODELS_DIR / "mc2-discover.json")))
|
DISCOVER_CACHE_PATH = Path(os.environ.get("MC_DISCOVER_CACHE", str(MODELS_DIR / "mc2-discover.json")))
|
||||||
DISCOVER_TTL = int(os.environ.get("MC_DISCOVER_TTL", "43200")) # 12 h
|
DISCOVER_TTL = int(os.environ.get("MC_DISCOVER_TTL", "43200")) # 12 h
|
||||||
# Geteiltes Gedächtnis (SQLite, WAL). Persistent neben den Modellen.
|
|
||||||
# Hinweis: nur noch für die einmalige Mem0-Migration relevant — das aktive Gedächtnis
|
|
||||||
# liegt jetzt in Mem0/Chroma hinter dem Sidecar (siehe MEM0_SERVICE_URL).
|
|
||||||
MEMORY_DB = Path(os.environ.get("MC_MEMORY_DB", str(MODELS_DIR / "mc2-memory.db")))
|
|
||||||
# Mem0-Sidecar (auto-lernendes, semantisches Gedächtnis). Läuft im ~/.mem0/venv (Python 3.12),
|
|
||||||
# weil mem0+chromadb unter dem 3.14-Backend nicht laufen. MC2 spricht ihn lokal per HTTP an.
|
|
||||||
MEM0_SERVICE_URL = os.environ.get("MC_MEM0_SERVICE_URL", "http://127.0.0.1:8765").rstrip("/")
|
|
||||||
# Befehl-Vorlage für llama-swap: {model}=GGUF-Pfad, {ctx}=Kontext, ${PORT} bleibt stehen.
|
# Befehl-Vorlage für llama-swap: {model}=GGUF-Pfad, {ctx}=Kontext, ${PORT} bleibt stehen.
|
||||||
# Hinweis: --prompt-cache/--prompt-cache-all sind llama-CLI-Flags, NICHT llama-server —
|
# Hinweis: --prompt-cache/--prompt-cache-all sind llama-CLI-Flags, NICHT llama-server —
|
||||||
# llama-server lehnt sie ab ("invalid argument") und startet dann nicht. Prompt-Caching
|
# llama-server lehnt sie ab ("invalid argument") und startet dann nicht. Prompt-Caching
|
||||||
@@ -72,7 +65,7 @@ V1_UPSTREAM = os.environ.get("MC_V1_UPSTREAM", "").rstrip("/")
|
|||||||
HERMES_API_URL = os.environ.get("HERMES_API_URL", "http://127.0.0.1:8642").rstrip("/")
|
HERMES_API_URL = os.environ.get("HERMES_API_URL", "http://127.0.0.1:8642").rstrip("/")
|
||||||
# API-Key der Hermes-`api_server`-Plattform (~/.hermes/.env: API_SERVER_KEY). Nötig für
|
# API-Key der Hermes-`api_server`-Plattform (~/.hermes/.env: API_SERVER_KEY). Nötig für
|
||||||
# /v1/chat/completions (Voice-Pipeline) — Bearer-Auth, sonst 401. Derselbe volle Agent
|
# /v1/chat/completions (Voice-Pipeline) — Bearer-Auth, sonst 401. Derselbe volle Agent
|
||||||
# (Tools + geteiltes Mem0) wie CLI/Telegram, nur über HTTP.
|
# (Tools + geteiltes Gedächtnis) wie CLI/Telegram, nur über HTTP.
|
||||||
def _read_hermes_env(key: str) -> str:
|
def _read_hermes_env(key: str) -> str:
|
||||||
"""Liest einen Schlüssel aus ~/.hermes/.env (Fallback, falls nicht in der Prozess-Env).
|
"""Liest einen Schlüssel aus ~/.hermes/.env (Fallback, falls nicht in der Prozess-Env).
|
||||||
Der MC2-Dienst erbt die Hermes-Secrets sonst nicht."""
|
Der MC2-Dienst erbt die Hermes-Secrets sonst nicht."""
|
||||||
@@ -97,7 +90,7 @@ HERMES_API_KEY = (
|
|||||||
HERMES_API_MODEL = os.environ.get("HERMES_API_MODEL", "hermes")
|
HERMES_API_MODEL = os.environ.get("HERMES_API_MODEL", "hermes")
|
||||||
|
|
||||||
# --- Voice-Sidecar (STT faster-whisper + TTS Piper/Chatterbox) ---------------
|
# --- Voice-Sidecar (STT faster-whisper + TTS Piper/Chatterbox) ---------------
|
||||||
# Eigenes Python-3.12-venv (~/.voice/venv), analog Mem0-Sidecar. MC2 proxyt nach außen.
|
# Eigenes Python-3.12-venv (~/.voice/venv). MC2 proxyt nach außen.
|
||||||
VOICE_SERVICE_URL = os.environ.get("MC_VOICE_SERVICE_URL", "http://127.0.0.1:8650").rstrip("/")
|
VOICE_SERVICE_URL = os.environ.get("MC_VOICE_SERVICE_URL", "http://127.0.0.1:8650").rstrip("/")
|
||||||
# Box-Konsole: ttyd-Web-Terminal (echte Login-Shell). Bindet NUR an Loopback
|
# Box-Konsole: ttyd-Web-Terminal (echte Login-Shell). Bindet NUR an Loopback
|
||||||
# (127.0.0.1:7682, base-path /console) und wird von MC2 über den ohnehin offenen Port 9001
|
# (127.0.0.1:7682, base-path /console) und wird von MC2 über den ohnehin offenen Port 9001
|
||||||
|
|||||||
@@ -2,6 +2,16 @@
|
|||||||
"_comment": "Kuratierter Modell-Katalog (Cookbook) für Strix Halo / Ryzen AI MAX+ 395 — 128GB unified, bandbreiten-limitiert (256 GB/s). MoE-first. EINE Quelle der Wahrheit für KORREKTE Metadaten (total/active params, moe, generation) → präzise Empfehlungen ohne Namens-Raterei. Inspiriert vom Odysseus-Cookbook (statischer, validierter Katalog statt Live-Scraping). Erweiterbar: neue Modelle hier eintragen. Felder: name (Match-Identifier), repo (HF org/name für Install), family (+Subtyp), generation (numerisch, für Upgrade-Vergleich), total_params_b, active_params_b (=total bei dense), moe, quant, ctx (empfohlen), tools, vision.",
|
"_comment": "Kuratierter Modell-Katalog (Cookbook) für Strix Halo / Ryzen AI MAX+ 395 — 128GB unified, bandbreiten-limitiert (256 GB/s). MoE-first. EINE Quelle der Wahrheit für KORREKTE Metadaten (total/active params, moe, generation) → präzise Empfehlungen ohne Namens-Raterei. Inspiriert vom Odysseus-Cookbook (statischer, validierter Katalog statt Live-Scraping). Erweiterbar: neue Modelle hier eintragen. Felder: name (Match-Identifier), repo (HF org/name für Install), family (+Subtyp), generation (numerisch, für Upgrade-Vergleich), total_params_b, active_params_b (=total bei dense), moe, quant, ctx (empfohlen), tools, vision.",
|
||||||
"version": "2026-07-03",
|
"version": "2026-07-03",
|
||||||
"models": [
|
"models": [
|
||||||
|
{
|
||||||
|
"role": "debugger", "name": "Muse-Glimmer-30B", "repo": "unsloth/Muse-Glimmer-30B-GGUF",
|
||||||
|
"family": "muse", "generation": 1.0, "total_params_b": 30, "active_params_b": 30,
|
||||||
|
"moe": false, "quant": "Q4_K_XL", "ctx": 65536, "tools": true, "vision": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "coder", "name": "Qwen3.8-27B", "repo": "unsloth/Qwen3.8-27B-GGUF",
|
||||||
|
"family": "qwen", "generation": 3.8, "total_params_b": 27, "active_params_b": 27,
|
||||||
|
"moe": false, "quant": "Q4_K_M", "ctx": 65536, "tools": true, "vision": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"role": "fast", "name": "Qwen3.6-35B-A3B", "repo": "Qwen/Qwen3.6-35B-A3B-GGUF",
|
"role": "fast", "name": "Qwen3.6-35B-A3B", "repo": "Qwen/Qwen3.6-35B-A3B-GGUF",
|
||||||
"family": "qwen", "generation": 3.6, "total_params_b": 35, "active_params_b": 3,
|
"family": "qwen", "generation": 3.6, "total_params_b": 35, "active_params_b": 3,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import os
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from config import GATEWAY_URL, HERMES_API_URL, LLAMA_SWAP_URL, MEM0_SERVICE_URL, V1_UPSTREAM, VOICE_SERVICE_URL
|
from config import GATEWAY_URL, HERMES_API_URL, LLAMA_SWAP_URL, V1_UPSTREAM, VOICE_SERVICE_URL
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from services import backup as backup_svc
|
from services import backup as backup_svc
|
||||||
@@ -42,13 +42,6 @@ def history(minutes: int = 60) -> dict:
|
|||||||
return metrics_history.history(minutes)
|
return metrics_history.history(minutes)
|
||||||
|
|
||||||
|
|
||||||
def _mem0_reachable() -> bool:
|
|
||||||
try:
|
|
||||||
return httpx.get(f"{MEM0_SERVICE_URL}/health", timeout=2).status_code == 200
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _voice_reachable() -> bool:
|
def _voice_reachable() -> bool:
|
||||||
try:
|
try:
|
||||||
return httpx.get(f"{VOICE_SERVICE_URL}/health", timeout=2).status_code == 200
|
return httpx.get(f"{VOICE_SERVICE_URL}/health", timeout=2).status_code == 200
|
||||||
@@ -95,12 +88,10 @@ def services() -> dict:
|
|||||||
rows += [
|
rows += [
|
||||||
{"name": "Wächter (Steward)", "unit": "mc2-steward", "url": "",
|
{"name": "Wächter (Steward)", "unit": "mc2-steward", "url": "",
|
||||||
"ok": _user_unit_active("mc2-steward"), "scope": "user"},
|
"ok": _user_unit_active("mc2-steward"), "scope": "user"},
|
||||||
{"name": "Hermes-Gateway", "unit": "hermes-gateway", "url": HERMES_API_URL,
|
{"name": "Hermes-Gateway (Agent & Gedächtnis)", "unit": "hermes-gateway", "url": HERMES_API_URL,
|
||||||
"ok": a["gateway_reachable"], "scope": "user"},
|
"ok": a["gateway_reachable"], "scope": "user"},
|
||||||
{"name": "Hermes-GUI (Desktop-Gateway)", "unit": "hermes-builtin-ui", "url": a["hermes_ui_url"],
|
{"name": "Hermes-GUI (Desktop-Gateway)", "unit": "hermes-builtin-ui", "url": a["hermes_ui_url"],
|
||||||
"ok": a["hermes_ui_reachable"], "scope": "user"},
|
"ok": a["hermes_ui_reachable"], "scope": "user"},
|
||||||
{"name": "Mem0 (Gedächtnis)", "unit": "mem0-service", "url": MEM0_SERVICE_URL,
|
|
||||||
"ok": _mem0_reachable(), "scope": "user"},
|
|
||||||
{"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL,
|
{"name": "Voice (STT/TTS)", "unit": "voice-service", "url": VOICE_SERVICE_URL,
|
||||||
"ok": _voice_reachable(), "scope": "user"},
|
"ok": _voice_reachable(), "scope": "user"},
|
||||||
# ttyd hinter dem /console/-Proxy — war als einziger UI-Dienst NICHT in der Liste,
|
# ttyd hinter dem /console/-Proxy — war als einziger UI-Dienst NICHT in der Liste,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Voice-Endpoints für „Mit Hermes reden" (Browser-Voice + 3D-Avatar).
|
|||||||
|
|
||||||
Dünner Layer: STT/TTS werden zum Voice-Sidecar (:8650) geproxyt; der Chat geht an den
|
Dünner Layer: STT/TTS werden zum Voice-Sidecar (:8650) geproxyt; der Chat geht an den
|
||||||
Hermes-`api_server` (:8642, OpenAI-kompatibel) — denselben vollen Agenten mit Tools +
|
Hermes-`api_server` (:8642, OpenAI-kompatibel) — denselben vollen Agenten mit Tools +
|
||||||
geteiltem Mem0 wie CLI/Telegram. Mit stabilem `X-Hermes-Session-Id` hält die Plattform den
|
geteiltem Gedächtnis wie CLI/Telegram. Mit stabilem `X-Hermes-Session-Id` hält die Plattform den
|
||||||
Transcript server-seitig, daher schickt der Client je Turn nur die neue User-Nachricht.
|
Transcript server-seitig, daher schickt der Client je Turn nur die neue User-Nachricht.
|
||||||
|
|
||||||
LAN-only (kein Token in der 2.0-Phase), wie die übrigen MC2-Endpoints.
|
LAN-only (kein Token in der 2.0-Phase), wie die übrigen MC2-Endpoints.
|
||||||
@@ -169,7 +169,7 @@ def voice_metrics() -> dict:
|
|||||||
@router.get("/voice/trace")
|
@router.get("/voice/trace")
|
||||||
def voice_trace(limit: int = 20) -> dict:
|
def voice_trace(limit: int = 20) -> dict:
|
||||||
"""Per-Turn-Trace: die letzten `limit` Chat-Turns mit Stufen-Breakdown (STT · Vision · Hirn ·
|
"""Per-Turn-Trace: die letzten `limit` Chat-Turns mit Stufen-Breakdown (STT · Vision · Hirn ·
|
||||||
Generierung, Mem0 als Unter-Detail). Neueste zuerst. Für die Latenz-Ansicht im Cockpit —
|
Generierung, Gedächtnis als Unter-Detail). Neueste zuerst. Für die Latenz-Ansicht im Cockpit —
|
||||||
damit man den EINEN langsamen Turn sieht, den ein Durchschnitt verschluckt."""
|
damit man den EINEN langsamen Turn sieht, den ein Durchschnitt verschluckt."""
|
||||||
return {"turns": get_trace(limit)}
|
return {"turns": get_trace(limit)}
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
|||||||
headers["X-Hermes-Session-Key"] = body.session_key
|
headers["X-Hermes-Session-Key"] = body.session_key
|
||||||
|
|
||||||
async def gen():
|
async def gen():
|
||||||
# Per-Turn-Trace: sammelt STT (davor, geparkt) + Vision + Hirn-TTFT + Generierung + Mem0
|
# Per-Turn-Trace: sammelt STT (davor, geparkt) + Vision + Hirn-TTFT + Generierung + Gedächtnis
|
||||||
# (Rückruf während) zu EINEM Datensatz -> die Latenz-Ansicht zeigt den einzelnen Hänger.
|
# (Rückruf während) zu EINEM Datensatz -> die Latenz-Ansicht zeigt den einzelnen Hänger.
|
||||||
trace = TurnTrace(session_id=body.session_id, kind="voice")
|
trace = TurnTrace(session_id=body.session_id, kind="voice")
|
||||||
first = True
|
first = True
|
||||||
@@ -325,7 +325,7 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
|||||||
payload = {"model": body.model or HERMES_API_MODEL, "messages": messages, "stream": True}
|
payload = {"model": body.model or HERMES_API_MODEL, "messages": messages, "stream": True}
|
||||||
# Lucys Hirn (Qwen3.6) ist ein Thinking-Modell -> für die gesprochene Assistentin Thinking AUS,
|
# Lucys Hirn (Qwen3.6) ist ein Thinking-Modell -> für die gesprochene Assistentin Thinking AUS,
|
||||||
# sonst generiert es tausende Reasoning-Token VOR der kurzen Antwort (gemessen: 11k Token, ~30s TTFB).
|
# sonst generiert es tausende Reasoning-Token VOR der kurzen Antwort (gemessen: 11k Token, ~30s TTFB).
|
||||||
# Gleiches Muster wie die fast-Spur im Gateway (gateway_proxy.py) und die Mem0-Extraktion.
|
# Gleiches Muster wie die fast-Spur im Gateway (gateway_proxy.py) und die Gedächtnis-Extraktion.
|
||||||
if os.environ.get("MC_VOICE_NO_THINK", "1") not in ("0", "false", "False"):
|
if os.environ.get("MC_VOICE_NO_THINK", "1") not in ("0", "false", "False"):
|
||||||
payload["chat_template_kwargs"] = {"enable_thinking": False}
|
payload["chat_template_kwargs"] = {"enable_thinking": False}
|
||||||
try:
|
try:
|
||||||
@@ -342,7 +342,7 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
|||||||
if first: # Time-To-First-Byte des Hermes-Streams (Verbindungs-Overhead)
|
if first: # Time-To-First-Byte des Hermes-Streams (Verbindungs-Overhead)
|
||||||
trace.note_ttfb()
|
trace.note_ttfb()
|
||||||
first = False
|
first = False
|
||||||
# Erster CONTENT-Delta = echte Hirn-Latenz (Agent-Overhead + Mem0 + LLM-TTFT) —
|
# Erster CONTENT-Delta = echte Hirn-Latenz (Agent-Overhead + Gedächtnis + LLM-TTFT) —
|
||||||
# chat_ttfb misst nur den SSE-Start (~5 ms) und ist dafür blind.
|
# chat_ttfb misst nur den SSE-Start (~5 ms) und ist dafür blind.
|
||||||
if first_content and b'"content"' in chunk:
|
if first_content and b'"content"' in chunk:
|
||||||
trace.note_first_content()
|
trace.note_first_content()
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ def list_snapshots() -> dict:
|
|||||||
|
|
||||||
@router.get("/zeitmaschine/inhalt")
|
@router.get("/zeitmaschine/inhalt")
|
||||||
def snapshot_contents(file: str) -> dict:
|
def snapshot_contents(file: str) -> dict:
|
||||||
"""Top-Level-Komponenten eines Snapshots (mem0, hermes, llama-swap, MANIFEST …)."""
|
"""Top-Level-Komponenten eines Snapshots (hermes, llama-swap, MANIFEST …)."""
|
||||||
if not _FILE_RX.match(file):
|
if not _FILE_RX.match(file):
|
||||||
raise HTTPException(400, "Ungültiger Snapshot-Name.")
|
raise HTTPException(400, "Ungültiger Snapshot-Name.")
|
||||||
p = backup_svc.BACKUP_DIR / file
|
p = backup_svc.BACKUP_DIR / file
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Voll-Zustands-Backup (mem0 + Hermes-Configs/Secrets + llama-swap config).
|
Voll-Zustands-Backup (Hermes-Configs/Secrets + llama-swap config).
|
||||||
Delegiert an deploy/backup.sh (eine Quelle der Wahrheit, identisch zum systemd-Timer);
|
Delegiert an deploy/backup.sh (eine Quelle der Wahrheit, identisch zum systemd-Timer);
|
||||||
Restore läuft bewusst nur per CLI (deploy/restore.sh) — siehe docs/BACKUP.md.
|
Restore läuft bewusst nur per CLI (deploy/restore.sh) — siehe docs/BACKUP.md.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ die häufigste Fehlerquelle. Der Aufrufer übergibt den Host explizit.
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
from config import HERMES_BUILTIN_UI_UPSTREAM, LLAMA_SWAP_URL, MEM0_SERVICE_URL, PORT, V1_UPSTREAM
|
from config import HERMES_BUILTIN_UI_UPSTREAM, LLAMA_SWAP_URL, PORT, V1_UPSTREAM
|
||||||
|
|
||||||
DEFAULT_HOST = "192.168.178.151"
|
DEFAULT_HOST = "192.168.178.151"
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ def build_snippets(host: str = DEFAULT_HOST,
|
|||||||
def check_health() -> dict:
|
def check_health() -> dict:
|
||||||
"""Live-Erreichbarkeit der drei Leitungen, aus Sicht der Box:
|
"""Live-Erreichbarkeit der drei Leitungen, aus Sicht der Box:
|
||||||
Leitung 1 = der ECHTE Modell-Pfad, den Clients nutzen (mc2-gateway bei V1_UPSTREAM,
|
Leitung 1 = der ECHTE Modell-Pfad, den Clients nutzen (mc2-gateway bei V1_UPSTREAM,
|
||||||
sonst llama-swap direkt), Leitung 2 = Gedächtnis-Sidecar (Mem0),
|
sonst llama-swap direkt), Leitung 2 = Natives Hermes-Gedächtnis (hermes-gateway),
|
||||||
Leitung 3 = Desktop-Gateway (Hermes-Builtin-UI)."""
|
Leitung 3 = Desktop-Gateway (Hermes-Builtin-UI)."""
|
||||||
gateway = {"ok": False, "detail": "nicht erreichbar"}
|
gateway = {"ok": False, "detail": "nicht erreichbar"}
|
||||||
try:
|
try:
|
||||||
@@ -144,9 +144,12 @@ def check_health() -> dict:
|
|||||||
memory = {"ok": False, "detail": "nicht erreichbar"}
|
memory = {"ok": False, "detail": "nicht erreichbar"}
|
||||||
try:
|
try:
|
||||||
with httpx.Client(timeout=3.0) as c:
|
with httpx.Client(timeout=3.0) as c:
|
||||||
r = c.get(f"{MEM0_SERVICE_URL}/health")
|
# Hermes Gateway stellt das native Gedächtnis & Agent-Loop bereit
|
||||||
memory = ({"ok": True, "detail": "bereit"} if r.status_code == 200
|
r = c.get("http://127.0.0.1:8642/health")
|
||||||
else {"ok": False, "detail": f"HTTP {r.status_code}"})
|
if r.status_code in (200, 404, 401):
|
||||||
|
memory = {"ok": True, "detail": "Hermes-Nativ bereit"}
|
||||||
|
else:
|
||||||
|
memory = {"ok": False, "detail": f"HTTP {r.status_code}"}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from services import catalog, discover, jobengine, llamaswap, system
|
|||||||
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
# System-Dienste (root, via sudo -n NOPASSWD) vs. User-Dienste (systemctl --user).
|
||||||
SYSTEM_SERVICES = {"llama-swap"}
|
SYSTEM_SERVICES = {"llama-swap"}
|
||||||
USER_SERVICES = {"mission-control-2", "mc2-gateway", "mc2-steward", "box-console",
|
USER_SERVICES = {"mission-control-2", "mc2-gateway", "mc2-steward", "box-console",
|
||||||
"hermes-gateway", "hermes-builtin-ui", "mem0-service", "voice-service"}
|
"hermes-gateway", "hermes-builtin-ui", "voice-service"}
|
||||||
|
|
||||||
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
|
# Engine-Update: lädt den neuesten Vulkan-Build (deploy/update-engine.sh, läuft als root).
|
||||||
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
_REPO_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", ".."))
|
||||||
@@ -73,8 +73,9 @@ def _installed_engine_build() -> int | None:
|
|||||||
out = subprocess.run([bin_path, "--version"], capture_output=True, text=True,
|
out = subprocess.run([bin_path, "--version"], capture_output=True, text=True,
|
||||||
timeout=20, env=env)
|
timeout=20, env=env)
|
||||||
txt = (out.stderr or "") + (out.stdout or "")
|
txt = (out.stderr or "") + (out.stdout or "")
|
||||||
# Formate je nach Build: "version: 9821 (hash)" (aktuell), "build: <hash> (9821)", "b9821".
|
# Formate je nach Build: "version: 0.1.0-dev (build 10426, ...)", "version: 9821 (hash)", "build: <hash> (9821)", "b9821".
|
||||||
if (m := re.search(r"version:\s*(\d{3,})", txt)) \
|
if (m := re.search(r"\bbuild\s+(\d{3,})\b", txt)) \
|
||||||
|
or (m := re.search(r"version:\s*(\d{3,})", txt)) \
|
||||||
or (m := re.search(r"build:\s*\S+\s*\((\d+)\)", txt)) \
|
or (m := re.search(r"build:\s*\S+\s*\((\d+)\)", txt)) \
|
||||||
or (m := re.search(r"\bb(\d{3,})\b", txt)):
|
or (m := re.search(r"\bb(\d{3,})\b", txt)):
|
||||||
return int(m.group(1))
|
return int(m.group(1))
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ Lane-Routing für den eingebauten MC2-Gateway (:9001/v1).
|
|||||||
|
|
||||||
Zwei virtuelle Lanes, die Clients/IDEs auswählen — der Router pickt das echte Modell:
|
Zwei virtuelle Lanes, die Clients/IDEs auswählen — der Router pickt das echte Modell:
|
||||||
- **chat** (= altes `auto`): Alltag → `fast`, schwer/lang → `heavy`.
|
- **chat** (= altes `auto`): Alltag → `fast`, schwer/lang → `heavy`.
|
||||||
- **coding**: Code-Arbeit → `coder` (Qwen3-Coder-Next); riesiger/architektonischer Kontext → `heavy`;
|
- **coding**: Code-Arbeit → `coder`; riesiger/architektonischer Kontext → `heavy`;
|
||||||
triviale Kurzfrage ohne Code → `fast` (Tempo).
|
triviale Kurzfrage ohne Code → `fast` (Tempo).
|
||||||
|
|
||||||
Regelbasiert, sub-ms, ohne Cloud. Schwellen/Aliases liegen in einer UI-editierbaren Policy
|
Regelbasiert, sub-ms, ohne Cloud. Schwellen/Aliases liegen in einer UI-editierbaren Policy
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Health-Wächter der Box (Lucy-Proaktivität, Faden A3).
|
Health-Wächter der Box (Lucy-Proaktivität, Faden A3).
|
||||||
|
|
||||||
Prüft periodisch die Kern-Dienste (Engine, Agent-Hirn, Hermes-Gateway, Mem0,
|
Prüft periodisch die Kern-Dienste (Engine, Agent-Hirn, Hermes-Gateway,
|
||||||
Voice-Sidecar, Platte) und meldet ZUSTANDSWECHSEL in den Melde-Briefkasten
|
Voice-Sidecar, Platte) und meldet ZUSTANDSWECHSEL in den Melde-Briefkasten
|
||||||
(services/announce.py → Lucy spricht es) und via notify.sh (Telegram).
|
(services/announce.py → Lucy spricht es) und via notify.sh (Telegram).
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import time
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import psutil
|
import psutil
|
||||||
from config import HERMES_API_URL, MEM0_SERVICE_URL, MODELS_DIR, VOICE_SERVICE_URL
|
from config import HERMES_API_URL, MODELS_DIR, VOICE_SERVICE_URL
|
||||||
|
|
||||||
from services import announce, llamaswap
|
from services import announce, llamaswap
|
||||||
|
|
||||||
@@ -70,12 +70,9 @@ CHECKS: dict[str, tuple] = {
|
|||||||
"brain": (_check_brain,
|
"brain": (_check_brain,
|
||||||
"Mein Gehirn lädt nicht — ich kann gerade nicht richtig denken. Ein Neustart der Engine könnte helfen.",
|
"Mein Gehirn lädt nicht — ich kann gerade nicht richtig denken. Ein Neustart der Engine könnte helfen.",
|
||||||
"Mein Gehirn ist wieder geladen. Alles klar bei mir."),
|
"Mein Gehirn ist wieder geladen. Alles klar bei mir."),
|
||||||
"hermes": (lambda: _reach(HERMES_API_URL),
|
"hermes": (lambda: _reach(HERMES_API_URL, "/v1/models"),
|
||||||
"Der Agent-Dienst ist ausgefallen — Telegram und meine Tools gehen gerade nicht.",
|
"Der Agent-Dienst ist ausgefallen — Telegram und meine Tools gehen gerade nicht.",
|
||||||
"Der Agent-Dienst läuft wieder."),
|
"Der Agent-Dienst läuft wieder."),
|
||||||
"mem0": (lambda: _reach(MEM0_SERVICE_URL),
|
|
||||||
"Mein Gedächtnis-Dienst ist ausgefallen — ich merke mir vorübergehend nichts Neues.",
|
|
||||||
"Mein Gedächtnis ist wieder online."),
|
|
||||||
"voice": (lambda: _reach(VOICE_SERVICE_URL),
|
"voice": (lambda: _reach(VOICE_SERVICE_URL),
|
||||||
"Der Hör-Dienst auf der Box ist ausgefallen — Spracheingabe könnte haken.",
|
"Der Hör-Dienst auf der Box ist ausgefallen — Spracheingabe könnte haken.",
|
||||||
"Der Hör-Dienst läuft wieder."),
|
"Der Hör-Dienst läuft wieder."),
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ Zwei Sichten auf dieselben Messungen:
|
|||||||
|
|
||||||
**Was MC2 messen kann — und was nicht:** MC2 proxyt den Chat nur an Hermes (:8642). Die Stufen STT,
|
**Was MC2 messen kann — und was nicht:** MC2 proxyt den Chat nur an Hermes (:8642). Die Stufen STT,
|
||||||
Vision, Hirn-TTFT (Zeit bis zum ersten Inhalts-Token) und Generierung sind hier direkt messbar. Der
|
Vision, Hirn-TTFT (Zeit bis zum ersten Inhalts-Token) und Generierung sind hier direkt messbar. Der
|
||||||
**Mem0-Retrieve** läuft zwar in Hermes, ruft aber MC2s `/api/memory` per HTTP zurück → messbar und als
|
**Gedächtnis-Retrieve** läuft zwar in Hermes, ruft aber MC2s Memory-Endpoint per HTTP zurück → messbar
|
||||||
Unter-Detail INNERHALB der Hirn-Zeit ausgewiesen (kein Doppelzählen). Die **Tool-Runden** dagegen laufen
|
und als Unter-Detail INNERHALB der Hirn-Zeit ausgewiesen (kein Doppelzählen). Die **Tool-Runden**
|
||||||
im Hermes-LLM-Loop ohne Callback an MC2 → für MC2 unsichtbar, sie stecken im „Generierung"-Bucket.
|
dagegen laufen im Hermes-LLM-Loop ohne Callback an MC2 → für MC2 unsichtbar, sie stecken im
|
||||||
|
„Generierung"-Bucket.
|
||||||
|
|
||||||
STT (davor) und Mem0-Retrieve (währenddessen) sind separate HTTP-Requests ohne Turn-ID. Auf einem
|
STT (davor) und Gedächtnis-Retrieve (währenddessen) sind separate HTTP-Requests ohne Turn-ID. Auf einem
|
||||||
EIN-Nutzer-Gerät genügt eine schlanke Best-Effort-Korrelation: die zuletzt gemessene STT-Dauer bzw. der
|
EIN-Nutzer-Gerät genügt eine schlanke Best-Effort-Korrelation: die zuletzt gemessene STT-Dauer bzw. der
|
||||||
letzte Retrieve werden global „geparkt" und vom nächsten Chat-Turn eingesammelt (mit Frist-/Reihenfolge-
|
letzte Retrieve werden global „geparkt" und vom nächsten Chat-Turn eingesammelt (mit Frist-/Reihenfolge-
|
||||||
Check). Kein Turn-ID-Durchreichen durch den Lucy-Client nötig.
|
Check). Kein Turn-ID-Durchreichen durch den Lucy-Client nötig.
|
||||||
@@ -36,7 +37,7 @@ _TURNS: deque = deque(maxlen=60) # letzte N vollständige Chat-Turns (Per-Turn-
|
|||||||
# Bekannte Stufen (für stabile UI-Reihenfolge); unbekannte werden trotzdem erfasst.
|
# Bekannte Stufen (für stabile UI-Reihenfolge); unbekannte werden trotzdem erfasst.
|
||||||
STAGES = ("stt", "vision", "memory_retrieve", "chat_ttfb", "chat_first_content", "tts")
|
STAGES = ("stt", "vision", "memory_retrieve", "chat_ttfb", "chat_first_content", "tts")
|
||||||
|
|
||||||
# Best-effort-Korrelation (Ein-Nutzer-Gerät): zuletzt gemessene STT-Dauer / Mem0-Retrieve, je
|
# Best-effort-Korrelation (Ein-Nutzer-Gerät): zuletzt gemessene STT-Dauer / Gedächtnis-Retrieve, je
|
||||||
# (ms, perf_counter-Zeitstempel). Der nächste passende Chat-Turn sammelt sie ein und leert sie.
|
# (ms, perf_counter-Zeitstempel). Der nächste passende Chat-Turn sammelt sie ein und leert sie.
|
||||||
_PARKED: dict[str, tuple[float, float] | None] = {"stt": None, "retrieve": None}
|
_PARKED: dict[str, tuple[float, float] | None] = {"stt": None, "retrieve": None}
|
||||||
|
|
||||||
@@ -53,7 +54,7 @@ def record_stage(stage: str, ms: float) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def park(kind: str, ms: float) -> None:
|
def park(kind: str, ms: float) -> None:
|
||||||
"""Eine Messung, die NICHT im Chat-Request selbst passiert (STT davor, Mem0-Retrieve als
|
"""Eine Messung, die NICHT im Chat-Request selbst passiert (STT davor, Gedächtnis-Retrieve als
|
||||||
Rückruf während), global parken, damit der nächste Chat-Turn sie einsammeln kann."""
|
Rückruf während), global parken, damit der nächste Chat-Turn sie einsammeln kann."""
|
||||||
if ms is None or ms < 0 or kind not in _PARKED:
|
if ms is None or ms < 0 or kind not in _PARKED:
|
||||||
return
|
return
|
||||||
@@ -72,7 +73,7 @@ def _take_stt(max_age: float = 20.0) -> float | None:
|
|||||||
|
|
||||||
|
|
||||||
def _take_retrieve(since_perf: float, max_age: float = 90.0) -> float | None:
|
def _take_retrieve(since_perf: float, max_age: float = 90.0) -> float | None:
|
||||||
"""Geparkten Mem0-Retrieve einsammeln, wenn er NACH dem Turn-Start kam (Rückruf während des
|
"""Geparkten Gedächtnis-Retrieve einsammeln, wenn er NACH dem Turn-Start kam (Rückruf während des
|
||||||
Turns) und frisch ist."""
|
Turns) und frisch ist."""
|
||||||
with _LOCK:
|
with _LOCK:
|
||||||
v = _PARKED.get("retrieve")
|
v = _PARKED.get("retrieve")
|
||||||
@@ -103,7 +104,7 @@ class TurnTrace:
|
|||||||
gehalten; `commit()` schreibt den Datensatz in den Ringpuffer UND speist die rollenden Stats.
|
gehalten; `commit()` schreibt den Datensatz in den Ringpuffer UND speist die rollenden Stats.
|
||||||
|
|
||||||
Balken-Stufen (zeitlich DISJUNKT, ergeben zusammen den Turn): stt · vision · hirn · gen.
|
Balken-Stufen (zeitlich DISJUNKT, ergeben zusammen den Turn): stt · vision · hirn · gen.
|
||||||
Unter-Detail: mem0 (Teil VON hirn, wird separat ausgewiesen, aber NICHT zum Balken addiert)."""
|
Unter-Detail: memory (Teil VON hirn, wird separat ausgewiesen, aber NICHT zum Balken addiert)."""
|
||||||
|
|
||||||
def __init__(self, session_id: str = "", kind: str = "voice") -> None:
|
def __init__(self, session_id: str = "", kind: str = "voice") -> None:
|
||||||
self.id = uuid.uuid4().hex[:8]
|
self.id = uuid.uuid4().hex[:8]
|
||||||
@@ -113,7 +114,7 @@ class TurnTrace:
|
|||||||
self.session_id = (session_id or "")[:24]
|
self.session_id = (session_id or "")[:24]
|
||||||
self.kind = kind
|
self.kind = kind
|
||||||
self.vision_ms: float | None = None # Bildschirm-Beschreibung (falls Bilder)
|
self.vision_ms: float | None = None # Bildschirm-Beschreibung (falls Bilder)
|
||||||
self.hirn_ms: float | None = None # Zeit bis zum ersten Inhalts-Token (Agent + Mem0 + TTFT)
|
self.hirn_ms: float | None = None # Zeit bis zum ersten Inhalts-Token (Agent + Gedächtnis + TTFT)
|
||||||
self.had_images = False
|
self.had_images = False
|
||||||
self.error: str | None = None
|
self.error: str | None = None
|
||||||
|
|
||||||
@@ -130,7 +131,7 @@ class TurnTrace:
|
|||||||
record_stage("chat_ttfb", (time.perf_counter() - self._brain0) * 1000.0) # SSE-Start (~5 ms), nur rollend
|
record_stage("chat_ttfb", (time.perf_counter() - self._brain0) * 1000.0) # SSE-Start (~5 ms), nur rollend
|
||||||
|
|
||||||
def note_first_content(self) -> None:
|
def note_first_content(self) -> None:
|
||||||
"""Erster Inhalts-Delta = echte Hirn-Latenz (Agent-Overhead + Mem0 + LLM-TTFT)."""
|
"""Erster Inhalts-Delta = echte Hirn-Latenz (Agent-Overhead + Gedächtnis + LLM-TTFT)."""
|
||||||
ms = (time.perf_counter() - self._brain0) * 1000.0
|
ms = (time.perf_counter() - self._brain0) * 1000.0
|
||||||
self.hirn_ms = round(ms, 1)
|
self.hirn_ms = round(ms, 1)
|
||||||
record_stage("chat_first_content", ms)
|
record_stage("chat_first_content", ms)
|
||||||
@@ -138,7 +139,7 @@ class TurnTrace:
|
|||||||
def commit(self) -> dict:
|
def commit(self) -> dict:
|
||||||
total = (time.perf_counter() - self.perf0) * 1000.0
|
total = (time.perf_counter() - self.perf0) * 1000.0
|
||||||
stt = _take_stt() # rollend bereits in /voice/stt erfasst
|
stt = _take_stt() # rollend bereits in /voice/stt erfasst
|
||||||
mem0 = _take_retrieve(self.perf0) # rollend bereits in /api/memory erfasst
|
memory = _take_retrieve(self.perf0) # Gedächtnis-Retrieve (HTTP-Rückruf), best-effort
|
||||||
# Generierung = alles nach dem ersten Inhalts-Token bis Stream-Ende.
|
# Generierung = alles nach dem ersten Inhalts-Token bis Stream-Ende.
|
||||||
gen = round(total - self.hirn_ms, 1) if self.hirn_ms is not None else None
|
gen = round(total - self.hirn_ms, 1) if self.hirn_ms is not None else None
|
||||||
rec = {
|
rec = {
|
||||||
@@ -151,7 +152,7 @@ class TurnTrace:
|
|||||||
"vision_ms": self.vision_ms,
|
"vision_ms": self.vision_ms,
|
||||||
"hirn_ms": self.hirn_ms,
|
"hirn_ms": self.hirn_ms,
|
||||||
"gen_ms": gen if (gen is None or gen >= 0) else 0.0,
|
"gen_ms": gen if (gen is None or gen >= 0) else 0.0,
|
||||||
"mem0_ms": mem0,
|
"memory_ms": memory,
|
||||||
"total_ms": round(total, 1),
|
"total_ms": round(total, 1),
|
||||||
"error": self.error,
|
"error": self.error,
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-8
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
MC2-Steward — die Wächter-Loops als EIGENER Prozess (UMBAU v3, P2).
|
MC2-Steward — die Wächter-Loops als EIGENER Prozess (UMBAU v3, P2).
|
||||||
|
|
||||||
Bisher hingen Re-Warm-Wächter, Health-Wächter (sentry) und Mem0-Auto-Dedupe am
|
Bisher hingen Re-Warm-Wächter und Health-Wächter (sentry) am
|
||||||
Lebenszyklus des Steuerpult-Webservers (app.py-Lifespan): jeder MC2-Neustart riss
|
Lebenszyklus des Steuerpult-Webservers (app.py-Lifespan): jeder MC2-Neustart riss
|
||||||
den Wächtern Timing und Flanken-Gedächtnis weg — und ein TOTES Steuerpult konnte
|
den Wächtern Timing und Flanken-Gedächtnis weg — und ein TOTES Steuerpult konnte
|
||||||
sich prinzipbedingt nicht selbst melden. Hier laufen DIESELBEN Loops (unveränderte
|
sich prinzipbedingt nicht selbst melden. Hier laufen DIESELBEN Loops (unveränderte
|
||||||
@@ -11,14 +11,14 @@ Module) als eigener Mini-Dienst (mc2-steward.service, Restart=always):
|
|||||||
In-Process-Nudge aus llamaswap.write_config)
|
In-Process-Nudge aus llamaswap.write_config)
|
||||||
• sentry.sentry_loop — Health-Flanken → Briefkasten (HTTP an MC2) + Telegram;
|
• sentry.sentry_loop — Health-Flanken → Briefkasten (HTTP an MC2) + Telegram;
|
||||||
beobachtet im Steward-Modus AUCH MC2 selbst + mc2-gateway
|
beobachtet im Steward-Modus AUCH MC2 selbst + mc2-gateway
|
||||||
• memory.auto_dedupe_loop — Gedächtnis-Dubletten (HTTP an den Mem0-Sidecar)
|
|
||||||
|
|
||||||
BEWUSST NICHT hier: reminders_loop — der teilt sich Datei UND CRUD-Pfade mit dem
|
BEWUSST NICHT hier: reminders_loop — der teilt sich Datei UND CRUD-Pfade mit dem
|
||||||
/api/reminders-Router (Zwei-Schreiber-Risiko auf mc2-reminders.json); er bleibt im
|
/api/reminders-Router (Zwei-Schreiber-Risiko auf mc2-reminders.json); er bleibt im
|
||||||
Steuerpult. Der Briefkasten-Store gehört weiter EXKLUSIV dem MC2-Prozess — dieser
|
Steuerpult. Der Briefkasten-Store gehört weiter EXKLUSIV dem MC2-Prozess — dieser
|
||||||
Prozess liefert Meldungen per HTTP ab (announce.py, MC_ANNOUNCE_HTTP).
|
Prozess liefert Meldungen per HTTP ab (announce.py, MC_ANNOUNCE_HTTP).
|
||||||
|
|
||||||
Die Loop-Schalter (MC_REWARM_ENABLED / MC_SENTRY_ENABLED / MC_MEM_DEDUPE_ENABLED)
|
Die Loop-Schalter (MC_REWARM_ENABLED / MC_SENTRY_ENABLED)
|
||||||
stehen in der MC2-Unit auf 0 und hier auf Default 1 — reiner Konfig-Split, kein
|
stehen in der MC2-Unit auf 0 und hier auf Default 1 — reiner Konfig-Split, kein
|
||||||
Verhaltens-Code im Steuerpult angefasst. Zeilen dort entfernen = Rollback.
|
Verhaltens-Code im Steuerpult angefasst. Zeilen dort entfernen = Rollback.
|
||||||
"""
|
"""
|
||||||
@@ -28,7 +28,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from config import CONFIG_PATH
|
from config import CONFIG_PATH
|
||||||
from services import memory as memory_svc
|
|
||||||
from services import sentry, warmer
|
from services import sentry, warmer
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@@ -66,10 +65,6 @@ async def main() -> None:
|
|||||||
warmer.INTERVAL, CONFIG_WATCH_S)
|
warmer.INTERVAL, CONFIG_WATCH_S)
|
||||||
if sentry.ENABLED:
|
if sentry.ENABLED:
|
||||||
tasks.append(asyncio.create_task(sentry.sentry_loop()))
|
tasks.append(asyncio.create_task(sentry.sentry_loop()))
|
||||||
if memory_svc.AUTO_DEDUPE_ENABLED:
|
|
||||||
tasks.append(asyncio.create_task(memory_svc.auto_dedupe_loop()))
|
|
||||||
log.info("Mem0-Auto-Dedupe aktiv (alle %ss, Schwelle %s)",
|
|
||||||
memory_svc.AUTO_DEDUPE_INTERVAL, memory_svc.AUTO_DEDUPE_THRESHOLD)
|
|
||||||
if not tasks:
|
if not tasks:
|
||||||
log.warning("steward: alle Loops per Env deaktiviert — nichts zu tun, Ende.")
|
log.warning("steward: alle Loops per Env deaktiviert — nichts zu tun, Ende.")
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ hint=""
|
|||||||
# Stabile Anti-Flail-Orientierung: WER bin ich, WO laufen die Modelle, WAS ist tabu.
|
# Stabile Anti-Flail-Orientierung: WER bin ich, WO laufen die Modelle, WAS ist tabu.
|
||||||
ctx="$(cat <<EOF
|
ctx="$(cat <<EOF
|
||||||
[BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.]
|
[BOX-ORIENTIERUNG — du bist ein Hintergrund-Worker auf der lokalen AI-Box (Linux, Zeit UTC / lokal Europe/Berlin, locale de_DE), NICHT Lucy und kein Dev-Laptop. Du arbeitest EINE Kanban-Aufgabe ab.]
|
||||||
Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642, mem0-Gedaechtnis :8765. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint}
|
Die Modelle / das "Hirn" laufen ausschliesslich ueber den Router llama-swap auf 127.0.0.1:8080 (startet je Modell einen llama-server) bzw. das OpenAI-kompatible Gateway von MC2 auf 127.0.0.1:9001/v1 — es gibt KEINEN anderen Modell-Server, also nicht danach suchen. Weitere Dienste: hermes-gateway :8642. Der MC2-Live-Checkout ~/mission-control-v2 ist nur zum LESEN da (Schreiben/committen TABU — Code-Arbeit immer im frischen Klon deines Task-Workspaces). Grenzen (was gehoert wohin): MC2 = Steuerpult (verwalten/klicken/zusehen, KEIN Chat-UI), Lucy = Chat & Stimme (eigenes Repo), Hermes-Quelle ist TABU (Agenten-Verhalten nur ueber Config/SOUL/Skill/Hook/MCP aendern). Feste Entscheide/Verbote + volle Grenzen-Landkarte: ~/mission-control-v2/docs/wissen/ (GRENZEN/VERDIKTE/FALLEN/STACK) + ~/wissens-vault/eigenbau-landkarte.md.${hint}
|
||||||
TRIAGE (19.07. — Drei-Bahnen-Regel): Ordne deine Karte ZUERST einer Bahn zu und bleib darin: Denken (Konzept/Plan), Bauen (Code schreiben/aendern), Betrieb (SSH/Deploy/Dienste). Fuer Betrieb-Arbeit lade IMMER zuerst den Skill betrieb-playbook (Diagnose-Checklisten; max 3 Anlaeufe pro Hindernis, dann kanban_block mit exaktem Fehlertext — die No-Progress-Bremse erzwingt das mechanisch).
|
TRIAGE (19.07. — Drei-Bahnen-Regel): Ordne deine Karte ZUERST einer Bahn zu und bleib darin: Denken (Konzept/Plan), Bauen (Code schreiben/aendern), Betrieb (SSH/Deploy/Dienste). Fuer Betrieb-Arbeit lade IMMER zuerst den Skill betrieb-playbook (Diagnose-Checklisten; max 3 Anlaeufe pro Hindernis, dann kanban_block mit exaktem Fehlertext — die No-Progress-Bremse erzwingt das mechanisch).
|
||||||
PROTOKOLL-PFLICHT (15.07.): Beende deine Aufgabe IMMER mit dem Tool-Aufruf kanban_complete (fertig) oder kanban_block (Frage/Sackgasse). Eine Text-Zusammenfassung OHNE diesen Aufruf zaehlt als Absturz (Protokollverstoss) und blockiert die Karte — genau so ging am 14.07. eine fertige Arbeit verloren.
|
PROTOKOLL-PFLICHT (15.07.): Beende deine Aufgabe IMMER mit dem Tool-Aufruf kanban_complete (fertig) oder kanban_block (Frage/Sackgasse). Eine Text-Zusammenfassung OHNE diesen Aufruf zaehlt als Absturz (Protokollverstoss) und blockiert die Karte — genau so ging am 14.07. eine fertige Arbeit verloren.
|
||||||
REPO-WEGWEISER mission-control-v2 (NICHT raten/suchen — so liegt es): backend/app.py = Einstieg + Router-Mounting · backend/routers/*.py = REST-Endpunkte · backend/services/*.py = Logik · backend/gateway_app.py = /v1-Gateway-Prozess (:9010) · frontend/src/views/*.tsx = Ansichten · frontend/src/lib/api.ts = Typen+Fetch · frontend/src/lib/queries.ts = TanStack-Query-Hooks (Polling) · frontend/src/nav.ts = Sidebar · deploy/ = Skripte/Units/Hooks/Skills · Projekt-Regeln: AGENTS.md (Wurzel) + docs/wissen/.
|
REPO-WEGWEISER mission-control-v2 (NICHT raten/suchen — so liegt es): backend/app.py = Einstieg + Router-Mounting · backend/routers/*.py = REST-Endpunkte · backend/services/*.py = Logik · backend/gateway_app.py = /v1-Gateway-Prozess (:9010) · frontend/src/views/*.tsx = Ansichten · frontend/src/lib/api.ts = Typen+Fetch · frontend/src/lib/queries.ts = TanStack-Query-Hooks (Polling) · frontend/src/nav.ts = Sidebar · deploy/ = Skripte/Units/Hooks/Skills · Projekt-Regeln: AGENTS.md (Wurzel) + docs/wissen/.
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ check_hermes(){
|
|||||||
done
|
done
|
||||||
if [ "$state" = "done" ]; then
|
if [ "$state" = "done" ]; then
|
||||||
SUMMARY+=("$name: $behind Commits eingespielt, Gehirn-Check grün.")
|
SUMMARY+=("$name: $behind Commits eingespielt, Gehirn-Check grün.")
|
||||||
notify "Hermes-Agent aktualisiert ($behind Commits). Gehirn-Check (Mem0, Tools, Voice) grün — alles läuft."
|
notify "Hermes-Agent aktualisiert ($behind Commits). Gehirn-Check (Gedächtnis, Tools, Voice) grün — alles läuft."
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
# Rot/Timeout: ERST Selbstreparatur versuchen (Config-Bruch selbst ziehen, neue Version behalten) —
|
# Rot/Timeout: ERST Selbstreparatur versuchen (Config-Bruch selbst ziehen, neue Version behalten) —
|
||||||
|
|||||||
+6
-9
@@ -3,8 +3,8 @@
|
|||||||
# zurückkommt. Erzeugt EIN Tarball mc2-state-<ts>.tar.gz, behält die letzten N.
|
# zurückkommt. Erzeugt EIN Tarball mc2-state-<ts>.tar.gz, behält die letzten N.
|
||||||
# Läuft per systemd-Timer (täglich), manuell, oder über den UI-Snapshot-Button.
|
# Läuft per systemd-Timer (täglich), manuell, oder über den UI-Snapshot-Button.
|
||||||
#
|
#
|
||||||
# Inhalt: mem0 (Chroma + history.db) · ~/.hermes (config.yaml, .env, plugins/) ·
|
# Inhalt: ~/.hermes (config.yaml, .env, plugins/) · /etc/llama-swap/config.yaml ·
|
||||||
# /etc/llama-swap/config.yaml · known-good/ (Versions-Manifest, s. u.)
|
# known-good/ (Versions-Manifest, s. u.)
|
||||||
# NICHT enthalten (bewusst): GGUF-Modelle (riesig, neu ladbar), MC2-Code (Git), venvs.
|
# NICHT enthalten (bewusst): GGUF-Modelle (riesig, neu ladbar), MC2-Code (Git), venvs.
|
||||||
#
|
#
|
||||||
# ACHTUNG: das Tarball enthält ~/.hermes/.env (Secrets) → chmod 600, nicht in Git.
|
# ACHTUNG: das Tarball enthält ~/.hermes/.env (Secrets) → chmod 600, nicht in Git.
|
||||||
@@ -20,16 +20,14 @@ RETAIN="${MC_BACKUP_RETAIN:-14}"
|
|||||||
# Off-Box-Ziel (leer = deaktiviert). Default: Proxmox-Host, eigener Key mit minimalem Recht.
|
# Off-Box-Ziel (leer = deaktiviert). Default: Proxmox-Host, eigener Key mit minimalem Recht.
|
||||||
OFFSITE="${MC_BACKUP_OFFSITE:-root@192.168.178.108:/var/lib/vz/mc2-backups}"
|
OFFSITE="${MC_BACKUP_OFFSITE:-root@192.168.178.108:/var/lib/vz/mc2-backups}"
|
||||||
OFFSITE_KEY="${MC_BACKUP_OFFSITE_KEY:-$HOME/.ssh/mc2_offsite}"
|
OFFSITE_KEY="${MC_BACKUP_OFFSITE_KEY:-$HOME/.ssh/mc2_offsite}"
|
||||||
MEM0_DIR="${MC_MEM0_DIR:-/srv/models/mem0}"
|
|
||||||
LSWAP="${MC_CONFIG_PATH:-/etc/llama-swap/config.yaml}"
|
LSWAP="${MC_CONFIG_PATH:-/etc/llama-swap/config.yaml}"
|
||||||
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
||||||
TS="$(date +%Y%m%d-%H%M%S)"
|
TS="$(date +%Y%m%d-%H%M%S)"
|
||||||
|
|
||||||
STAGE="$(mktemp -d)"
|
STAGE="$(mktemp -d)"
|
||||||
trap 'rm -rf "$STAGE"' EXIT
|
trap 'rm -rf "$STAGE"' EXIT
|
||||||
mkdir -p "$STAGE/mem0" "$STAGE/hermes" "$STAGE/llama-swap"
|
mkdir -p "$STAGE/hermes" "$STAGE/llama-swap"
|
||||||
|
|
||||||
[ -d "$MEM0_DIR" ] && cp -a "$MEM0_DIR/." "$STAGE/mem0/" || true
|
|
||||||
[ -f "$HERMES/config.yaml" ] && cp -a "$HERMES/config.yaml" "$STAGE/hermes/" || true
|
[ -f "$HERMES/config.yaml" ] && cp -a "$HERMES/config.yaml" "$STAGE/hermes/" || true
|
||||||
[ -f "$HERMES/.env" ] && cp -a "$HERMES/.env" "$STAGE/hermes/" || true
|
[ -f "$HERMES/.env" ] && cp -a "$HERMES/.env" "$STAGE/hermes/" || true
|
||||||
[ -d "$HERMES/plugins" ] && cp -a "$HERMES/plugins" "$STAGE/hermes/plugins" || true
|
[ -d "$HERMES/plugins" ] && cp -a "$HERMES/plugins" "$STAGE/hermes/plugins" || true
|
||||||
@@ -60,7 +58,7 @@ SRC="${MC2_SRC:-$HOME/mission-control-v2}"
|
|||||||
HERMES_AGENT="${HERMES_AGENT_DIR:-$HOME/.hermes/hermes-agent}"
|
HERMES_AGENT="${HERMES_AGENT_DIR:-$HOME/.hermes/hermes-agent}"
|
||||||
UV="$(command -v uv || echo "$HOME/.local/bin/uv")"
|
UV="$(command -v uv || echo "$HOME/.local/bin/uv")"
|
||||||
|
|
||||||
# pip freeze je venv; mem0-venv ist uv-managed (kein pip-Modul) → uv als Fallback.
|
# pip freeze je venv; voice-venv ist uv-managed (kein pip-Modul) → uv als Fallback.
|
||||||
freeze_venv() { # $1 = python-Pfad, $2 = Zieldatei
|
freeze_venv() { # $1 = python-Pfad, $2 = Zieldatei
|
||||||
if [ ! -x "$1" ]; then echo "venv fehlt: $1" > "$2"; return 0; fi
|
if [ ! -x "$1" ]; then echo "venv fehlt: $1" > "$2"; return 0; fi
|
||||||
"$1" -m pip freeze > "$2" 2>/dev/null \
|
"$1" -m pip freeze > "$2" 2>/dev/null \
|
||||||
@@ -68,7 +66,6 @@ freeze_venv() { # $1 = python-Pfad, $2 = Zieldatei
|
|||||||
|| echo "pip freeze fehlgeschlagen: $1" > "$2"
|
|| echo "pip freeze fehlgeschlagen: $1" > "$2"
|
||||||
}
|
}
|
||||||
freeze_venv "$SRC/backend/.venv/bin/python" "$KG/pip-backend.txt"
|
freeze_venv "$SRC/backend/.venv/bin/python" "$KG/pip-backend.txt"
|
||||||
freeze_venv "$HOME/.mem0/venv/bin/python" "$KG/pip-mem0.txt"
|
|
||||||
freeze_venv "$HOME/.voice/venv/bin/python" "$KG/pip-voice.txt"
|
freeze_venv "$HOME/.voice/venv/bin/python" "$KG/pip-voice.txt"
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -79,7 +76,7 @@ freeze_venv "$HOME/.voice/venv/bin/python" "$KG/pip-voice.txt"
|
|||||||
echo "llama.cpp : $(LD_LIBRARY_PATH=/opt/llamacpp-vulkan /usr/local/bin/llama-server --version 2>&1 | grep -m1 -o 'version: .*' || echo '?')"
|
echo "llama.cpp : $(LD_LIBRARY_PATH=/opt/llamacpp-vulkan /usr/local/bin/llama-server --version 2>&1 | grep -m1 -o 'version: .*' || echo '?')"
|
||||||
echo "llama-swap : $(/usr/local/bin/llama-swap --version 2>/dev/null | head -1 || echo '?')"
|
echo "llama-swap : $(/usr/local/bin/llama-swap --version 2>/dev/null | head -1 || echo '?')"
|
||||||
echo "python-venvs :"
|
echo "python-venvs :"
|
||||||
for P in "$SRC/backend/.venv/bin/python" "$HOME/.mem0/venv/bin/python" "$HOME/.voice/venv/bin/python"; do
|
for P in "$SRC/backend/.venv/bin/python" "$HOME/.voice/venv/bin/python"; do
|
||||||
echo " $P → $("$P" --version 2>&1 || echo 'fehlt')"
|
echo " $P → $("$P" --version 2>&1 || echo 'fehlt')"
|
||||||
done
|
done
|
||||||
} > "$KG/versions.txt" || true
|
} > "$KG/versions.txt" || true
|
||||||
@@ -92,7 +89,7 @@ cat > "$STAGE/MANIFEST.txt" <<EOF
|
|||||||
mc2-state backup
|
mc2-state backup
|
||||||
created : $TS
|
created : $TS
|
||||||
host : $(hostname)
|
host : $(hostname)
|
||||||
inhalt : mem0 (chroma + history.db), hermes (config.yaml, .env, plugins/), llama-swap (config.yaml), known-good/ (Versions-Manifest: pip freeze, Engine, Modelle)
|
inhalt : hermes (config.yaml, .env, plugins/), llama-swap (config.yaml), known-good/ (Versions-Manifest: pip freeze, Engine, Modelle)
|
||||||
restore : bash ~/mission-control-v2/deploy/restore.sh mc2-state-$TS.tar.gz
|
restore : bash ~/mission-control-v2/deploy/restore.sh mc2-state-$TS.tar.gz
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,6 @@ echo "Starte MC2 Deployment..."
|
|||||||
# 1. Neuesten Code holen
|
# 1. Neuesten Code holen
|
||||||
git pull origin main
|
git pull origin main
|
||||||
|
|
||||||
# 1.5 Frontend aus dem Release-Branch holen (nur dist Ordner)
|
|
||||||
echo "Hole gebautes Frontend aus release-dist Branch..."
|
|
||||||
git fetch origin release-dist:refs/remotes/origin/release-dist || true
|
|
||||||
git restore --source=origin/release-dist --staged --worktree frontend/dist 2>/dev/null || echo "Kein release-dist Branch gefunden (erster Lauf?)."
|
|
||||||
|
|
||||||
# 2. Abhängigkeiten prüfen (falls sich was geändert hat)
|
# 2. Abhängigkeiten prüfen (falls sich was geändert hat)
|
||||||
echo "Aktualisiere Python Abhängigkeiten..."
|
echo "Aktualisiere Python Abhängigkeiten..."
|
||||||
|
|||||||
@@ -1,28 +1,15 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Post-Update-Check nach einem Hermes-Agent-Update: läuft unser geteiltes „Gehirn"
|
# Post-Update-Check nach einem Hermes-Agent-Update: läuft unser geteiltes „Gehirn"
|
||||||
# (Mem0 + die mc2-memory-Integration) noch? Exit 0 = alles ok, sonst 1 → der Job wird
|
# (die mc2-memory-Integration) noch? Exit 0 = alles ok, sonst 1 → der Job wird
|
||||||
# im UI rot, damit ein kaputtes Gehirn sofort auffällt.
|
# im UI rot, damit ein kaputtes Gehirn sofort auffällt.
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
MC_URL="${MC_URL:-http://127.0.0.1:9001}"
|
MC_URL="${MC_URL:-http://127.0.0.1:9001}"
|
||||||
MEM0_URL="${MEM0_SERVICE_URL:-http://127.0.0.1:8765}"
|
|
||||||
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
||||||
fail=0
|
fail=0
|
||||||
|
|
||||||
echo "=== Hermes Post-Update: Gehirn-Check ==="
|
echo "=== Hermes Post-Update: Gehirn-Check ==="
|
||||||
|
|
||||||
if curl -sf -m 5 "$MEM0_URL/health" >/dev/null 2>&1; then
|
|
||||||
echo "PASS · Mem0-Sidecar erreichbar"
|
|
||||||
else
|
|
||||||
echo "FAIL · Mem0-Sidecar NICHT erreichbar"; fail=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if curl -sf -m 8 "$MC_URL/api/memory" >/dev/null 2>&1; then
|
|
||||||
echo "PASS · /api/memory antwortet"
|
|
||||||
else
|
|
||||||
echo "FAIL · /api/memory antwortet nicht"; fail=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if grep -qE "^[[:space:]]*provider:[[:space:]]*'?mc2-memory'?" "$HERMES/config.yaml" 2>/dev/null \
|
if grep -qE "^[[:space:]]*provider:[[:space:]]*'?mc2-memory'?" "$HERMES/config.yaml" 2>/dev/null \
|
||||||
&& grep -qE "memory_enabled:[[:space:]]*true" "$HERMES/config.yaml" 2>/dev/null; then
|
&& grep -qE "memory_enabled:[[:space:]]*true" "$HERMES/config.yaml" 2>/dev/null; then
|
||||||
echo "PASS · memory.provider=mc2-memory aktiv"
|
echo "PASS · memory.provider=mc2-memory aktiv"
|
||||||
|
|||||||
@@ -21,17 +21,33 @@ models:
|
|||||||
out: [text]
|
out: [text]
|
||||||
tools: true
|
tools: true
|
||||||
context: 65536
|
context: 65536
|
||||||
Qwen3-Coder-Next:
|
Qwen3.8-27B:
|
||||||
|
# Qwen 3.8 27B: Dichtes 27B-Modell mit hybrider Linear-Attention (48/64 Schichten linear),
|
||||||
|
# nativem Multimodal-Support (mmproj-BF16) und Tool-Calling via --jinja.
|
||||||
|
# Unser neuer Haupt-Coder mit 262k Kontext, Thinking Mode und extrem sauberer Code-Qualität.
|
||||||
cmd: |
|
cmd: |
|
||||||
llama-server -m /srv/models/Qwen3-Coder-Next-GGUF/Qwen3-Coder-Next-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 131072 -ngl 999 -fa on --no-mmap --jinja --cache-reuse 256 -cram 16384
|
llama-server -m /srv/models/Qwen3.8-27B-GGUF/Qwen3.8-27B-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 131072 -ngl 999 -fa on --no-mmap --mmproj /srv/models/Qwen3.8-27B-GGUF/mmproj-BF16.gguf --jinja --parallel 2 -cram 16384 -ctk q8_0 -ctv q8_0
|
||||||
ttl: 5400
|
ttl: 5400
|
||||||
aliases:
|
aliases:
|
||||||
- coder
|
- coder
|
||||||
capabilities:
|
capabilities:
|
||||||
in: [text]
|
in: [text, image]
|
||||||
out: [text]
|
out: [text]
|
||||||
tools: true
|
tools: true
|
||||||
context: 131072
|
context: 65536
|
||||||
|
Muse-Glimmer-30B:
|
||||||
|
# Meta Muse Glimmer 30B: Dichtes 30B-Agenten-Modell mit DFlash-Speculative-Drafting
|
||||||
|
# und 1.8B Perception Multimodal Projector. Ideal als Runtime-Debugger & Fehler-Diagnostiker.
|
||||||
|
cmd: |
|
||||||
|
llama-server -m /srv/models/Muse-Glimmer-30B-GGUF/Muse-Glimmer-30B-UD-Q4_K_XL.gguf --host 127.0.0.1 --port ${PORT} -c 65536 -ngl 999 -fa on --no-mmap --mmproj /srv/models/Muse-Glimmer-30B-GGUF/mmproj-kquant.gguf --jinja --spec-type draft-dflash --spec-draft-model /srv/models/Muse-Glimmer-30B-GGUF/dflash-kquant.gguf
|
||||||
|
ttl: 600
|
||||||
|
aliases:
|
||||||
|
- debugger
|
||||||
|
capabilities:
|
||||||
|
in: [text, image]
|
||||||
|
out: [text]
|
||||||
|
tools: true
|
||||||
|
context: 65536
|
||||||
Qwen3-Embedding-0.6B:
|
Qwen3-Embedding-0.6B:
|
||||||
cmd: |
|
cmd: |
|
||||||
llama-server -m /srv/models/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf --host 127.0.0.1 --port ${PORT} --embedding --pooling last -ngl 999 -fa on --no-mmap -c 8192
|
llama-server -m /srv/models/Qwen3-Embedding-0.6B-GGUF/Qwen3-Embedding-0.6B-Q8_0.gguf --host 127.0.0.1 --port ${PORT} --embedding --pooling last -ngl 999 -fa on --no-mmap -c 8192
|
||||||
@@ -52,11 +68,6 @@ models:
|
|||||||
tools: true
|
tools: true
|
||||||
context: 32768
|
context: 32768
|
||||||
Qwen3-VL-30B-A3B-Instruct:
|
Qwen3-VL-30B-A3B-Instruct:
|
||||||
# Lucys AUGEN — seit 07.07. ON-DEMAND (User-Entscheid: „Gehirn + Embedding reichen
|
|
||||||
# komplett; Vision nur wenn Lucy auf dem Desktop an ist oder die IDE-Lane sie braucht").
|
|
||||||
# NICHT mehr in brains; ttl 900 = 15 Min Nachlauf nach dem letzten Blick. Die Lucy-App
|
|
||||||
# wärmt die Augen beim Start selbst an (useVoiceAgent-Warm-Gate). Vorher: warm, ttl 0 —
|
|
||||||
# das kostete ~19 GB Dauerbelegung und verdrängte den 60-GB-Chef-Gutachter.
|
|
||||||
cmd: |
|
cmd: |
|
||||||
llama-server -m /srv/models/Qwen3-VL-30B-A3B-Instruct-GGUF/Qwen3-VL-30B-A3B-Instruct-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 32768 -ngl 999 -fa on --no-mmap --mmproj /srv/models/Qwen3-VL-30B-A3B-Instruct-GGUF/mmproj-F16.gguf --jinja
|
llama-server -m /srv/models/Qwen3-VL-30B-A3B-Instruct-GGUF/Qwen3-VL-30B-A3B-Instruct-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 32768 -ngl 999 -fa on --no-mmap --mmproj /srv/models/Qwen3-VL-30B-A3B-Instruct-GGUF/mmproj-F16.gguf --jinja
|
||||||
ttl: 900
|
ttl: 900
|
||||||
@@ -66,33 +77,6 @@ models:
|
|||||||
in: [text, image]
|
in: [text, image]
|
||||||
out: [text]
|
out: [text]
|
||||||
context: 32768
|
context: 32768
|
||||||
GLM-4.6V-Flash:
|
|
||||||
cmd: |
|
|
||||||
llama-server -m /srv/models/GLM-4.6V-Flash-GGUF/GLM-4.6V-Flash-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 131072 -ngl 999 -fa on --no-mmap --mmproj /srv/models/GLM-4.6V-Flash-GGUF/mmproj-BF16.gguf --jinja
|
|
||||||
ttl: 300
|
|
||||||
aliases:
|
|
||||||
- scout
|
|
||||||
capabilities:
|
|
||||||
in: [text, image]
|
|
||||||
out: [text]
|
|
||||||
tools: true
|
|
||||||
context: 131072
|
|
||||||
Devstral-Small-2-24B:
|
|
||||||
# KRITIKER (25.07.2026): Gegenleser aus einer ANDEREN Modellfamilie (Mistral, Apache-2.0,
|
|
||||||
# 24B DICHT, 256k Kontext, 68,0 % SWE-bench Verified vs. 59,2 % bei GLM-4.7-Flash).
|
|
||||||
# Loest GLM-4.7-Flash in der kritiker-Rolle ab. Dicht = auf Strix Halo bandbreitengebunden
|
|
||||||
# und damit langsam beim SCHREIBEN — fuer einen Kritiker egal: er liest viel, schreibt wenig.
|
|
||||||
# --jinja ist Pflicht (Chat-Template, sonst kaputte Tool-Calls).
|
|
||||||
cmd: |
|
|
||||||
llama-server -m /srv/models/Devstral-Small-2-GGUF/mistralai_Devstral-Small-2-24B-Instruct-2512-Q4_K_M.gguf --host 127.0.0.1 --port ${PORT} -c 16384 -ngl 999 -fa on --no-mmap --jinja --cache-reuse 256
|
|
||||||
ttl: 1800
|
|
||||||
aliases:
|
|
||||||
- kritiker
|
|
||||||
capabilities:
|
|
||||||
in: [text]
|
|
||||||
out: [text]
|
|
||||||
tools: true
|
|
||||||
context: 16384
|
|
||||||
Qwen3-Reranker-0.6B:
|
Qwen3-Reranker-0.6B:
|
||||||
# Gedächtnis-Zweitstufe (07.07., Rollen-Audit): ordnet Mem0-Suchtreffer nach echter
|
# Gedächtnis-Zweitstufe (07.07., Rollen-Audit): ordnet Mem0-Suchtreffer nach echter
|
||||||
# Relevanz (/v1/rerank, Mungert-GGUF — Community-Konvertierungen liefern oft Nullscores!).
|
# Relevanz (/v1/rerank, Mungert-GGUF — Community-Konvertierungen liefern oft Nullscores!).
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Projekte-Sync (20.08.2026): haelt ~/projekte mit Gitea deckungsgleich.
|
||||||
|
#
|
||||||
|
# Warum: Der Hermes-Desktop laeuft gegen das Box-Backend, sein Ordner-Waehler zeigt
|
||||||
|
# also das Dateisystem der BOX. Damit dort alle Projekte auftauchen, muessen sie hier
|
||||||
|
# liegen — und aktuell sein.
|
||||||
|
#
|
||||||
|
# Regeln (bewusst konservativ — dieses Skript laeuft unbeaufsichtigt):
|
||||||
|
# • NUR fast-forward (`--ff-only`). Kein merge, kein rebase, kein reset, kein force.
|
||||||
|
# • Repos mit lokalen Aenderungen werden UEBERSPRUNGEN, nicht ueberfahren.
|
||||||
|
# • Live-Deployments werden NIE gezogen (mission-control-v2 wird aus dem Deploy
|
||||||
|
# bedient und pusht selbst) — sie bekommen nur eine Verknuepfung, damit sie im
|
||||||
|
# Waehler auftauchen.
|
||||||
|
# • Bestehende Checkouts in ~ bleiben, wo sie sind; ~/projekte bekommt eine
|
||||||
|
# Verknuepfung darauf statt eines zweiten Klons.
|
||||||
|
# • Interne Gitea-IP, NICHT die DDNS-Domain: die ist nachts durch die
|
||||||
|
# Zwangstrennung zeitweise tot (Lehre vom 24.07.).
|
||||||
|
#
|
||||||
|
# Aufruf: projekte-sync.sh [--dry-run]
|
||||||
|
# Exit: 0 = alles gut · 1 = mindestens ein Repo hatte ein Problem
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
GITEA="${GITEA_URL:-http://192.168.178.153:3000}"
|
||||||
|
ZIEL="${PROJEKTE_DIR:-$HOME/projekte}"
|
||||||
|
LOG="${PROJEKTE_SYNC_LOG:-$HOME/.hermes/logs/projekte-sync.log}"
|
||||||
|
DRY=0
|
||||||
|
[ "${1:-}" = "--dry-run" ] && DRY=1
|
||||||
|
|
||||||
|
# Repos, die NICHT nach ~/projekte geklont werden (Live-Deployment oder Altlast).
|
||||||
|
# mission-control-v2: laeuft aus ~/mission-control-v2, Dienste arbeiten daraus.
|
||||||
|
# mission-control: archivierte v1.
|
||||||
|
NIE_ZIEHEN="mission-control-v2"
|
||||||
|
UEBERSPRINGEN="mission-control"
|
||||||
|
|
||||||
|
mkdir -p "$ZIEL" "$(dirname "$LOG")"
|
||||||
|
FEHLER=0
|
||||||
|
|
||||||
|
log() { printf '%s %s\n' "$(date '+%F %T')" "$*" | tee -a "$LOG"; }
|
||||||
|
|
||||||
|
TOKEN=""
|
||||||
|
for f in "$HOME/.config/gitea/create-token"; do
|
||||||
|
[ -r "$f" ] && TOKEN="$(tr -d '[:space:]' < "$f")" && break
|
||||||
|
done
|
||||||
|
if [ -z "$TOKEN" ]; then
|
||||||
|
log "ABBRUCH: kein Gitea-Token unter ~/.config/gitea/create-token"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REPOS="$(curl -s -m 20 -H "Authorization: token $TOKEN" \
|
||||||
|
"$GITEA/api/v1/user/repos?limit=100" \
|
||||||
|
| python3 -c 'import sys,json; [print(r["name"]) for r in json.load(sys.stdin)]' 2>/dev/null)"
|
||||||
|
if [ -z "$REPOS" ]; then
|
||||||
|
log "ABBRUCH: Gitea lieferte keine Repo-Liste ($GITEA)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "=== Sync-Lauf ($(printf '%s\n' "$REPOS" | wc -l) Repos gemeldet)$([ $DRY -eq 1 ] && echo ' [Trockenlauf]') ==="
|
||||||
|
|
||||||
|
# Vorhandenen Checkout eines Repos finden (oberste Home-Ebene bevorzugt).
|
||||||
|
finde_checkout() {
|
||||||
|
local name="$1" d
|
||||||
|
for d in "$HOME/$name" "$ZIEL/$name"; do
|
||||||
|
[ -d "$d/.git" ] && { printf '%s' "$d"; return 0; }
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
verknuepfe() { # $1=Zielpfad des echten Checkouts, $2=Repo-Name
|
||||||
|
local ziel="$1" name="$2" link="$ZIEL/$name"
|
||||||
|
[ "$ziel" = "$link" ] && return 0
|
||||||
|
[ -L "$link" ] && [ "$(readlink -f "$link")" = "$(readlink -f "$ziel")" ] && return 0
|
||||||
|
[ -e "$link" ] && [ ! -L "$link" ] && { log " ! $name: $link existiert und ist keine Verknuepfung — nichts getan"; return 1; }
|
||||||
|
[ $DRY -eq 1 ] && { log " ~ $name: wuerde verknuepfen -> $ziel"; return 0; }
|
||||||
|
ln -sfn "$ziel" "$link" && log " ~ $name: verknuepft -> ${ziel/$HOME/\~}"
|
||||||
|
}
|
||||||
|
|
||||||
|
for name in $REPOS; do
|
||||||
|
case " $UEBERSPRINGEN " in *" $name "*) log " - $name: uebersprungen (Altlast)"; continue;; esac
|
||||||
|
|
||||||
|
if checkout="$(finde_checkout "$name")"; then
|
||||||
|
case " $NIE_ZIEHEN " in
|
||||||
|
*" $name "*)
|
||||||
|
log " = $name: Live-Deployment, kein Pull"
|
||||||
|
verknuepfe "$checkout" "$name"
|
||||||
|
continue;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "$(git -C "$checkout" status --porcelain 2>/dev/null)" ]; then
|
||||||
|
log " ! $name: lokale Aenderungen — uebersprungen (nichts ueberfahren)"
|
||||||
|
verknuepfe "$checkout" "$name"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $DRY -eq 1 ]; then
|
||||||
|
log " ~ $name: wuerde pullen (${checkout/$HOME/\~})"
|
||||||
|
else
|
||||||
|
vorher="$(git -C "$checkout" rev-parse --short HEAD 2>/dev/null)"
|
||||||
|
if out="$(git -C "$checkout" pull --ff-only 2>&1)"; then
|
||||||
|
nachher="$(git -C "$checkout" rev-parse --short HEAD 2>/dev/null)"
|
||||||
|
if [ "$vorher" = "$nachher" ]; then
|
||||||
|
log " = $name: aktuell ($vorher)"
|
||||||
|
else
|
||||||
|
log " + $name: $vorher -> $nachher"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log " ! $name: Pull fehlgeschlagen — $(printf '%s' "$out" | tail -1)"
|
||||||
|
FEHLER=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
verknuepfe "$checkout" "$name"
|
||||||
|
else
|
||||||
|
if [ $DRY -eq 1 ]; then
|
||||||
|
log " ~ $name: wuerde klonen nach ${ZIEL/$HOME/\~}/$name"
|
||||||
|
elif out="$(git clone -q "$GITEA/Hitonabi/$name.git" "$ZIEL/$name" 2>&1)"; then
|
||||||
|
log " + $name: neu geklont"
|
||||||
|
else
|
||||||
|
log " ! $name: Klonen fehlgeschlagen — $(printf '%s' "$out" | tail -1)"
|
||||||
|
FEHLER=1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
log "=== Ende (Fehler: $FEHLER) ==="
|
||||||
|
exit $FEHLER
|
||||||
+1
-4
@@ -15,11 +15,10 @@ set -euo pipefail
|
|||||||
|
|
||||||
MODELS_DIR="${MC_MODELS_DIR:-/srv/models}"
|
MODELS_DIR="${MC_MODELS_DIR:-/srv/models}"
|
||||||
DEST_DIR="$MODELS_DIR/mc2-backups"
|
DEST_DIR="$MODELS_DIR/mc2-backups"
|
||||||
MEM0_DIR="${MC_MEM0_DIR:-/srv/models/mem0}"
|
|
||||||
LSWAP="${MC_CONFIG_PATH:-/etc/llama-swap/config.yaml}"
|
LSWAP="${MC_CONFIG_PATH:-/etc/llama-swap/config.yaml}"
|
||||||
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
HERMES="${HERMES_HOME:-$HOME/.hermes}"
|
||||||
SRC="${MC2_SRC:-$HOME/mission-control-v2}"
|
SRC="${MC2_SRC:-$HOME/mission-control-v2}"
|
||||||
SERVICES="mem0-service mission-control-2 hermes-gateway"
|
SERVICES="mission-control-2 hermes-gateway"
|
||||||
OFFSITE="${MC_BACKUP_OFFSITE:-root@192.168.178.108:/var/lib/vz/mc2-backups}"
|
OFFSITE="${MC_BACKUP_OFFSITE:-root@192.168.178.108:/var/lib/vz/mc2-backups}"
|
||||||
OFFSITE_KEY="${MC_BACKUP_OFFSITE_KEY:-$HOME/.ssh/mc2_offsite}"
|
OFFSITE_KEY="${MC_BACKUP_OFFSITE_KEY:-$HOME/.ssh/mc2_offsite}"
|
||||||
OFFSITE_SSH="ssh -i $OFFSITE_KEY -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new"
|
OFFSITE_SSH="ssh -i $OFFSITE_KEY -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new"
|
||||||
@@ -98,7 +97,6 @@ echo "--- Inhalt ---"; cat "$STAGE/MANIFEST.txt" 2>/dev/null || true; echo "----
|
|||||||
|
|
||||||
if [ "$DRY" = 1 ]; then
|
if [ "$DRY" = 1 ]; then
|
||||||
echo "[dry-run] würde zurücksetzen:"
|
echo "[dry-run] würde zurücksetzen:"
|
||||||
[ -d "$STAGE/mem0" ] && echo " • mem0 → $MEM0_DIR (wird ersetzt)"
|
|
||||||
[ -f "$STAGE/hermes/config.yaml" ] && echo " • $HERMES/config.yaml"
|
[ -f "$STAGE/hermes/config.yaml" ] && echo " • $HERMES/config.yaml"
|
||||||
[ -f "$STAGE/hermes/.env" ] && echo " • $HERMES/.env"
|
[ -f "$STAGE/hermes/.env" ] && echo " • $HERMES/.env"
|
||||||
[ -d "$STAGE/hermes/plugins" ] && echo " • $HERMES/plugins/"
|
[ -d "$STAGE/hermes/plugins" ] && echo " • $HERMES/plugins/"
|
||||||
@@ -119,7 +117,6 @@ bash "$SRC/deploy/backup.sh" || echo " (Pre-Restore-Backup fehlgeschlagen — f
|
|||||||
echo "→ Dienste stoppen…"
|
echo "→ Dienste stoppen…"
|
||||||
systemctl --user stop $SERVICES 2>/dev/null || true
|
systemctl --user stop $SERVICES 2>/dev/null || true
|
||||||
|
|
||||||
if [ -d "$STAGE/mem0" ]; then rm -rf "$MEM0_DIR"; mkdir -p "$MEM0_DIR"; cp -a "$STAGE/mem0/." "$MEM0_DIR/"; fi
|
|
||||||
[ -f "$STAGE/hermes/config.yaml" ] && { mkdir -p "$HERMES"; cp -a "$STAGE/hermes/config.yaml" "$HERMES/"; }
|
[ -f "$STAGE/hermes/config.yaml" ] && { mkdir -p "$HERMES"; cp -a "$STAGE/hermes/config.yaml" "$HERMES/"; }
|
||||||
[ -f "$STAGE/hermes/.env" ] && cp -a "$STAGE/hermes/.env" "$HERMES/"
|
[ -f "$STAGE/hermes/.env" ] && cp -a "$STAGE/hermes/.env" "$HERMES/"
|
||||||
[ -d "$STAGE/hermes/plugins" ] && { mkdir -p "$HERMES/plugins"; cp -a "$STAGE/hermes/plugins/." "$HERMES/plugins/"; }
|
[ -d "$STAGE/hermes/plugins" ] && { mkdir -p "$HERMES/plugins"; cp -a "$STAGE/hermes/plugins/." "$HERMES/plugins/"; }
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ def chunk(t,d):
|
|||||||
c=t+d; return struct.pack(">I",len(d))+c+struct.pack(">I",zlib.crc32(c))
|
c=t+d; return struct.pack(">I",len(d))+c+struct.pack(">I",zlib.crc32(c))
|
||||||
png=b"\x89PNG\r\n\x1a\n"+chunk(b"IHDR",struct.pack(">IIBBBBB",w,h,8,2,0,0,0))+chunk(b"IDAT",zlib.compress(raw))+chunk(b"IEND",b"")
|
png=b"\x89PNG\r\n\x1a\n"+chunk(b"IHDR",struct.pack(">IIBBBBB",w,h,8,2,0,0,0))+chunk(b"IDAT",zlib.compress(raw))+chunk(b"IEND",b"")
|
||||||
url="data:image/png;base64,"+base64.b64encode(png).decode()
|
url="data:image/png;base64,"+base64.b64encode(png).decode()
|
||||||
print(json.dumps({"model":"coder","stream":False,"max_tokens":40,"messages":[{"role":"user","content":[{"type":"image_url","image_url":{"url":url}},{"type":"text","text":"Welche Farbe hat das angehaengte Bild? Antworte nur mit der Farbe."}]}]}))
|
print(json.dumps({"model":"coder","stream":False,"max_tokens":150,"messages":[{"role":"user","content":[{"type":"image_url","image_url":{"url":url}},{"type":"text","text":"Welche Farbe hat das angehaengte Bild? Antworte nur mit der Farbe."}]}]}))
|
||||||
PYIN
|
PYIN
|
||||||
)
|
)
|
||||||
BW_HDR=$(mktemp); BW_OUT=$(mktemp)
|
BW_HDR=$(mktemp); BW_OUT=$(mktemp)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ heavy-Rollen einer Runde am Stueck (heavy bleibt warm), DANN der Skeptiker (ein
|
|||||||
aus — fuer diesen Hintergrund-Job gewollt. heavy hat nur **32k Kontext** → halte das Konzept-
|
aus — fuer diesen Hintergrund-Job gewollt. heavy hat nur **32k Kontext** → halte das Konzept-
|
||||||
Dokument kompakt und schleppe KEINE alten Runden-Protokolle mit (siehe Kontext-Uebergabe).
|
Dokument kompakt und schleppe KEINE alten Runden-Protokolle mit (siehe Kontext-Uebergabe).
|
||||||
- **REASONING-MODELLE brauchen Token-Luft (verifiziert 14.07.).** heavy (gpt-oss) UND der Skeptiker
|
- **REASONING-MODELLE brauchen Token-Luft (verifiziert 14.07.).** heavy (gpt-oss) UND der Skeptiker
|
||||||
(GLM-4.7-Flash) „denken" erst ausfuehrlich (im `reasoning_content`), bevor die eigentliche Antwort
|
(`fast`) „denken" erst ausfuehrlich (im `reasoning_content`), bevor die eigentliche Antwort
|
||||||
in `content` landet — `worker.sh`/`fremdblick.sh` lesen NUR `content`. Zu knappes `max_tokens` =
|
in `content` landet — `worker.sh`/`fremdblick.sh` lesen NUR `content`. Zu knappes `max_tokens` =
|
||||||
das Modell verbraucht alles beim Denken, `content` bleibt LEER, das Skript meldet „FEHLGESCHLAGEN"
|
das Modell verbraucht alles beim Denken, `content` bleibt LEER, das Skript meldet „FEHLGESCHLAGEN"
|
||||||
(obwohl das Modell lief). GLM verbrennt real ~2000 Tokens Denken fuer 2 Zeilen Antwort. Deshalb
|
(obwohl das Modell lief). GLM verbrennt real ~2000 Tokens Denken fuer 2 Zeilen Antwort. Deshalb
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ Vorschlag ausweisen, was fehlt oder wacklig ist.
|
|||||||
NIEMALS auf `main` committen, NIEMALS mergen, NIEMALS deployen oder Dienste neu starten.
|
NIEMALS auf `main` committen, NIEMALS mergen, NIEMALS deployen oder Dienste neu starten.
|
||||||
- **Nur im Worktree arbeiten.** Die Live-Instanz `~/mission-control-v2` bleibt unberuehrt.
|
- **Nur im Worktree arbeiten.** Die Live-Instanz `~/mission-control-v2` bleibt unberuehrt.
|
||||||
- **Warm-Set-Schutz (Speicher vs Latenz).** Worker werden ueber **`worker.sh`** aufgerufen.
|
- **Warm-Set-Schutz (Speicher vs Latenz).** Worker werden ueber **`worker.sh`** aufgerufen.
|
||||||
Fuer die Umsetzung von Code nutzt du **`Qwen3-Coder-Next`** (laedt klein neben dem Warm-Set).
|
Fuer die Umsetzung von Code nutzt du **`coder`** (laedt klein neben dem Warm-Set).
|
||||||
Fuer die initiale **Planung** nutzt du explizit den Chef-Gutachter **`gpt-oss-120b`** (heavy),
|
Fuer die initiale **Planung** nutzt du explizit den Chef-Gutachter **`gpt-oss-120b`** (heavy),
|
||||||
auch wenn dessen Laden (~15s) Lucys Sprech-Latenz temporaer aussetzt. Das ist fuer diesen
|
auch wenn dessen Laden (~15s) Lucys Sprech-Latenz temporaer aussetzt. Das ist fuer diesen
|
||||||
Hintergrund-Prozess gewollt.
|
Hintergrund-Prozess gewollt.
|
||||||
@@ -83,7 +83,7 @@ Bevor du selbst Tasks aufteilst, befragst du zwingend **`gpt-oss-120b`** nach ei
|
|||||||
printf '%s' "Auftrag: $auftrag" | WORKER_MODEL=gpt-oss-120b WORKER_ROLE=plan ~/.hermes/scripts/worker.sh > /tmp/orch-plan.txt
|
printf '%s' "Auftrag: $auftrag" | WORKER_MODEL=gpt-oss-120b WORKER_ROLE=plan ~/.hermes/scripts/worker.sh > /tmp/orch-plan.txt
|
||||||
```
|
```
|
||||||
2. Lies den Plan (`cat /tmp/orch-plan.txt`) und nutze ihn, um den Auftrag in eine **geordnete, nummerierte Liste von Teil-Tasks** zu zerlegen.
|
2. Lies den Plan (`cat /tmp/orch-plan.txt`) und nutze ihn, um den Auftrag in eine **geordnete, nummerierte Liste von Teil-Tasks** zu zerlegen.
|
||||||
3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`Qwen3-Coder-Next` + `build`/`refactor`), (c) Kontext.
|
3. Fuer jeden Teil-Task entscheide: (a) Artefakt, (b) Worker (`coder` + `build`/`refactor`), (c) Kontext.
|
||||||
|
|
||||||
### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags)
|
### 2. Worktree anlegen (Slug = kurzer Kebab-Case-Name des Auftrags)
|
||||||
Worktrees liegen unter `~/.hermes/worktrees/` (NICHT /tmp — ueberlebt keinen Reboot und
|
Worktrees liegen unter `~/.hermes/worktrees/` (NICHT /tmp — ueberlebt keinen Reboot und
|
||||||
@@ -102,7 +102,7 @@ Fuer jeden Bau-Teil-Task:
|
|||||||
```
|
```
|
||||||
printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > ~/.hermes/worktrees/orch-<slug>-work/<n>.out
|
printf '%s' "$eingabe" | WORKER_ROLE=build ~/.hermes/scripts/worker.sh > ~/.hermes/worktrees/orch-<slug>-work/<n>.out
|
||||||
```
|
```
|
||||||
(Default-Modell ist `Qwen3-Coder-Next`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<echte-id>`.)
|
(Default-Modell ist `coder`. Fuer einen bewusst anderen Worker `WORKER_MODEL=<rolle-oder-id>`.)
|
||||||
3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig
|
3. **Pruefe die Ausgabe, bevor du sie verwendest:** Beginnt sie mit `FEHLT:`, hast du zu wenig
|
||||||
Kontext gegeben → nachliefern und erneut rufen. Hat der Worker versehentlich einen ```-Codezaun
|
Kontext gegeben → nachliefern und erneut rufen. Hat der Worker versehentlich einen ```-Codezaun
|
||||||
oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen
|
oder Prosa drumherum gesetzt, entferne ihn. Dann schreibe das saubere Artefakt mit deinen
|
||||||
@@ -164,13 +164,13 @@ GIT-DIFF des Worktrees:
|
|||||||
$(git -C ~/.hermes/worktrees/orch-<slug> diff --cached origin/main)"
|
$(git -C ~/.hermes/worktrees/orch-<slug> diff --cached origin/main)"
|
||||||
```
|
```
|
||||||
Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben.
|
Ist dieser Diff LEER, hast du nicht gestaged (oder nichts gebaut) → NICHT weiter, erst beheben.
|
||||||
**Kritik A — Kompetenz** (`Qwen3-Coder-Next`, starker Coder, vom Bauen noch geladen → KEIN Swap):
|
**Kritik A — Kompetenz** (`coder`, starker Coder, vom Bauen noch geladen → KEIN Swap):
|
||||||
```
|
```
|
||||||
printf '%s' "$EINGABE" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
printf '%s' "$EINGABE" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
||||||
```
|
```
|
||||||
**Kritik B — Fremd-Blick** (`GLM-4.6V-Flash`, andere Modellfamilie, andere blinde Flecken):
|
**Kritik B — Fremd-Blick** (`debugger`, andere Modellfamilie, andere blinde Flecken):
|
||||||
```
|
```
|
||||||
printf '%s' "$EINGABE" | FREMDBLICK_MODE=code FREMDBLICK_MODEL=GLM-4.6V-Flash FREMDBLICK_MAXTOK=3000 ~/.hermes/scripts/fremdblick.sh
|
printf '%s' "$EINGABE" | FREMDBLICK_MODE=code FREMDBLICK_MODEL=debugger FREMDBLICK_MAXTOK=3000 ~/.hermes/scripts/fremdblick.sh
|
||||||
```
|
```
|
||||||
Reihenfolge bewusst: erst A (Coder-Next ist noch warm), dann B (ein Swap zu GLM). Beide beginnen mit
|
Reihenfolge bewusst: erst A (Coder-Next ist noch warm), dann B (ein Swap zu GLM). Beide beginnen mit
|
||||||
`URTEIL: <ABGELEHNT | FREIGABE-MIT-VORBEHALT | FREIGABE>`. **Warte auf beide.**
|
`URTEIL: <ABGELEHNT | FREIGABE-MIT-VORBEHALT | FREIGABE>`. **Warte auf beide.**
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ unangenommene Branches aufbauen — erst wenn die Vor-Etappe in main ist, kommt
|
|||||||
<git -C ~/.hermes/worktrees/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
<git -C ~/.hermes/worktrees/wartung-<slug> diff origin/main>" | FREMDBLICK_MODE=code ~/.hermes/scripts/fremdblick.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Das läuft auf `Qwen3-Coder-Next` (128k, code-stark, anderes Modell als der Qwen3.6-Worker,
|
Das läuft auf `coder` (128k, code-stark, anderes Modell als der Qwen3.6-Worker,
|
||||||
lädt klein neben dem Warm-Set) mit einem Code-Review-Raster. **Warte auf die Ausgabe.** Der
|
lädt klein neben dem Warm-Set) mit einem Code-Review-Raster. **Warte auf die Ausgabe.** Der
|
||||||
Kritiker leitet den Fehlerfall SELBST aus der Bug-Beschreibung ab (glaubt dem Autor nicht),
|
Kritiker leitet den Fehlerfall SELBST aus der Bug-Beschreibung ab (glaubt dem Autor nicht),
|
||||||
spielt den geänderten Code durch und beginnt mit `URTEIL: <ABGELEHNT | FREIGABE-MIT-VORBEHALT
|
spielt den geänderten Code durch und beginnt mit `URTEIL: <ABGELEHNT | FREIGABE-MIT-VORBEHALT
|
||||||
|
|||||||
@@ -3,15 +3,14 @@
|
|||||||
# dass der komplette Inferenz-Stack noch FUNKTIONIERT — nicht nur "Befehl lief durch".
|
# dass der komplette Inferenz-Stack noch FUNKTIONIERT — nicht nur "Befehl lief durch".
|
||||||
# Exit 0 = alles ok, sonst 1 → der jobengine-Job wird im UI ROT (state=failed).
|
# Exit 0 = alles ok, sonst 1 → der jobengine-Job wird im UI ROT (state=failed).
|
||||||
#
|
#
|
||||||
# Pendant zu hermes-postcheck.sh (prüft das Gehirn/Mem0); dieser prüft Router+Engine+MC2
|
# Pendant zu hermes-postcheck.sh (prüft das Gehirn); dieser prüft Router+Engine+MC2
|
||||||
# inkl. einer ECHTEN 1-Token-Inferenz (beweist, dass ein Modell wirklich lädt & generiert).
|
# inkl. einer ECHTEN 1-Token-Inferenz (beweist, dass ein Modell wirklich lädt & generiert).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
SWAP_URL="${MC_LLAMA_SWAP_URL:-http://127.0.0.1:8080}"
|
SWAP_URL="${MC_LLAMA_SWAP_URL:-http://127.0.0.1:8080}"
|
||||||
MC_URL="${MC_URL:-http://127.0.0.1:9001}"
|
MC_URL="${MC_URL:-http://127.0.0.1:9001}"
|
||||||
MEM0_URL="${MEM0_SERVICE_URL:-http://127.0.0.1:8765}"
|
|
||||||
BRAIN="${MC_WARMUP_MODELS:-fast}"; BRAIN="${BRAIN%% *}" # erstes Modell, falls Liste
|
BRAIN="${MC_WARMUP_MODELS:-fast}"; BRAIN="${BRAIN%% *}" # erstes Modell, falls Liste
|
||||||
EMBED="${MC_WARMUP_EMBED:-embed}"; EMBED="${EMBED%% *}" # Embedding-Modell (Mem0/Gedächtnis)
|
EMBED="${MC_WARMUP_EMBED:-embed}"; EMBED="${EMBED%% *}" # Embedding-Modell (Gedächtnis)
|
||||||
fail=0
|
fail=0
|
||||||
|
|
||||||
echo "=== Stack Post-Update: Funktionsprüfung ==="
|
echo "=== Stack Post-Update: Funktionsprüfung ==="
|
||||||
@@ -48,14 +47,14 @@ else
|
|||||||
echo "FAIL · Inferenz auf '$BRAIN' fehlgeschlagen (Modell lädt/generiert nicht)"; fail=1
|
echo "FAIL · Inferenz auf '$BRAIN' fehlgeschlagen (Modell lädt/generiert nicht)"; fail=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3b. Embedding-Modell (für Mem0/Gedächtnis) lädt und liefert einen Vektor?
|
# 3b. Embedding-Modell (für Gedächtnis) lädt und liefert einen Vektor?
|
||||||
eresp="$(curl -s -m 120 -X POST "$SWAP_URL/v1/embeddings" \
|
eresp="$(curl -s -m 120 -X POST "$SWAP_URL/v1/embeddings" \
|
||||||
-H 'Content-Type: application/json' \
|
-H 'Content-Type: application/json' \
|
||||||
-d "{\"model\":\"$EMBED\",\"input\":\"ping\"}" 2>/dev/null)"
|
-d "{\"model\":\"$EMBED\",\"input\":\"ping\"}" 2>/dev/null)"
|
||||||
if printf '%s' "$eresp" | grep -q '"embedding"'; then
|
if printf '%s' "$eresp" | grep -q '"embedding"'; then
|
||||||
echo "PASS · Embedding-Modell '$EMBED' liefert Vektoren"
|
echo "PASS · Embedding-Modell '$EMBED' liefert Vektoren"
|
||||||
else
|
else
|
||||||
echo "FAIL · Embedding-Modell '$EMBED' lädt/antwortet nicht (Mem0/Gedächtnis betroffen)"; fail=1
|
echo "FAIL · Embedding-Modell '$EMBED' lädt/antwortet nicht (Gedächtnis betroffen)"; fail=1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. MC2 selbst gesund (Engine + Gateway erreichbar)?
|
# 4. MC2 selbst gesund (Engine + Gateway erreichbar)?
|
||||||
@@ -85,13 +84,6 @@ if systemctl --user is-enabled --quiet mc2-steward 2>/dev/null; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. Mem0-Sidecar (Gedächtnis) erreichbar?
|
|
||||||
if curl -sf -m 5 "$MEM0_URL/health" >/dev/null 2>&1; then
|
|
||||||
echo "PASS · Mem0-Sidecar erreichbar"
|
|
||||||
else
|
|
||||||
echo "FAIL · Mem0-Sidecar NICHT erreichbar"; fail=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$fail" -eq 0 ]; then
|
if [ "$fail" -eq 0 ]; then
|
||||||
echo "=== STACK OK ✓ ==="
|
echo "=== STACK OK ✓ ==="
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Venv-Audit (monatlich, 17.07.2026): die Python-Nebendienste (Mem0, Voice) altern
|
# Venv-Audit (monatlich, 17.07.2026): die Python-Nebendienste (Voice) altern
|
||||||
# eingefroren — kein Auto-Update-Kanal deckt sie ab. Dieser Audit MELDET nur
|
# eingefroren — kein Auto-Update-Kanal deckt sie ab. Dieser Audit MELDET nur
|
||||||
# (bewusst kein Auto-Update: Python-Deps brechen gern):
|
# (bewusst kein Auto-Update: Python-Deps brechen gern):
|
||||||
# je venv: veraltete Pakete zählen (pip list --outdated) + bekannte
|
# je venv: veraltete Pakete zählen (pip list --outdated) + bekannte
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
# idempotent je Monat). Läuft als Hermes-Cron --no-agent (stdout = Telegram).
|
# idempotent je Monat). Läuft als Hermes-Cron --no-agent (stdout = Telegram).
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
VENVS=("$HOME/.mem0/venv:mem0" "$HOME/.voice/venv:voice")
|
VENVS=("$HOME/.voice/venv:voice")
|
||||||
AUDIT_VENV="$HOME/.venv-audit"
|
AUDIT_VENV="$HOME/.venv-audit"
|
||||||
HERMES="$HOME/.local/bin/hermes"
|
HERMES="$HOME/.local/bin/hermes"
|
||||||
MONAT="$(date '+%Y-%m')"
|
MONAT="$(date '+%Y-%m')"
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
# Eingehängt als ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh in llama-swap.
|
# Eingehängt als ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh in llama-swap.
|
||||||
#
|
#
|
||||||
# `fast` = das Agent-Hirn (Qwen3.6-35B-A3B), `embed` = das Embedding-Modell
|
# `fast` = das Agent-Hirn (Qwen3.6-35B-A3B), `embed` = das Embedding-Modell
|
||||||
# (Qwen3-Embedding-0.6B, für Mem0/Gedächtnis). Die Augen (vision/VL-30B) sind seit 07.07.
|
# (Qwen3-Embedding-0.6B, für Gedächtnis). Die Augen (vision/VL-30B) sind seit 07.07.
|
||||||
# ON-DEMAND (User-Entscheid) und gehören NICHT mehr ins Warm-Set — die Lucy-App wärmt sie
|
# ON-DEMAND (User-Entscheid) und gehören NICHT mehr ins Warm-Set — die Lucy-App wärmt sie
|
||||||
# beim Start selbst an. Modelle werden NICHT automatisch zusammen geladen — jedes einzeln
|
# beim Start selbst an. Modelle werden NICHT automatisch zusammen geladen — jedes einzeln
|
||||||
# anstoßen, sonst bleibt es kalt.
|
# anstoßen, sonst bleibt es kalt.
|
||||||
|
|||||||
+4
-4
@@ -6,7 +6,7 @@
|
|||||||
# neben dem Warm-Set liegt bewusst in DEINER Hand.
|
# neben dem Warm-Set liegt bewusst in DEINER Hand.
|
||||||
#
|
#
|
||||||
# WICHTIG (Speicher & Latenz-Trade-off):
|
# WICHTIG (Speicher & Latenz-Trade-off):
|
||||||
# Der Standard-Worker ist Qwen3-Coder-Next (laedt klein und schnell neben dem Warm-Set).
|
# Der Standard-Worker ist die Rolle `coder` (laedt klein und schnell neben dem Warm-Set).
|
||||||
# Fuer initiale Planung kann "gpt-oss-120b" gerufen werden. Das Modell passt knapp in den
|
# Fuer initiale Planung kann "gpt-oss-120b" gerufen werden. Das Modell passt knapp in den
|
||||||
# Speicher (124 GB RAM), aber das on-demand Laden unterbricht Lucys 1-s-Latenz fuer ca.
|
# Speicher (124 GB RAM), aber das on-demand Laden unterbricht Lucys 1-s-Latenz fuer ca.
|
||||||
# 15-30 Sekunden. Dieser Latenz-Hit wird fuer die Orchestrator-Planung in Kauf genommen.
|
# 15-30 Sekunden. Dieser Latenz-Hit wird fuer die Orchestrator-Planung in Kauf genommen.
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
# in Dateien macht der Manager (Hermes) danach mit seinen eigenen Tools.
|
# in Dateien macht der Manager (Hermes) danach mit seinen eigenen Tools.
|
||||||
#
|
#
|
||||||
# Nutzung: printf '%s' "$teiltask_mit_kontext" | WORKER_ROLE=build worker.sh > /tmp/out.py
|
# Nutzung: printf '%s' "$teiltask_mit_kontext" | WORKER_ROLE=build worker.sh > /tmp/out.py
|
||||||
# printf '%s' "$spec" | WORKER_MODEL=Qwen3-Coder-Next WORKER_ROLE=build worker.sh
|
# printf '%s' "$spec" | WORKER_MODEL=coder WORKER_ROLE=build worker.sh
|
||||||
#
|
#
|
||||||
# Env-Overrides:
|
# Env-Overrides:
|
||||||
# WORKER_MODEL echte llama-swap-ID (Default: Qwen3-Coder-Next). NICHT gpt-oss/heavy (siehe oben).
|
# WORKER_MODEL Rollen-Alias oder echte ID (Default: coder). NICHT gpt-oss/heavy (siehe oben).
|
||||||
# WORKER_ROLE build (Default) | refactor | plan | prose — waehlt das System-Raster.
|
# WORKER_ROLE build (Default) | refactor | plan | prose — waehlt das System-Raster.
|
||||||
# WORKER_SYS eigenes System-Raster (uebersteuert WORKER_ROLE).
|
# WORKER_SYS eigenes System-Raster (uebersteuert WORKER_ROLE).
|
||||||
# WORKER_MAXTOK max_tokens (Default 4000 — Code braucht Platz).
|
# WORKER_MAXTOK max_tokens (Default 4000 — Code braucht Platz).
|
||||||
@@ -29,7 +29,7 @@ set -uo pipefail
|
|||||||
# Achtung: llama-swap listet unter /v1/models die ECHTEN Modell-IDs, nicht die Gateway-Aliase
|
# Achtung: llama-swap listet unter /v1/models die ECHTEN Modell-IDs, nicht die Gateway-Aliase
|
||||||
# (kein "heavy"/"coder"/"scout" hier — die echte ID nehmen).
|
# (kein "heavy"/"coder"/"scout" hier — die echte ID nehmen).
|
||||||
ENDPOINT="${WORKER_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}"
|
ENDPOINT="${WORKER_ENDPOINT:-http://127.0.0.1:8080/v1/chat/completions}"
|
||||||
MODEL="${WORKER_MODEL:-Qwen3-Coder-Next}"
|
MODEL="${WORKER_MODEL:-coder}"
|
||||||
ROLE="${WORKER_ROLE:-build}"
|
ROLE="${WORKER_ROLE:-build}"
|
||||||
MAXTOK="${WORKER_MAXTOK:-4000}"
|
MAXTOK="${WORKER_MAXTOK:-4000}"
|
||||||
TEMP="${WORKER_TEMP:-0.1}"
|
TEMP="${WORKER_TEMP:-0.1}"
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Referenz-Check (20.08.2026): prueft die fuenf Fertig-Kriterien der Messlatte.
|
||||||
|
#
|
||||||
|
# Zweck: "fertig" darf keine Auslegungssache sein. Derselbe Befehl fuer den Agenten
|
||||||
|
# (Selbstpruefung) und fuer die Auswertung. Siehe docs/aufgaben/referenzaufgabe-mem0-ausbau.md
|
||||||
|
#
|
||||||
|
# Aufruf: bash docs/aufgaben/referenz-check.sh [worktree-pfad]
|
||||||
|
# Exit: 0 = alle Kriterien gruen · 1 = mindestens eines rot
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
W="${1:-$HOME/projekte/mc2-referenz}"
|
||||||
|
LINT="${LINT_RUFF:-$HOME/.venv-lint/bin/ruff}"
|
||||||
|
PY="${BACKEND_PY:-$HOME/mission-control-v2/backend/.venv/bin/python}"
|
||||||
|
SMOKE="${SMOKE_SH:-$HOME/mission-control-v2/deploy/self-smoke.sh}"
|
||||||
|
ROT=0
|
||||||
|
|
||||||
|
pruef() { # $1=Nummer $2=Beschreibung $3=Ergebnis(0/1) $4=Detail
|
||||||
|
if [ "$3" -eq 0 ]; then printf ' %s %-34s GRUEN %s\n' "$1" "$2" "${4:-}"
|
||||||
|
else printf ' %s %-34s ROT %s\n' "$1" "$2" "${4:-}"; ROT=1; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
[ -d "$W" ] || { echo "ABBRUCH: Worktree nicht gefunden: $W"; exit 1; }
|
||||||
|
echo "=== Referenz-Check ($W) ==="
|
||||||
|
|
||||||
|
# 1 — Lint
|
||||||
|
if [ -x "$LINT" ]; then
|
||||||
|
out="$("$LINT" check "$W" 2>&1)"; rc=$?
|
||||||
|
pruef 1 "Lint sauber (ruff)" $rc "$(printf '%s' "$out" | tail -1)"
|
||||||
|
else
|
||||||
|
pruef 1 "Lint sauber (ruff)" 1 "ruff fehlt unter $LINT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2 — Backend laedt
|
||||||
|
out="$(cd "$W/backend" && "$PY" -c 'import app' 2>&1)"; rc=$?
|
||||||
|
pruef 2 "Backend importierbar (app)" $rc "$(printf '%s' "$out" | tail -1)"
|
||||||
|
|
||||||
|
# 3 — keine mem0-Reste (docs/ ausgenommen: Historie)
|
||||||
|
n="$(cd "$W" && grep -ril mem0 backend/ mcp/ deploy/ --include='*.py' --include='*.sh' 2>/dev/null | wc -l)"
|
||||||
|
[ "$n" -eq 0 ]; rc=$?
|
||||||
|
pruef 3 "Keine mem0-Reste" $rc "$n Datei(en) mit Treffern"
|
||||||
|
[ "$n" -gt 0 ] && (cd "$W" && grep -ril mem0 backend/ mcp/ deploy/ --include='*.py' --include='*.sh' 2>/dev/null | sed 's/^/ /')
|
||||||
|
|
||||||
|
# 3b — der Sidecar-Ordner selbst
|
||||||
|
if [ -d "$W/mem0_service" ]; then pruef "3b" "Ordner mem0_service entfernt" 1 "liegt noch da"
|
||||||
|
else pruef "3b" "Ordner mem0_service entfernt" 0; fi
|
||||||
|
|
||||||
|
# 4 — Selbsttest (laeuft gegen das LAUFENDE System, nicht gegen den Worktree)
|
||||||
|
if [ -r "$SMOKE" ]; then
|
||||||
|
out="$(timeout 540 bash "$SMOKE" 2>&1)"; rc=$?
|
||||||
|
pruef 4 "Selbsttest self-smoke" $rc "$(printf '%s' "$out" | grep -cE '\bPASS\b') PASS / $(printf '%s' "$out" | grep -cE '\bFAIL\b') FAIL"
|
||||||
|
else
|
||||||
|
pruef 4 "Selbsttest self-smoke" 1 "nicht lesbar: $SMOKE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 5 — MC2 lebt
|
||||||
|
curl -s -m 8 localhost:9001/api/health 2>/dev/null | grep -q '"status":"ok"'; rc=$?
|
||||||
|
pruef 5 "MC2 gesund" $rc
|
||||||
|
|
||||||
|
echo
|
||||||
|
if [ $ROT -eq 0 ]; then echo " ERGEBNIS: FERTIG — alle Kriterien gruen."
|
||||||
|
else echo " ERGEBNIS: NICHT fertig — mindestens ein Kriterium ist rot."; fi
|
||||||
|
exit $ROT
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
# Referenzaufgabe: Mem0-Sidecar restlos ausbauen
|
||||||
|
|
||||||
|
> **Zweck dieser Datei:** Messlatte für den Stack-Umbau (Stufe 2, 20.08.2026). Dieselbe Aufgabe wird
|
||||||
|
> mehrfach mit verschiedenen Modellen gefahren. Gemessen wird **nicht** Tokens pro Sekunde, sondern:
|
||||||
|
> *Wurde sie fertig? Wie lange? Wie viele Züge?*
|
||||||
|
> Alle Pfade und Befehle unten sind am 20.08.2026 auf der Box **nachgemessen**, nicht geraten.
|
||||||
|
|
||||||
|
## Hintergrund
|
||||||
|
|
||||||
|
Mem0 wurde im August abgelöst (`bf145a3`, `eb4bcfc`, `2286879`). Der Rückbau blieb unvollständig:
|
||||||
|
Der Sidecar-Ordner liegt noch im Baum, MC2 fragt weiterhin einen toten Port ab.
|
||||||
|
|
||||||
|
Gemessen: `curl http://127.0.0.1:8765/health` → **keine Antwort**. Kein systemd-Dienst dafür,
|
||||||
|
`/srv/models/mem0` bereits entfernt. Es ist also wirklich tot, nicht nur gerade aus.
|
||||||
|
|
||||||
|
## Arbeitsplatz
|
||||||
|
|
||||||
|
**Isolierter Worktree — NICHT das Live-Deployment:**
|
||||||
|
|
||||||
|
```
|
||||||
|
~/projekte/mc2-referenz Zweig: referenz/mem0-ausbau
|
||||||
|
```
|
||||||
|
|
||||||
|
`~/mission-control-v2` bleibt auf `main` und bedient weiter den laufenden Dienst auf `:9001`.
|
||||||
|
**Dort nichts ändern.**
|
||||||
|
|
||||||
|
## ‼️ Arbeitsweise — zuerst lesen
|
||||||
|
|
||||||
|
**Arbeite Datei für Datei. Lies HÖCHSTENS zwei Dateien, bevor du die erste änderst.**
|
||||||
|
|
||||||
|
Nach jeder geänderten Datei gehst du zur nächsten. **Kein Gesamtplan, keine Vollinventur vorab** — du
|
||||||
|
darfst jederzeit nachlesen, wenn du etwas brauchst. Die Liste unten ist eine Landkarte, keine
|
||||||
|
Leseliste.
|
||||||
|
|
||||||
|
*Warum das hier so deutlich steht:* Im ersten Anlauf (20.08., über Nacht) hat der Agent in sechs
|
||||||
|
Sitzungen ~336 Nachrichten und ~360.000 Tokens verbraucht, 19 Dateien gelesen — und **nicht eine
|
||||||
|
einzige Zeile geändert**. Er lief fünfmal in dasselbe Muster: „Ich habe jetzt das vollständige Bild,
|
||||||
|
jetzt lese ich nur noch die restlichen Dateien, *bevor ich anfange*." Dann war das Zug-Limit
|
||||||
|
erreicht. Sein eigenes Fazit: *„Iterationslimit erreicht, bevor ich die erste Zeile geändert habe."*
|
||||||
|
|
||||||
|
Eine halbfertige Änderung ist wertvoller als eine vollständige Analyse ohne Änderung.
|
||||||
|
|
||||||
|
## Auftrag
|
||||||
|
|
||||||
|
Entferne die Mem0-Reste vollständig, ohne etwas anderes kaputtzumachen.
|
||||||
|
|
||||||
|
**17 Dateien haben Treffer** (gemessen 20.08., selbst nachprüfen statt blind vertrauen):
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/steward.py deploy/stack-postcheck.sh
|
||||||
|
backend/config.py deploy/venv-audit.sh
|
||||||
|
backend/routers/voice.py deploy/agent-hooks/box-steckbrief-inject.sh
|
||||||
|
backend/routers/system.py deploy/warmup.sh
|
||||||
|
backend/routers/zeitmaschine.py deploy/autoupdate.sh
|
||||||
|
backend/services/sentry.py deploy/hermes-postcheck.sh
|
||||||
|
backend/services/voice_metrics.py deploy/restore.sh
|
||||||
|
backend/services/backup.py deploy/backup.sh
|
||||||
|
mcp/mcp_mc.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Dazu der Ordner `mem0_service/` (app.py, migrate.py, requirements.txt, smoke_test.py).
|
||||||
|
|
||||||
|
> ‼️ **Der eigentliche Prüfstein:** Wenn eine API-Antwort ein Feld wie `mem0_reachable` liefert, das
|
||||||
|
> das Frontend liest, darf es nicht einfach verschwinden — sonst bricht die Oberfläche. Entweder das
|
||||||
|
> Frontend mit anpassen oder das Feld sauber entfernen. Wer nur `grep mem0` laufen lässt und alles
|
||||||
|
> wegwirft, produziert eine Fassade.
|
||||||
|
|
||||||
|
## Fertig-Kriterien
|
||||||
|
|
||||||
|
Ausgangswert am 20.08.: **1, 2, 4, 5 sind bereits grün** — sie sind Schutzgeländer, nicht die Arbeit.
|
||||||
|
Nur Kriterium 3 ist offen. Wer 3 erfüllt und dabei 1/2/4/5 kaputtmacht, hat versagt.
|
||||||
|
|
||||||
|
| # | Prüfung | Befehl | Ausgangswert |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | Lint sauber | `cd ~/projekte/mc2-referenz && ~/.venv-lint/bin/ruff check .` | ✅ *All checks passed* |
|
||||||
|
| 2 | Backend lädt | `cd ~/projekte/mc2-referenz/backend && ~/mission-control-v2/backend/.venv/bin/python -c "import app"` | ✅ ok |
|
||||||
|
| 3 | **keine mem0-Reste** | `cd ~/projekte/mc2-referenz && grep -ril mem0 backend/ mcp/ deploy/ --include='*.py' --include='*.sh' \| wc -l` | ❌ **17** → muss **0** werden |
|
||||||
|
| 4 | Selbsttest grün | `bash ~/mission-control-v2/deploy/self-smoke.sh` | ✅ grün |
|
||||||
|
| 5 | MC2 gesund | `curl -s localhost:9001/api/health` | ✅ `"status":"ok"` |
|
||||||
|
|
||||||
|
Treffer in `docs/` sind erlaubt — das ist Historie und darf stehenbleiben.
|
||||||
|
|
||||||
|
Selbstprüfung mit einem Befehl:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash ~/projekte/mc2-referenz/docs/aufgaben/referenz-check.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
> Der Prüfbefehl liegt bewusst unter `docs/aufgaben/` und **nicht** unter `deploy/`. Im ersten Anlauf
|
||||||
|
> lag er in `deploy/`, enthielt selbst siebenmal das Wort „mem0" (er sucht ja danach) und zählte sich
|
||||||
|
> damit in Kriterium 3 mit — das Kriterium war unerfüllbar. In `docs/` wird er nicht durchsucht.
|
||||||
|
|
||||||
|
## Randbedingungen
|
||||||
|
|
||||||
|
- ‼️ **In einer FRISCHEN Sitzung starten**, nicht in einer fortgesetzten. Beim ersten Anlauf hat der
|
||||||
|
Desktop eine Sitzung vom Vortag weitergeführt und deren Kontext mitgeschleppt — die Messung war
|
||||||
|
dadurch wertlos. Im Desktop also einen **neuen Chat** anlegen, nicht den alten öffnen.
|
||||||
|
- **Nicht pushen.** Der Zweig bleibt lokal, damit der Lauf wiederholbar ist.
|
||||||
|
- **Nichts außerhalb des Worktrees anfassen** — kein `/etc`, keine systemd-Dienste, kein Neustart.
|
||||||
|
- Wenn ein Kriterium nicht erfüllbar ist: **ehrlich sagen und aufhören.** Ein klares
|
||||||
|
„Kriterium 3 scheitert an X" ist wertvoller als ein beschönigter Abschluss.
|
||||||
|
|
||||||
|
## Zurücksetzen zwischen den Läufen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/mission-control-v2
|
||||||
|
git worktree remove --force ~/projekte/mc2-referenz
|
||||||
|
git branch -D referenz/mem0-ausbau
|
||||||
|
git worktree add -b referenz/mem0-ausbau ~/projekte/mc2-referenz main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Messprotokoll
|
||||||
|
|
||||||
|
| Lauf | Aufbau | Fertig? | Wanduhr | Züge | Bemerkung |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| — | *Fehlversuch 20.08. 21:10* | ✗ | 76 min, abgebrochen | 8 | Kriterium 3 unerfüllbar + fortgesetzte Alt-Sitzung. **Verwertbar trotzdem:** 19.899 Ausgabe-Tokens, davon nur ~2.600 Zeichen sichtbar → **>95 % unsichtbares Nachdenken**. Kompression feuerte bereits. |
|
||||||
|
| **A** | Qwen3.8-27B, `medium`, `max_turns: 40`, ohne Arbeitsanweisung | ✗ | über Nacht | 6 Sitzungen, ~336 Nachrichten | **0 Dateien geschrieben.** Analyse-Schleife, Zug-Limit erreicht. Werkzeug `file` war die ganze Zeit verfügbar, wurde nie benutzt. ~360k Tokens. Inhaltlich gut: fand `mem0_ms` im Frontend **und** einen toten `memory_svc`-Import in `steward.py`. |
|
||||||
|
| **A2** | dasselbe, **+ Arbeitsanweisung oben**, `max_turns: 80` | | | | ← **als Nächstes.** Prüft, ob die Schleife am Auftrag lag |
|
||||||
|
| D | Qwen3.8-27B, `reasoning_effort: low` | | | | Nächster Hebel, falls A2 auch nur liest |
|
||||||
|
| B | Qwen3.8-27B **+ DFlash-2** | | | | Entwurfsmodell liegt bereit |
|
||||||
|
| C | Qwen3.6-35B-A3B (`fast`, 95,7 t/s) | | | | SWE-bench 73,4 |
|
||||||
|
|
||||||
|
> ★★ **Die Lehre aus Lauf A:** Das ist **kein Tempo-Problem.** Mit 95 t/s statt 12,6 wäre derselbe
|
||||||
|
> Lauf genauso gescheitert — nur schneller. Deshalb kommen A2 und D **vor** den Modellwechseln B und C.
|
||||||
|
|
||||||
|
### Was der Fehlversuch schon gezeigt hat
|
||||||
|
|
||||||
|
Pro Zug erzeugte das Modell ~2.487 Tokens, sichtbar wurden davon 181–451 Zeichen. Bei 12,6 t/s sind
|
||||||
|
das ~26 Minuten reine Erzeugung für ~2.600 Zeichen Text. **Der Engpass ist Denk-Aufwand × Bandbreite,
|
||||||
|
nicht das Harness.** Darum Lauf D vor B und C: er prüft den billigsten Hebel zuerst.
|
||||||
|
|
||||||
|
Inhaltlich war das Modell gut — es fand von selbst den Prüfstein (`mem0_ms` → `memory_ms` im Frontend),
|
||||||
|
statt blind zu löschen.
|
||||||
+30
-36
@@ -1,7 +1,7 @@
|
|||||||
# Stack-Wahrheiten — Infrastruktur, Dienste, Modelle
|
# Stack-Wahrheiten — Infrastruktur, Dienste, Modelle
|
||||||
|
|
||||||
_Live gegen die Box verifiziert am **10.07.2026** (Dienste-Liste, llama-swap-Config, hermes
|
_Live gegen die Box verifiziert am **19.08.2026** (Dienste-Liste, llama-swap-Config, hermes
|
||||||
--version, /api/health). Bei Widerspruch zu älteren Docs (SAVEPOINT.md, docs/memory/) gewinnt
|
--version v0.20.4, llama.cpp b10502, /api/health). Bei Widerspruch zu älteren Docs gewinnt
|
||||||
diese Datei. Wer sie ändert: erst messen, dann schreiben._
|
diese Datei. Wer sie ändert: erst messen, dann schreiben._
|
||||||
|
|
||||||
## Maschinen & Zugänge
|
## Maschinen & Zugänge
|
||||||
@@ -24,55 +24,49 @@ diese Datei. Wer sie ändert: erst messen, dann schreiben._
|
|||||||
- **Wissens-Vault:** `~/wissens-vault/` auf der Box (eigenes git; Lucys Lernschicht:
|
- **Wissens-Vault:** `~/wissens-vault/` auf der Box (eigenes git; Lucys Lernschicht:
|
||||||
Träume, Radar-Funde, **Eigenbau-Landkarte**, Skill-Kandidaten). Im MC2-UI als Wissens-Tab.
|
Träume, Radar-Funde, **Eigenbau-Landkarte**, Skill-Kandidaten). Im MC2-UI als Wissens-Tab.
|
||||||
|
|
||||||
## Dienste auf der Box (live 10.07.2026)
|
## Dienste auf der Box (live 19.08.2026)
|
||||||
|
|
||||||
| Dienst | Port | Zweck |
|
| Dienst | Port | Zweck |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `llama-swap` (System-Dienst) | `:8080` | Modell-Router; Engine = llama.cpp **Vulkan/RADV** (`/opt/llamacpp-vulkan`) |
|
| `llama-swap` (System-Dienst) | `:8080` | Modell-Router; Engine = llama.cpp **Vulkan/RADV** (`/opt/llamacpp-vulkan`, b10502) |
|
||||||
| `mission-control-2` (User) | `:9001` | MC2 Backend+Frontend; `/v1` = OpenAI-Gateway; `/hermes-ui/` = Desktop-Gateway-Proxy |
|
| `mission-control-2` (User) | `:9001` | MC2 Cockpit (FastAPI + React Frontend) & Steuerpult |
|
||||||
| `hermes-gateway` (User) | `:8642` | Hermes Agent API (Lucys Lane) |
|
| `mc2-gateway` (User) | `:9010` | `/v1`-Datenpfad (model:auto Routing + native Bildweiche) |
|
||||||
| `hermes-builtin-ui` (User) | `:9119` (loopback) | Eingebaute Hermes-GUI; LAN-Zugang NUR via MC2-Proxy `/hermes-ui/` (statischer Token im systemd-Drop-in) |
|
| `mc2-steward` (User) | — | Hintergrundwächter (Re-Warm, Health-Sentry, Mem0-Dedupe) |
|
||||||
| `voice-service` (User) | `:8650` | STT (Parakeet/Whisper) + Turn-Check für Lucy |
|
| `hermes-gateway` (User) | `:8642` | Hermes Agent Core API (v0.20.4, Bot Mode) |
|
||||||
|
| `hermes-builtin-ui` (User) | `:9119` (loopback) | Eingebaute Hermes-GUI; LAN-Zugang via MC2-Proxy `/hermes-ui/` |
|
||||||
|
| `mem0-service` (User) | `:8765` | Semantischer Chroma-Gedächtnis-Sidecar |
|
||||||
|
| `voice-service` (User) | `:8650` | STT (faster-whisper) + TTS (Piper/Chatterbox) |
|
||||||
| `box-console` (User) | `:7682` | ttyd-Login-Shell hinter MC2-Proxy |
|
| `box-console` (User) | `:7682` | ttyd-Login-Shell hinter MC2-Proxy |
|
||||||
|
|
||||||
**Stillgelegt (Diät 09.07.):** `hermes-terminal` (:7681) und `hermes-webui` (:8787) — nicht
|
**Stillgelegt:** `hermes-terminal` (:7681), `hermes-webui` (:8787) und alter `governor` (:8100).
|
||||||
wiederbeleben. ⚠️ User-Units per SSH nur sichtbar mit
|
⚠️ User-Units per SSH nur sichtbar mit `export XDG_RUNTIME_DIR=/run/user/$(id -u)` vor `systemctl --user`.
|
||||||
`export XDG_RUNTIME_DIR=/run/user/$(id -u)` vor `systemctl --user`.
|
|
||||||
|
|
||||||
## Modell-Stack (llama-swap, live 10.07.2026)
|
## Modell-Stack (llama-swap, live 19.08.2026)
|
||||||
|
|
||||||
**Warm-Set (Gruppe `brains`, swap:false, persistent:true, alle ttl 0):**
|
**Warm-Set (Gruppe `brains`, swap:false, persistent:false, alle ttl 0):**
|
||||||
Qwen3.6-35B-A3B + Qwen3-Embedding-0.6B + Qwen3-Reranker-0.6B. Sonst NICHTS dauerhaft warm.
|
Qwen3.6-35B-A3B + Qwen3-Embedding-0.6B + Qwen3-Reranker-0.6B. Sonst NICHTS dauerhaft warm.
|
||||||
|
|
||||||
| Alias/Rolle | Modell | ttl | Notizen |
|
| Alias/Rolle | Modell | ttl | Notizen |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| `hermes` + `fast` | Qwen3.6-35B-A3B (UD-Q4_K_M, MTP) | 0 | Agent-Hirn. `-c 131072 --parallel 2` → **65536/Slot**, KV **q8_0**, `--spec-draft-n-max 3` |
|
| `hermes` + `fast` | Qwen3.6-35B-A3B (UD-Q4_K_M, DFlash) | 0 | Agent-Hirn. `-c 131072 --parallel 2` → **65536/Slot**, KV **q8_0**, ~70–90 t/s |
|
||||||
| `reranker` | Qwen3-Reranker-0.6B (q8_0, **Mungert-GGUF**) | 0 | Gedächtnis-Sortierer (`/v1/rerank`); Community-GGUFs liefern oft Nullscores |
|
| `coder` | Qwen3-Coder-Next (Q4_K_M) | 5400 | 131k ctx; Autonomer Coder & Builder (80B MoE, 51,5 t/s) |
|
||||||
| `coder` | Qwen3-Coder-Next (Q4_K_M) | 600 | 131k ctx; Werkstatt-/Orchestrator-Worker + IDE „bauen"; auch `delegation.model` |
|
| `debugger` / `doctor` | Muse-Glimmer-30B (Q4_K_XL, DFlash) | 600 | 65k ctx; Runtime-Diagnostik & Fehler-Debugger (Multimodal) |
|
||||||
| `heavy` | gpt-oss-120b (mxfp4) | 600 | 32k ctx! Planer/Chef-Gutachter/Radar-Analyst — nur Ein-Schuss, lädt seit der LLM-Diät in ~28 s |
|
| `kritiker` | Devstral-Small-2-24B (Q4_K_M) | 1800 | 16k ctx Deckel; Fremdblick-Code-Reviewer gegen Halluzinationen |
|
||||||
| `vision` | Qwen3-VL-30B-A3B (Q4_K_M) | 900 | Augen, ON-DEMAND; Lucy-App wärmt beim Start + alle 10 min |
|
| `vision` | Qwen3-VL-30B-A3B (Q4_K_M) | 900 | Multimodal-Augen (On-Demand) |
|
||||||
| `kritiker` | GLM-4.7-Flash (UD-Q4_K_XL) | 600 | Fremdblick-Prosa + Chef-Gutachter-Vertretung |
|
| `scout` | GLM-4.6V-Flash (Q4_K_M) | 300 | Schneller Tool- und Vision-Allrounder |
|
||||||
| `scout` | GLM-4.6V-Flash (Q4_K_M) | 300 | Vision+Tools-Allrounder |
|
| `heavy` | gpt-oss-120b (mxfp4) | 600 | 32k ctx; Chef-Gutachter (nur nachts / gezielter On-Demand Call) |
|
||||||
|
| `dense-planer` | Qwen3.8-27B (Q4_K_M, BF16 mmproj) | 300 | 65k ctx; Dichtes 27B-Modell für tiefes Planen & Multimodalität |
|
||||||
|
| `reranker` | Qwen3-Reranker-0.6B (q8_0, Mungert) | 0 | Gedächtnis-Sortierer (`/v1/rerank`) |
|
||||||
|
| `embed` | Qwen3-Embedding-0.6B (f16) | 0 | Vektorisierung für Chroma / Mem0 |
|
||||||
|
|
||||||
Config: `/etc/llama-swap/config.yaml` (sudo zum Schreiben). Qwen3.5-122B und VL-8B sind
|
Config: `/etc/llama-swap/config.yaml` (sudo zum Schreiben).
|
||||||
ENTFERNT (07.07.) — die alten Rollback-Pfade existieren nicht mehr.
|
|
||||||
**Merkregeln:** Schutz-Key heißt `persistent:` (nicht `persist:`) · jedes brains-Mitglied
|
|
||||||
MUSS ttl:0 haben · nach JEDEM Config-Reload ist das Warm-Set leer → hermes/embed (+reranker)
|
|
||||||
per Mini-Request anwärmen (direkt curlen ist zuverlässiger als warmup.sh).
|
|
||||||
|
|
||||||
## Hermes (Agent)
|
## Hermes (Agent)
|
||||||
|
|
||||||
- **v0.18.2** (git-Install `~/.hermes/hermes-agent`, Py 3.11), Config `~/.hermes/config.yaml`
|
- **v0.20.4** (git-Install `~/.hermes/hermes-agent`), Config `~/.hermes/config.yaml`, Persona `~/.hermes/SOUL.md` = **Lucy**.
|
||||||
(IMMER Backup vor Änderung), Persona `~/.hermes/SOUL.md` = **Lucy** (alle Kanäle, Anrede
|
- **Bot Mode:** Unterstützt Multi-Bot-Roster (`Lucy` als Haupt-Agentin, `Coder` mit `Qwen3-Coder-Next`, `Debugger` mit `Muse-Glimmer-30B`).
|
||||||
„Commander").
|
- `approvals.mode: smart`, `approvals.cron_mode: deny`.
|
||||||
- Hirn: `model.default` UND `model.model` = `fast`. `delegation.model: coder` (seit 10.07.).
|
- MCP-Server (aus `~/mission-control-v2/mcp/`): mission-control-memory, mission-control-stack, hermes-pc-control, hermes-web-fetch, mcp_voice.
|
||||||
- `approvals.mode: smart`, `approvals.cron_mode: deny` · `platform_toolsets.api_server` =
|
|
||||||
Voice-Diät (NICHT mästen!) · cli-Bucket = volles Coding-Set (auch Hermes Desktop).
|
|
||||||
- MCP-Server (aus `~/mission-control-v2/mcp/`): mission-control-memory, mission-control-stack
|
|
||||||
(mcp_mc.py, 23 Tools), hermes-pc-control, hermes-web-fetch, mcp_voice (dünn, für Voice-Lane).
|
|
||||||
- **Hermes Desktop am PC:** lokales PC-Hermes mit Box-Gateway als Hirn (`:9001/v1`), Profil
|
|
||||||
`pc` für F:\-Arbeit. Remote-Gateway der Box =
|
|
||||||
`http://192.168.178.151:9001/hermes-ui`.
|
|
||||||
|
|
||||||
## Automatik-Fahrplan (was nachts von allein läuft)
|
## Automatik-Fahrplan (was nachts von allein läuft)
|
||||||
|
|
||||||
|
|||||||
+6
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+31
File diff suppressed because one or more lines are too long
+26
File diff suppressed because one or more lines are too long
+36
File diff suppressed because one or more lines are too long
+35
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
import{u,ah as m,k as x,j as s,y as n,ai as b,b as p,n as f,q as h}from"./index-0Xcu6PnS.js";function y(){const d=u(),{data:t=[]}=m(),{showAlert:l,dialogElement:o}=x();async function i(e){try{await f(`/api/jobs/${e}/cancel`,{method:"POST"}),d.invalidateQueries({queryKey:h.jobs})}catch(c){l("Fehler",c.message)}}const a=t.filter(e=>e.state==="running"||e.state==="queued"),r=t.filter(e=>e.state!=="running"&&e.state!=="queued").slice(-3);return a.length===0&&r.length===0?null:s.jsxs("div",{className:"space-y-3 mc-card p-4",children:[s.jsx("div",{className:"text-[11px] font-semibold text-muted-foreground uppercase tracking-wider",children:"Aktive Downloads"}),a.map(e=>s.jsxs("div",{className:"space-y-1.5 p-3 rounded-xl bg-background/20 border border-border/40",children:[s.jsxs("div",{className:"flex justify-between items-center text-xs",children:[s.jsx("span",{className:"font-semibold truncate max-w-[250px]",children:e.label}),s.jsxs("div",{className:"flex items-center gap-3",children:[s.jsxs("span",{className:"text-muted-foreground font-mono",children:[e.progress??0,"% • ",n(e.done_bytes),"/",n(e.total_bytes),e.eta_s?` • ETA ${b(e.eta_s)}`:""]}),s.jsx("button",{onClick:()=>i(e.id),className:"text-[10px] text-red-400 hover:text-red-300 font-semibold border border-red-500/25 bg-red-500/5 px-2 py-0.5 rounded transition-all cursor-pointer",children:"Abbrechen"})]})]}),s.jsx("div",{className:"h-1.5 overflow-hidden rounded-full bg-muted",children:s.jsx("div",{className:"h-full rounded-full bg-primary transition-all duration-500",style:{width:`${e.progress??0}%`}})})]},e.id)),r.map(e=>s.jsxs("div",{className:"flex justify-between items-center text-xs text-muted-foreground px-1",children:[s.jsx("span",{className:"truncate",children:e.label}),s.jsx("span",{className:p("font-semibold text-[10px] px-1.5 py-0.5 rounded uppercase font-mono",e.state==="done"?"bg-emerald-500/10 text-emerald-400":"bg-amber-500/10 text-amber-400"),children:e.state})]},e.id)),o]})}export{y as J};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import{I as b,u as f,K as h,r as d,j as e,b as c,J as g,T as p,R as j,Q as N,n as v,U as w,q as x}from"./index-0Xcu6PnS.js";function y(){const{data:s}=b(),m=f(),r=s!=null&&s.box_console_url?h(s.box_console_url):void 0,a=s==null?void 0:s.box_console_reachable,[l,o]=d.useState(!1),[i,n]=d.useState("");async function u(){o(!0),n("");try{const t=await v("/api/maintenance/restart",{method:"POST",body:JSON.stringify({service:"box-console"})});n(t.ok?"Neu gestartet — einen Moment, dann lädt das Terminal.":`Fehlgeschlagen: ${t.err||"Unbekannter Fehler"}`),w(m,x.agentStatus,x.services)}catch(t){n(`Fehlgeschlagen: ${(t==null?void 0:t.message)||t}`)}finally{o(!1)}}return e.jsxs("div",{className:"flex h-full flex-col gap-4",children:[e.jsxs("div",{className:"flex flex-wrap items-end justify-between gap-3",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text font-space text-2xl font-bold tracking-tight text-transparent",children:"Konsole"}),e.jsx("p",{className:"flex items-center gap-2 text-sm text-muted-foreground",children:"Direkte Shell auf der Box — wie ein SSH-Fenster, mitten im Browser."})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsxs("span",{className:"flex items-center gap-1.5 text-[11px] font-medium text-muted-foreground",children:[e.jsx("span",{className:c("h-2 w-2 rounded-full",a?"bg-emerald-500 animate-pulse":"bg-amber-500")}),a?"online":"offline"]}),r&&e.jsxs("a",{href:r,target:"_blank",rel:"noopener",className:"flex h-8 items-center gap-1.5 rounded-lg border border-border/60 bg-background/20 px-3 text-xs font-semibold text-muted-foreground transition-all hover:border-primary/50 hover:text-foreground",children:[e.jsx(g,{className:"h-3.5 w-3.5"})," In neuem Tab"]})]})]}),r?e.jsxs("div",{className:"relative min-h-[58vh] flex-1 overflow-hidden rounded-2xl border border-border/60 bg-black/50 shadow-lg shadow-black/25",children:[a===!1&&e.jsxs("div",{className:"absolute inset-0 z-10 flex flex-col items-center justify-center gap-3 bg-black/70 text-center",children:[e.jsx(p,{className:"h-8 w-8 text-amber-400"}),e.jsx("div",{className:"text-sm font-semibold text-amber-300",children:"Konsole nicht erreichbar"}),e.jsxs("p",{className:"max-w-sm text-[11px] leading-normal text-muted-foreground",children:["Der Terminal-Dienst (",e.jsx("code",{className:"font-mono text-primary",children:"box-console"}),") läuft gerade nicht."]}),e.jsxs("button",{onClick:u,disabled:l,className:"flex h-9 items-center gap-1.5 rounded-lg border border-amber-500/40 bg-amber-500/10 px-4 text-[11px] font-bold uppercase tracking-wide text-amber-300 transition-all hover:bg-amber-500/20 cursor-pointer disabled:opacity-50",children:[e.jsx(j,{className:c("h-3.5 w-3.5",l&&"animate-spin")})," Dienst neu starten"]}),i&&e.jsx("p",{className:"max-w-sm text-[11px] text-muted-foreground",children:i})]}),e.jsx("iframe",{src:r,title:"Box-Konsole",className:"h-full w-full border-0",style:{minHeight:"58vh"}})]}):e.jsxs("div",{className:"flex min-h-[58vh] flex-1 items-center justify-center rounded-2xl border border-border/60 bg-background/20 text-xs text-muted-foreground",children:[e.jsx(N,{className:"mr-2 h-4 w-4"})," Lade Konsole…"]})]})}export{y as KonsoleView};
|
||||||
+83
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
|||||||
|
import{c as s,j as e}from"./index-0Xcu6PnS.js";/**
|
||||||
|
* @license lucide-react v0.460.0 - ISC
|
||||||
|
*
|
||||||
|
* This source code is licensed under the ISC license.
|
||||||
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
|
*/const r=s("Hammer",[["path",{d:"m15 12-8.373 8.373a1 1 0 1 1-3-3L12 9",key:"eefl8a"}],["path",{d:"m18 15 4-4",key:"16gjal"}],["path",{d:"m21.5 11.5-1.914-1.914A2 2 0 0 1 19 8.172V7l-2.26-2.26a6 6 0 0 0-4.202-1.756L9 2.96l.92.82A6.18 6.18 0 0 1 12 8.4V10l2 2h1.172a2 2 0 0 1 1.414.586L18.5 14.5",key:"b7pghm"}]]);function c({icon:a,children:t}){return e.jsxs("p",{className:"flex items-center gap-1.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60",children:[e.jsx(a,{className:"h-3.5 w-3.5"})," ",t]})}export{r as H,c as S};
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
var M=r=>{throw TypeError(r)};var O=(r,e,t)=>e.has(r)||M("Cannot "+t);var a=(r,e,t)=>(O(r,e,"read from private field"),t?t.call(r):e.get(r)),y=(r,e,t)=>e.has(r)?M("Cannot add the same private member more than once"):e instanceof WeakSet?e.add(r):e.set(r,t),v=(r,e,t,n)=>(O(r,e,"write to private field"),n?n.call(r,t):e.set(r,t),t),j=(r,e,t)=>(O(r,e,"access private method"),t);import{a9 as B,aa as F,ab as E,ac as I,ad as P,u as A,r as f,ae as J,af as Q,c as T,ag as U,j as s,G as D,T as H,b as K,L,M as G,q as V,n as R}from"./index-0Xcu6PnS.js";import{J as _}from"./JobsBar-JgL4-cSI.js";var c,g,o,h,m,w,C,q,z=(q=class extends B{constructor(e,t){super();y(this,m);y(this,c);y(this,g);y(this,o);y(this,h);v(this,c,e),this.setOptions(t),this.bindMethods(),j(this,m,w).call(this)}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){var n;const t=this.options;this.options=a(this,c).defaultMutationOptions(e),F(this.options,t)||a(this,c).getMutationCache().notify({type:"observerOptionsUpdated",mutation:a(this,o),observer:this}),t!=null&&t.mutationKey&&this.options.mutationKey&&E(t.mutationKey)!==E(this.options.mutationKey)?this.reset():((n=a(this,o))==null?void 0:n.state.status)==="pending"&&a(this,o).setOptions(this.options)}onUnsubscribe(){var e;this.hasListeners()||(e=a(this,o))==null||e.removeObserver(this)}onMutationUpdate(e){j(this,m,w).call(this),j(this,m,C).call(this,e)}getCurrentResult(){return a(this,g)}reset(){var e;(e=a(this,o))==null||e.removeObserver(this),v(this,o,void 0),j(this,m,w).call(this),j(this,m,C).call(this)}mutate(e,t){var n;return v(this,h,t),(n=a(this,o))==null||n.removeObserver(this),v(this,o,a(this,c).getMutationCache().build(a(this,c),this.options)),a(this,o).addObserver(this),a(this,o).execute(e)}},c=new WeakMap,g=new WeakMap,o=new WeakMap,h=new WeakMap,m=new WeakSet,w=function(){var t;const e=((t=a(this,o))==null?void 0:t.state)??I();v(this,g,{...e,isPending:e.status==="pending",isSuccess:e.status==="success",isError:e.status==="error",isIdle:e.status==="idle",mutate:this.mutate,reset:this.reset})},C=function(e){P.batch(()=>{var t,n,l,x,d,p,k,i;if(a(this,h)&&this.hasListeners()){const u=a(this,g).variables,N=a(this,g).context,S={client:a(this,c),meta:this.options.meta,mutationKey:this.options.mutationKey};if((e==null?void 0:e.type)==="success"){try{(n=(t=a(this,h)).onSuccess)==null||n.call(t,e.data,u,N,S)}catch(b){Promise.reject(b)}try{(x=(l=a(this,h)).onSettled)==null||x.call(l,e.data,null,u,N,S)}catch(b){Promise.reject(b)}}else if((e==null?void 0:e.type)==="error"){try{(p=(d=a(this,h)).onError)==null||p.call(d,e.error,u,N,S)}catch(b){Promise.reject(b)}try{(i=(k=a(this,h)).onSettled)==null||i.call(k,void 0,e.error,u,N,S)}catch(b){Promise.reject(b)}}}this.listeners.forEach(u=>{u(a(this,g))})})},q);function W(r,e){const t=A(),[n]=f.useState(()=>new z(t,r));f.useEffect(()=>{n.setOptions(r)},[n,r]);const l=f.useSyncExternalStore(f.useCallback(d=>n.subscribe(P.batchCalls(d)),[n]),()=>n.getCurrentResult(),()=>n.getCurrentResult()),x=f.useCallback((d,p)=>{n.mutate(d,p).catch(J)},[n]);if(l.error&&Q(n.options.throwOnError,[l.error]))throw l.error;return{...l,mutate:x,mutateAsync:l.mutate}}/**
|
||||||
|
* @license lucide-react v0.460.0 - ISC
|
||||||
|
*
|
||||||
|
* This source code is licensed under the ISC license.
|
||||||
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
|
*/const X=T("Play",[["polygon",{points:"6 3 20 12 6 21 6 3",key:"1oa8hb"}]]);function ee(){const r=A(),{data:e,isLoading:t,isError:n}=U({queryKey:["skills-list"],queryFn:()=>R("/api/skills"),refetchInterval:5e3}),[l,x]=f.useState(null),[d,p]=f.useState(null),k=W({mutationFn:i=>R("/api/skills/run",{method:"POST",body:JSON.stringify({skill_name:i})}),onMutate:i=>{x(i),p(null)},onSuccess:(i,u)=>{x(null),p({name:u,ok:i.ok,msg:i.msg||i.err||"Unbekannter Fehler"}),r.invalidateQueries({queryKey:V.jobs}),r.invalidateQueries({queryKey:["skills-list"]})},onError:(i,u)=>{x(null),p({name:u,ok:!1,msg:String(i)})}});return s.jsxs("div",{className:"space-y-6",children:[s.jsx("div",{className:"flex flex-col sm:flex-row justify-between sm:items-center gap-4",children:s.jsxs("div",{children:[s.jsx("h1",{className:"text-2xl font-space font-bold tracking-tight bg-gradient-to-r from-foreground via-foreground to-primary bg-clip-text text-transparent",children:"Skills & Jobs"}),s.jsx("p",{className:"text-sm text-muted-foreground",children:"Löst autonome Agent-Skills manuell auf der Box aus. Die Ausführung erfolgt im Hintergrund durch Hermes."})]})}),s.jsx(_,{}),d&&s.jsxs("div",{className:K("p-4 rounded-lg border flex gap-3 items-start",d.ok?"bg-emerald-500/10 border-emerald-500/20 text-emerald-600 dark:text-emerald-400":"bg-red-500/10 border-red-500/20 text-red-600 dark:text-red-400"),children:[d.ok?s.jsx(D,{className:"h-5 w-5 mt-0.5 shrink-0"}):s.jsx(H,{className:"h-5 w-5 mt-0.5 shrink-0"}),s.jsxs("div",{children:[s.jsx("div",{className:"font-semibold text-sm",children:d.name}),s.jsx("div",{className:"text-sm opacity-90",children:d.msg})]})]}),t&&s.jsx("div",{className:"flex h-32 items-center justify-center",children:s.jsx(L,{className:"h-6 w-6 animate-spin text-muted-foreground"})}),n&&s.jsx("div",{className:"text-red-500 p-4 mc-card text-sm",children:"Fehler beim Laden der Skills. Ist das Backend erreichbar?"}),(e==null?void 0:e.skills)&&s.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",children:[e.skills.map(i=>s.jsxs("div",{className:"mc-card p-5 flex flex-col hover:border-primary/40 transition-colors group",children:[s.jsxs("div",{className:"flex items-start justify-between gap-3 mb-2",children:[s.jsxs("div",{className:"flex items-center gap-2 font-semibold text-base font-space",children:[s.jsx(G,{className:"h-5 w-5 text-primary/80"}),i.name]}),s.jsxs("button",{onClick:()=>k.mutate(i.name),disabled:l===i.name||i.running||k.isPending,className:K("shrink-0 h-8 px-3 rounded-md text-xs font-semibold uppercase tracking-wider flex items-center gap-1.5 transition-all cursor-pointer",l===i.name||i.running?"bg-sky-500/10 border border-sky-500/40 text-sky-400 cursor-not-allowed":"bg-primary/10 text-primary hover:bg-primary hover:text-primary-foreground group-hover:shadow-md group-hover:shadow-primary/20"),children:[l===i.name||i.running?s.jsx(L,{className:"h-3.5 w-3.5 animate-spin"}):s.jsx(X,{className:"h-3.5 w-3.5"}),l===i.name||i.running?"Läuft …":"Starten"]})]}),s.jsx("div",{className:"text-sm text-muted-foreground flex-1",children:i.description})]},i.name)),e.skills.length===0&&s.jsxs("div",{className:"col-span-full p-8 text-center text-muted-foreground mc-card border-dashed border-2",children:["Keine Skills im Ordner ",s.jsx("code",{className:"text-xs text-foreground bg-muted px-1.5 py-0.5 rounded",children:"deploy/skills"})," gefunden."]})]})]})}export{ee as SkillsView};
|
||||||
+47
File diff suppressed because one or more lines are too long
@@ -0,0 +1,6 @@
|
|||||||
|
import{c as o}from"./index-0Xcu6PnS.js";/**
|
||||||
|
* @license lucide-react v0.460.0 - ISC
|
||||||
|
*
|
||||||
|
* This source code is licensed under the ISC license.
|
||||||
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
|
*/const n=o("ChevronDown",[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]]);export{n as C};
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
import{c as e}from"./index-0Xcu6PnS.js";/**
|
||||||
|
* @license lucide-react v0.460.0 - ISC
|
||||||
|
*
|
||||||
|
* This source code is licensed under the ISC license.
|
||||||
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
|
*/const o=e("CodeXml",[["path",{d:"m18 16 4-4-4-4",key:"1inbqp"}],["path",{d:"m6 8-4 4 4 4",key:"15zrgr"}],["path",{d:"m14.5 4-5 16",key:"e7oirm"}]]);export{o as C};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import{c as a}from"./index-0Xcu6PnS.js";/**
|
||||||
|
* @license lucide-react v0.460.0 - ISC
|
||||||
|
*
|
||||||
|
* This source code is licensed under the ISC license.
|
||||||
|
* See the LICENSE file in the root directory of this source tree.
|
||||||
|
*/const o=a("FolderOpen",[["path",{d:"m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2",key:"usdka0"}]]);export{o as F};
|
||||||
+435
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user