MC2 Final (D16 c+e): System-Logs mit Fehler-Farben/Filter + Auto-Dedupe
c) System-Logs ausgebaut: neue LogConsole-Komponente färbt Fehler (rot) und Warnungen (amber) ein, "Nur Probleme"-Filter mit Zähler, Zeilen-Suche; voice-service in die Dienst-Liste aufgenommen (war nur backend-seitig). e) Mem0-Dubletten automatisch: deterministischer Auto-Dedupe-Loop (täglich, apply=True, Schwelle 0.9 > manueller 0.85 da ohne Review) als Backend- Hintergrund-Task — kein Memory-Bloat mehr ohne Zutun. Knopf bleibt on-demand. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+5
-1
@@ -19,7 +19,7 @@ from starlette.requests import Request
|
||||
|
||||
from config import FRONTEND_DIST, VERSION
|
||||
from routers import agent, connect, gateway_proxy, health, maintenance, memory, models, reminders as reminders_router, routing, system, voice
|
||||
from services import reminders, sentry, warmer
|
||||
from services import memory as memory_svc, reminders, sentry, warmer
|
||||
|
||||
# Zentrales Logging — Level via MC_LOG_LEVEL (INFO default). Eine Konfiguration
|
||||
# für alle Module (logging.getLogger(__name__)).
|
||||
@@ -40,6 +40,10 @@ async def lifespan(app: FastAPI):
|
||||
if sentry.ENABLED:
|
||||
tasks.append(asyncio.create_task(sentry.sentry_loop()))
|
||||
tasks.append(asyncio.create_task(reminders.reminders_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)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
|
||||
@@ -12,6 +12,9 @@ UI und MCP-Server kompatibel bleiben. Neu gegenüber der alten flachen SQLite:
|
||||
5 Kategorien (user · instruction · stable · versioned · ephemeral) bleiben als Metadaten erhalten.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
@@ -19,6 +22,8 @@ import httpx
|
||||
|
||||
from config import MEM0_SERVICE_URL
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CATEGORIES = ("identity", "knowledge", "rules", "events")
|
||||
|
||||
_TIMEOUT = httpx.Timeout(60.0, connect=5.0) # LLM-Extraktion kann ein paar Sekunden dauern
|
||||
@@ -145,3 +150,28 @@ def dedupe(apply: bool = False, threshold: float = 0.85) -> dict:
|
||||
if delete_memory(d["id"]):
|
||||
removed += 1
|
||||
return {"groups": groups, "duplicate_count": dup_count, "removed": removed, "applied": apply}
|
||||
|
||||
|
||||
# ── Auto-Dedupe (D16e): Dubletten von selbst wegräumen, kein Knopf-Zwang ─────────────
|
||||
# Deterministisch in UNSERER Schicht (wie reminders, Verdikt 11c) statt am Hermes-cron —
|
||||
# läuft als Hintergrund-Task. Höhere Schwelle als der manuelle Knopf (0.9 statt 0.85), weil
|
||||
# OHNE Mensch-Review angewandt wird: lieber eine Dublette stehen lassen als etwas Distinktes
|
||||
# löschen. Mem0 dedupliziert beim Auto-Lernen schon semantisch — das hier fängt den Rest
|
||||
# (Verbatim-Importe, wiederholte Fakten) ab, damit das Gedächtnis nicht ohne Zutun aufbläht.
|
||||
AUTO_DEDUPE_ENABLED = os.environ.get("MC_MEM_DEDUPE_ENABLED", "1") != "0"
|
||||
AUTO_DEDUPE_INTERVAL = int(os.environ.get("MC_MEM_DEDUPE_INTERVAL", str(24 * 3600))) # täglich
|
||||
AUTO_DEDUPE_START_DELAY = int(os.environ.get("MC_MEM_DEDUPE_START_DELAY", "300")) # 5 min nach Start
|
||||
AUTO_DEDUPE_THRESHOLD = float(os.environ.get("MC_MEM_DEDUPE_THRESHOLD", "0.9"))
|
||||
|
||||
|
||||
async def auto_dedupe_loop() -> None:
|
||||
"""Hintergrund-Task: räumt periodisch Gedächtnis-Dubletten weg (apply=True)."""
|
||||
await asyncio.sleep(AUTO_DEDUPE_START_DELAY) # Mem0-Sidecar nach Start hochkommen lassen
|
||||
while True:
|
||||
try:
|
||||
res = await asyncio.to_thread(dedupe, True, AUTO_DEDUPE_THRESHOLD) # sync HTTP → Thread
|
||||
if res.get("removed"):
|
||||
log.info("mem-dedupe: %d Dublette(n) automatisch entfernt", res["removed"])
|
||||
except Exception:
|
||||
log.debug("mem-dedupe: Lauf fehlgeschlagen", exc_info=True)
|
||||
await asyncio.sleep(AUTO_DEDUPE_INTERVAL)
|
||||
|
||||
Reference in New Issue
Block a user