From 46f108b6f3b4668ab5d94b389d0d7cf343e52104 Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Sat, 27 Jun 2026 21:32:36 +0200 Subject: [PATCH] =?UTF-8?q?Feat:=20Update-Absicherung=20=E2=80=94=20Versio?= =?UTF-8?q?ns-Pins=20+=20Smoke-Test=20+=20Upgrade-Doku?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Schliesst den offenen "Update"-Punkt aus dem Upgrade-Playbook (Backup war Schicht 1): - mem0_service/requirements.txt jetzt GEPINNT (mem0ai==2.0.8, chromadb==1.5.9, fastapi==0.138.1, uvicorn==0.49.0) -> Upgrades nur absichtlich. Grund: der Sidecar nutzt mem0-Interna (NoThink-Swap, Roh-Chroma-Zugriff), die ein blindes Upgrade still brechen koennte. - mem0_service/smoke_test.py: prueft add/Suche/Auto-Lernen/Deutsch/Auto-Einordnung/Graph gegen eine WEGWERF-Collection (fasst /srv/models/mem0 nicht an). Vor/nach mem0-Upgrades laufen lassen. Live: alle Tests gruen. - docs/UPGRADE.md: sichere Upgrade-Prozedur (Backup -> Pin -> install -> smoke_test -> gruen=restart / rot=anpassen oder restore) + reagraph/React- und Hermes-Plugin-Hinweise. Co-Authored-By: Claude Opus 4.8 --- docs/UPGRADE.md | 35 +++++++++++++++++ mem0_service/requirements.txt | 13 +++++-- mem0_service/smoke_test.py | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 docs/UPGRADE.md create mode 100644 mem0_service/smoke_test.py diff --git a/docs/UPGRADE.md b/docs/UPGRADE.md new file mode 100644 index 0000000..7b8bc7e --- /dev/null +++ b/docs/UPGRADE.md @@ -0,0 +1,35 @@ +# Upgrades & Versions-Pins + +## Warum Pins +Der Mem0-Sidecar nutzt mem0-**Interna** (NoThink-LLM-Swap, Roh-Zugriff auf die Chroma-Collection +für den Graphen, Annahmen übers Result-Format). Ein unkontrolliertes mem0/chromadb-Upgrade kann das +**still brechen**. Darum sind die Versionen in `mem0_service/requirements.txt` gepinnt — Upgrades +passieren nur absichtlich. + +## Isolation (warum ein Upgrade nicht alles mitreißt) +- mem0 lebt in `~/.mem0/venv` (Python 3.12) — getrennt vom MC2-Backend (3.14). +- Die stabile Grenze nach außen ist **`/api/memory`**. UI, MCP (IDEs) und der Hermes-Provider + reden NUR damit, nie direkt mit mem0. Ein mem0-Upgrade betrifft also **nur den Sidecar**. + +## mem0 / chromadb sicher upgraden +```bash +cd ~/mission-control-v2 +bash deploy/backup.sh # 1. Backup +# 2. Pin anheben in mem0_service/requirements.txt (z.B. mem0ai==X.Y.Z) +uv pip install --python ~/.mem0/venv/bin/python -r mem0_service/requirements.txt # 3. installieren +~/.mem0/venv/bin/python mem0_service/smoke_test.py # 4. Smoke-Test (Wegwerf-Collection) +``` +- **Grün** → `systemctl --user restart mem0-service`, fertig. +- **Rot** → entweder den Sidecar (`mem0_service/app.py`) an die neue mem0-API anpassen, oder Pin + zurücksetzen und alten Stand per `deploy/restore.sh latest` wiederherstellen. + +## Weitere Upgrade-Fallen +- **Embed-Modell wechseln** = andere Vektor-Dimension → Chroma muss **neu indiziert** werden + (alle Fakten neu einbetten). Aktuell: Qwen3-Embedding-0.6B / 1024 Dim. +- **reagraph** ist auf **4.22.0** gepinnt — 4.23+ braucht `@react-three/fiber` v9 = React 19, + das Projekt ist React 18 (Crash „reading 'S'" sonst). +- **Nach Hermes-Upgrades**: prüfen, dass `~/.hermes/plugins/mc2-memory` noch lädt und `sync_turn` + feuert — die `MemoryProvider`-ABC könnte sich ändern (ein Turn über den Gateway, dann + `curl /api/memory` checken). +- **OS/Engine-Updates** (apt, llama.cpp) laufen separat über die UI (Wartungs-Drawer) und sind + von mem0 entkoppelt. diff --git a/mem0_service/requirements.txt b/mem0_service/requirements.txt index 08166eb..d4cc504 100644 --- a/mem0_service/requirements.txt +++ b/mem0_service/requirements.txt @@ -1,4 +1,9 @@ -# Zusatz-Abhängigkeiten für den Mem0-Sidecar im ~/.mem0/venv (Python 3.12). -# mem0ai + chromadb sind dort bereits installiert; hier nur der HTTP-Server. -fastapi>=0.115 -uvicorn[standard]>=0.30 +# Mem0-Sidecar-Deps im ~/.mem0/venv (Python 3.12). BEWUSST GEPINNT — Upgrades nur +# absichtlich, weil der Sidecar mem0-INTERNA nutzt (NoThink-LLM-Swap, Roh-Zugriff auf die +# Chroma-Collection für den Graphen, Annahmen übers Result-Format). Ein unkontrolliertes +# mem0/chromadb-Upgrade kann das still brechen. +# Upgrade-Prozedur (Backup → Pin anheben → smoke_test.py): siehe docs/UPGRADE.md. +mem0ai==2.0.8 +chromadb==1.5.9 +fastapi==0.138.1 +uvicorn[standard]==0.49.0 diff --git a/mem0_service/smoke_test.py b/mem0_service/smoke_test.py new file mode 100644 index 0000000..21d7532 --- /dev/null +++ b/mem0_service/smoke_test.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +""" +Smoke-Test für den Mem0-Sidecar — VOR und NACH jedem mem0/chromadb-Upgrade laufen lassen. +Prüft genau die Pfade, die mem0-INTERNA nutzen (LLM-Extraktion/NoThink, Auto-Einordnung, +Embeddings/Suche, Roh-Chroma-Graph). Nutzt eine WEGWERF-Collection unter /tmp — das echte +Gedächtnis (/srv/models/mem0) wird NICHT angefasst. + +Aufruf: ~/.mem0/venv/bin/python ~/mission-control-v2/mem0_service/smoke_test.py +Exit 0 = alles grün, sonst 1. +""" + +import os +import shutil +import sys +import tempfile +import time + +# WICHTIG: Env vor dem Import von app setzen (app liest die Pfade beim Modul-Load). +_TMP = tempfile.mkdtemp(prefix="mem0_smoke_") +os.environ["MEM0_CHROMA_PATH"] = os.path.join(_TMP, "chroma") +os.environ["MEM0_HISTORY_DB"] = os.path.join(_TMP, "history.db") +os.environ["MEM0_COLLECTION"] = f"smoke_{int(time.time())}" + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +import app # noqa: E402 + +_fails: list[str] = [] + + +def check(name: str, cond: bool, detail: str = "") -> None: + print(("PASS" if cond else "FAIL"), "—", name, ("" if cond else f":: {detail}")) + if not cond: + _fails.append(name) + + +def main() -> int: + app.mem() # Memory bauen (öffnet Chroma, pingt Modelle) + + # 1) Verbatim add + Rücklesen + r = app.add_memory(app.MemIn(content="Smoke: der Himmel ist blau.", category="knowledge", source="smoke")) + check("verbatim add + get", bool(r.get("id")) and "blau" in r.get("content", ""), str(r)[:160]) + + # 2) Embeddings / semantische Suche (Bedeutung, nicht Wort) + res = app.list_memory(q="Welche Farbe hat der Himmel?") + check("semantische Suche", any("blau" in i.get("content", "") for i in res), str(res)[:160]) + + # 3) Auto-Lernen: LLM-Extraktion (NoThink) + Deutsch erzwungen + lr = app.learn(app.LearnIn(text="Ich heiße Max und programmiere am liebsten in Go.", source="smoke")) + items = lr.get("results", []) + check("Auto-Lernen extrahiert Fakten", len(items) > 0, str(lr)[:160]) + joined = " ".join((i.get("content") or "") for i in items).lower() + check("Extraktion auf Deutsch", any(w in joined for w in ("heißt", "programmiert", "mag", "bevorzugt", "name", "nutzt")), joined[:160]) + + # 4) Auto-Einordnung in die 4 gültigen Kategorien + cats = {i.get("category") for i in items if i.get("category")} + check("Auto-Einordnung gültige Kategorie", bool(cats) and cats <= set(app.CATEGORIES), str(cats)) + + # 5) Graph aus den Roh-Embeddings (Chroma-Interna) + g = app.graph() + check("Graph liefert Knoten", len(g.get("nodes", [])) >= 1, str({k: len(v) for k, v in g.items()})) + + print("\n" + ("ALLE TESTS GRÜN ✓" if not _fails else f"FEHLGESCHLAGEN: {_fails}")) + return 0 if not _fails else 1 + + +if __name__ == "__main__": + try: + code = main() + finally: + shutil.rmtree(_TMP, ignore_errors=True) + sys.exit(code)