MC2 Final (D16 f): Voice-Metrik-Reste raus + README auf Final-Stand (C15)

f) Lucy/Voice-Reste aus dem MC2-Web-UI entfernt (kein "Sprechen"-Tab mehr):
   VoiceLatencyCard + Sprach-Latenz-Plumbing (queries/api-Typen) und der
   Backend-Leseendpoint /voice/metrics raus. Der Proaktivität-/Alarm-Kanal
   (/voice/announce, /alarm) und die STT/TTS/chat-Proxys bleiben unberührt.
C15) README auf den Final-/Autonomie-Stand aktualisiert (Mem0 statt SQLite-MCP,
   live+eingefroren, Lucy als eigenes Repo, Fangnetz-Updates/Werkstatt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 20:23:13 +02:00
parent 8a2db70f3c
commit 494037dee5
11 changed files with 174 additions and 280 deletions
@@ -1,81 +0,0 @@
import { Gauge } from "lucide-react"
import { useVoiceMetrics } from "@/lib/queries"
import type { VoiceStageStat } from "@/lib/api"
// Stabile Reihenfolge + deutsche Labels, spiegelt STAGES in backend/services/voice_metrics.py.
const STAGES: { key: string; label: string; hint: string }[] = [
{ key: "stt", label: "STT", hint: "Sprache → Text" },
{ key: "vision", label: "Bildschirm-Sicht", hint: "Vision-Beschreibung" },
{ key: "chat_ttfb", label: "Chat-TTFB", hint: "Zeit bis 1. Token" },
{ key: "tts", label: "TTS", hint: "Text → Sprache" },
]
function fmtMs(ms?: number): string {
if (ms == null) return "—"
return ms >= 1000 ? `${(ms / 1000).toFixed(2)} s` : `${Math.round(ms)} ms`
}
// p95 relativ zum langsamsten p95 aller Stufen → grobe Balkenlänge.
function StageRow({ stat, label, hint, maxP95 }: { stat?: VoiceStageStat; label: string; hint: string; maxP95: number }) {
const has = !!stat && stat.count > 0
const pct = has && stat!.p95_ms && maxP95 > 0 ? Math.max(4, Math.min(100, (stat!.p95_ms! / maxP95) * 100)) : 0
return (
<div className="space-y-1.5">
<div className="flex items-baseline justify-between gap-2">
<div className="flex items-baseline gap-2 min-w-0">
<span className="text-xs font-semibold text-foreground">{label}</span>
<span className="truncate text-[10px] text-muted-foreground/60">{hint}</span>
</div>
{has ? (
<span className="shrink-0 font-mono text-sm font-bold tabular-nums text-foreground">{fmtMs(stat!.p50_ms)}</span>
) : (
<span className="shrink-0 text-[10px] italic text-muted-foreground/50">noch keine Messungen</span>
)}
</div>
<div className="h-1.5 w-full overflow-hidden rounded-full bg-background/50 border border-border/30">
<div
className="h-full rounded-full bg-gradient-to-r from-teal-500 to-indigo-500 transition-all duration-500"
style={{ width: `${pct}%` }}
/>
</div>
{has && (
<div className="flex items-center gap-3 font-mono text-[10px] text-muted-foreground/65">
<span>p50 {fmtMs(stat!.p50_ms)}</span>
<span>p95 {fmtMs(stat!.p95_ms)}</span>
<span>zuletzt {fmtMs(stat!.last_ms)}</span>
<span className="text-muted-foreground/45">· n={stat!.count}</span>
</div>
)}
</div>
)
}
export function VoiceLatencyCard() {
const { data: metrics } = useVoiceMetrics(5_000)
const maxP95 = Math.max(1, ...STAGES.map((s) => metrics?.[s.key]?.p95_ms ?? 0))
const anyData = STAGES.some((s) => (metrics?.[s.key]?.count ?? 0) > 0)
return (
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/15">
<div className="mb-4 flex items-center gap-2">
<Gauge className="h-4.5 w-4.5 text-primary" />
<h2 className="text-sm font-semibold uppercase tracking-wide text-foreground">Sprach-Latenz</h2>
<span className="ml-1 inline-flex items-center gap-1 text-[10px] font-medium text-muted-foreground/70">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-500 animate-pulse" /> live
</span>
</div>
<div className="grid gap-4 sm:grid-cols-2">
{STAGES.map((s) => (
<StageRow key={s.key} stat={metrics?.[s.key]} label={s.label} hint={s.hint} maxP95={maxP95} />
))}
</div>
<div className="mt-4 border-t border-border/30 pt-2 text-[10px] text-muted-foreground/70">
{anyData
? "Server-seitige Dauer je Pipeline-Stufe (p50 prominent). Rollender Schnitt über die letzten Turns; Reset bei Neustart."
: "Noch keine Voice-Turns gemessen — sprich einmal über den „Sprechen“-Tab, dann erscheinen hier STT/Vision/Chat/TTS."}
</div>
</div>
)
}
-11
View File
@@ -225,17 +225,6 @@ export const getRoutingPolicy = () => api<RoutingPolicyMeta>("/api/routing/polic
export const updateRoutingPolicy = (patch: Partial<RoutingPolicy>) =>
api<{ policy: RoutingPolicy }>("/api/routing/policy", { method: "PUT", body: JSON.stringify(patch) })
// Per-Stage-Latenz der Voice/Lucy-Pipeline (GET /api/voice/metrics, C2).
// Schlüssel = Stufe (stt|vision|chat_ttfb|tts), Wert = rollende Statistik.
export interface VoiceStageStat {
count: number
avg_ms?: number
p50_ms?: number
p95_ms?: number
last_ms?: number
}
export type VoiceMetrics = Record<string, VoiceStageStat>
export interface GitInfo {
hash: string
date: string
-5
View File
@@ -23,7 +23,6 @@ import {
type SystemStatus,
type TokenStats,
type UpdatesResp,
type VoiceMetrics,
} from "./api"
// Zentrale Query-Keys (eine Quelle der Wahrheit für invalidate).
@@ -35,7 +34,6 @@ export const qk = {
groups: ["groups"] as const,
routing: ["routing"] as const,
routingPolicy: ["routing-policy"] as const,
voiceMetrics: ["voice-metrics"] as const,
jobs: ["jobs"] as const,
tokenStats: ["token-stats"] as const,
agentStatus: ["agent-status"] as const,
@@ -74,9 +72,6 @@ export const useGroups = (refetchInterval = 8_000) =>
export const useRouting = (refetchInterval = 4_000) =>
useQuery({ queryKey: qk.routing, queryFn: () => api<RoutingResp>("/api/routing"), refetchInterval })
export const useVoiceMetrics = (refetchInterval = 5_000) =>
useQuery({ queryKey: qk.voiceMetrics, queryFn: () => api<VoiceMetrics>("/api/voice/metrics"), refetchInterval })
export const useRoutingPolicy = () =>
useQuery({ queryKey: qk.routingPolicy, queryFn: () => api<RoutingPolicyMeta>("/api/routing/policy") })
-2
View File
@@ -2,7 +2,6 @@ import { ArrowUpCircle, ChevronRight, Check, Cpu, Code } from "lucide-react"
import { LucyHealthCard } from "@/components/dashboard/LucyHealthCard"
import { SystemStatusCard } from "@/components/dashboard/SystemStatusCard"
import { TokenPerformanceCard } from "@/components/dashboard/TokenPerformanceCard"
import { VoiceLatencyCard } from "@/components/dashboard/VoiceLatencyCard"
import { useUpdates, useModels } from "@/lib/queries"
import { cn } from "@/lib/utils"
@@ -89,7 +88,6 @@ export function Mc3Start() {
<SystemStatusCard />
<TokenPerformanceCard />
</div>
<VoiceLatencyCard />
</div>
)
}
+1 -5
View File
@@ -4,7 +4,6 @@ import { AgentStatusCard } from "@/components/dashboard/AgentStatusCard"
import { MemoryInputCard } from "@/components/dashboard/MemoryInputCard"
import { TokenPerformanceCard } from "@/components/dashboard/TokenPerformanceCard"
import { ServicesCard } from "@/components/dashboard/ServicesCard"
import { VoiceLatencyCard } from "@/components/dashboard/VoiceLatencyCard"
import { ModelsCard } from "@/components/dashboard/ModelsCard"
import { LucyHealthCard } from "@/components/dashboard/LucyHealthCard"
@@ -35,10 +34,7 @@ export function DashboardView() {
{/* Stack & Telemetrie */}
<section>
<ZoneLabel>Stack &amp; Telemetrie</ZoneLabel>
<div className="grid gap-6 md:grid-cols-2">
<ServicesCard />
<VoiceLatencyCard />
</div>
<ServicesCard />
</section>
{/* Betrieb & Wissen */}