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:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user