wip(referenz): Stand des abgebrochenen Laufs A2 gesichert (5 von 17 Dateien)
Ampel / ampel (push) Successful in 21s
Ampel / ampel (push) Successful in 21s
This commit is contained in:
+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.
|
||||
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
|
||||
# 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.
|
||||
# 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
|
||||
@@ -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("/")
|
||||
# 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
|
||||
# (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:
|
||||
"""Liest einen Schlüssel aus ~/.hermes/.env (Fallback, falls nicht in der Prozess-Env).
|
||||
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")
|
||||
|
||||
# --- 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("/")
|
||||
# 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
|
||||
|
||||
@@ -10,7 +10,7 @@ import os
|
||||
import subprocess
|
||||
|
||||
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 pydantic import BaseModel
|
||||
from services import backup as backup_svc
|
||||
@@ -42,13 +42,6 @@ def history(minutes: int = 60) -> dict:
|
||||
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:
|
||||
try:
|
||||
return httpx.get(f"{VOICE_SERVICE_URL}/health", timeout=2).status_code == 200
|
||||
|
||||
@@ -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
|
||||
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.
|
||||
|
||||
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")
|
||||
def voice_trace(limit: int = 20) -> dict:
|
||||
"""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."""
|
||||
return {"turns": get_trace(limit)}
|
||||
|
||||
@@ -289,7 +289,7 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
||||
headers["X-Hermes-Session-Key"] = body.session_key
|
||||
|
||||
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.
|
||||
trace = TurnTrace(session_id=body.session_id, kind="voice")
|
||||
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}
|
||||
# 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).
|
||||
# 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"):
|
||||
payload["chat_template_kwargs"] = {"enable_thinking": False}
|
||||
try:
|
||||
@@ -342,7 +342,7 @@ async def voice_chat(body: ChatIn) -> StreamingResponse:
|
||||
if first: # Time-To-First-Byte des Hermes-Streams (Verbindungs-Overhead)
|
||||
trace.note_ttfb()
|
||||
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.
|
||||
if first_content and b'"content"' in chunk:
|
||||
trace.note_first_content()
|
||||
|
||||
@@ -33,7 +33,7 @@ def list_snapshots() -> dict:
|
||||
|
||||
@router.get("/zeitmaschine/inhalt")
|
||||
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):
|
||||
raise HTTPException(400, "Ungültiger Snapshot-Name.")
|
||||
p = backup_svc.BACKUP_DIR / file
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
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
|
||||
(services/announce.py → Lucy spricht es) und via notify.sh (Telegram).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user