Observability 1a: Per-Turn-Latenz-Trace + Latenz-Karte

voice_metrics.py: neben den rollenden Stats jetzt ein echter Per-Turn-Trace
(TurnTrace + Ringpuffer der letzten 60 Turns). Balken-Stufen zeitlich disjunkt
(stt · vision · hirn · gen); hirn misst ab mark_brain_start() VOR dem Hermes-
Request, damit die Vision-Zeit nicht doppelt gezaehlt wird. STT (davor) und
Mem0-Retrieve (Rueckruf waehrend) werden per park()/_take_* best-effort dem Turn
zugeordnet (Ein-Nutzer-Geraet, kein Turn-ID noetig). Mem0 = Unter-Detail INNERHALB
hirn (nicht addieren) -> ehrlich, kein Doppelzaehlen.

voice.py: TurnTrace in voice_chat's gen() (commit garantiert 1x via finally, auch
bei Fehler/Abbruch). STT-Endpoint parkt seine Dauer. NEU: GET /api/voice/metrics
(schliesst die Luecke - selbstkritik-feed.sh curlte das, existierte nie -> 404) +
GET /api/voice/trace?limit=N.

memory.py: GET /api/memory?q=... (= Hermes' Mem0-Prefetch) misst + parkt die
Retrieve-Zeit.

Frontend: LatencyCard (gestapelte Balken je Turn, "Taeter" = groesste Stufe,
Fehler-Turns rot, Tooltip mit "davon Mem0 X s"), Query useVoiceTrace, in der
Zentrale unter "Stack & Telemetrie". Lokal gegen Seed-Server verifiziert: 30,3-s-
Haenger -> Hirn-Balken 94% + "davon Mem0 26,1 s".

Grenzen (ehrlich, als Fussnote in der Karte): Tool-Runden im Hermes-LLM-Loop haben
keinen Callback an MC2 -> stecken in "Antwort". Reine Telegram-Text-Turns laufen an
MC2 vorbei und erscheinen hier nicht.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-04 13:21:52 +02:00
parent 58021331e1
commit 78448220b2
12 changed files with 552 additions and 207 deletions
+12
View File
@@ -1,9 +1,12 @@
"""Memory-Endpoints (geteiltes Gedächtnis). LAN-only, kein Token in 2.0-Phase 3."""
import time
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from services import memory
from services.voice_metrics import park, record_stage
router = APIRouter(prefix="/api")
@@ -56,6 +59,15 @@ def dedupe(body: DedupeIn) -> dict:
@router.get("/memory")
def list_mem(q: str = "", category: str = "") -> list[dict]:
# Semantischer Retrieve (q gesetzt) = u.a. Hermes' Mem0-Prefetch VOR jedem Turn. Dauer messen
# + parken, damit der laufende Voice-Chat-Turn sie als Unter-Detail seiner Hirn-Zeit einsammelt.
if q:
_t0 = time.perf_counter()
res = memory.list_memories(q=q, category=category)
_ms = (time.perf_counter() - _t0) * 1000.0
record_stage("memory_retrieve", _ms)
park("retrieve", _ms)
return res
return memory.list_memories(q=q, category=category)