05bef8642d
Paket A des Plans. Ersetzt die flache SQLite-Fakten-DB durch Mem0 (LLM-Auto-
Extraktion + Vektor/Chroma-Suche). Architektur erzwungen durch Python-Split:
MC2-Backend laeuft auf 3.14 (kann mem0 nicht importieren), mem0+chromadb nur
auf 3.12 (~/.mem0/venv) -> Mem0-Sidecar (FastAPI, localhost:8765), MC2 spricht
ihn per HTTP. /api/memory-Form bleibt unveraendert (UI + MCP kompatibel).
- mem0_service/: Sidecar (app.py), Migration (migrate.py), deps.
- Embeddings: neue llama-swap embed-Rolle (Qwen3-Embedding-0.6B, 1024 Dim,
pooling last) ueber /v1/embeddings.
- LLM-Extraktion: lokales fast-Hirn; NoThinkLLM schaltet Qwen3-Thinking ab
(sonst bricht json_object-Extraktion ab), custom_instructions halten Deutsch.
- backend/services/memory.py: duenner HTTP-Client auf den Sidecar (semantische
Suche mit score, verbatim add, learn()). Router: /api/memory/learn.
- mcp/mcp_memory.py: neues learn-Tool (Auto-Lernen aus Gespraechs-Turns),
search jetzt semantisch.
- Frontend: Relevanz-Score + Auto/Manuell-Herkunft im Gedaechtnis-Tab.
- deploy/: mem0-service.service + deploy.sh (uv-Install, Migration, Restart).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
76 lines
2.0 KiB
Python
76 lines
2.0 KiB
Python
"""Memory-Endpoints (geteiltes Gedächtnis). LAN-only, kein Token in 2.0-Phase 3."""
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from services import memory
|
|
|
|
router = APIRouter(prefix="/api")
|
|
|
|
|
|
class MemIn(BaseModel):
|
|
content: str
|
|
category: str = "stable"
|
|
source: str = "manual"
|
|
|
|
|
|
class MemUp(BaseModel):
|
|
content: str | None = None
|
|
category: str | None = None
|
|
|
|
|
|
class DedupeIn(BaseModel):
|
|
apply: bool = False
|
|
threshold: float = 0.85
|
|
|
|
|
|
class LearnIn(BaseModel):
|
|
text: str | None = None
|
|
messages: list[dict] | None = None
|
|
source: str = "auto"
|
|
category: str = "stable"
|
|
|
|
|
|
@router.get("/memory/export")
|
|
def export() -> dict:
|
|
return memory.export_text()
|
|
|
|
|
|
@router.post("/memory/learn", status_code=201)
|
|
def learn(body: LearnIn) -> dict:
|
|
"""Auto-Lernen: Gesprächs-Turns/Text durchreichen → Mem0 extrahiert Fakten selbst."""
|
|
return memory.learn(text=body.text, messages=body.messages,
|
|
source=body.source, category=body.category)
|
|
|
|
|
|
@router.post("/memory/dedupe")
|
|
def dedupe(body: DedupeIn) -> dict:
|
|
return memory.dedupe(apply=body.apply, threshold=body.threshold)
|
|
|
|
|
|
@router.get("/memory")
|
|
def list_mem(q: str = "", category: str = "") -> list[dict]:
|
|
return memory.list_memories(q=q, category=category)
|
|
|
|
|
|
@router.post("/memory", status_code=201)
|
|
def add(body: MemIn) -> dict:
|
|
if body.category not in memory.CATEGORIES:
|
|
raise HTTPException(400, f"Kategorie '{body.category}' unbekannt.")
|
|
return memory.add_memory(body.content, body.category, body.source)
|
|
|
|
|
|
@router.put("/memory/{mid}")
|
|
def update(mid: str, body: MemUp) -> dict:
|
|
res = memory.update_memory(mid, content=body.content, category=body.category)
|
|
if not res:
|
|
raise HTTPException(404, "Eintrag nicht gefunden")
|
|
return res
|
|
|
|
|
|
@router.delete("/memory/{mid}")
|
|
def delete(mid: str) -> dict:
|
|
if not memory.delete_memory(mid):
|
|
raise HTTPException(404, "Eintrag nicht gefunden")
|
|
return {"ok": True}
|