Feat: Frontend-Overhaul E — Lane-Editor, Routing-Policy (hot-reload) & Latenz-Karte

Teil 1: VoiceLatencyCard auf dem Dashboard (GET /api/voice/metrics, C2) —
zeigt STT/Vision/Chat-TTFB/TTS mit p50/p95/last + count.

Teil 2: UI-editierbare Routing-Policy. Neuer routing_policy.py (hot-reload JSON
unter MODELS_DIR/mc2-routing.json, Env=Defaults, atomarer Write, Validierung).
router_logic, gateway_proxy und gateway.routing_summary lesen jetzt live via
load_policy(); routing_summary ist lane-bewusst (chat/coding statt altem auto).
Neue Endpoints GET/PUT /api/routing/policy.

Teil 3: LaneEditor.tsx als ZONE im Cockpit (chat/coding-Aliase + Schwellen +
fast_no_think, Speichern/Default-je-Feld); Gateway-Node zeigt die Lanes.

Verifiziert: npm run build (tsc strict) clean, FastAPI TestClient (GET/PUT,
Validierung, Persistenz, Hot-reload durch die API), venv-Smoke (Routing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-06-29 18:58:17 +02:00
parent 3611defe5d
commit 9e432dbd2d
15 changed files with 876 additions and 367 deletions
+7
View File
@@ -6,6 +6,7 @@ import { RolesCard } from "@/components/dashboard/RolesCard"
import { MemoryInputCard } from "@/components/dashboard/MemoryInputCard"
import { TokenPerformanceCard } from "@/components/dashboard/TokenPerformanceCard"
import { ServicesCard } from "@/components/dashboard/ServicesCard"
import { VoiceLatencyCard } from "@/components/dashboard/VoiceLatencyCard"
function ZoneLabel({ children }: { children: React.ReactNode }) {
return <p className="mb-2 ml-0.5 text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">{children}</p>
@@ -37,6 +38,12 @@ export function DashboardView() {
</div>
</section>
{/* Sprach-Latenz (Voice/Lucy-Pipeline, C2) */}
<section>
<ZoneLabel>Sprach-Latenz</ZoneLabel>
<VoiceLatencyCard />
</section>
{/* Betrieb & Wissen */}
<section>
<ZoneLabel>Betrieb &amp; Wissen</ZoneLabel>
+16 -7
View File
@@ -8,6 +8,7 @@ import { cn } from "@/lib/utils"
import { fmtSize, fmtCtx } from "@/lib/format"
import { getBrandInfo, ROLES, roleTone } from "@/components/models/ModelBadges"
import { SpecDraftModal } from "@/components/models/SpecDraftModal"
import { LaneEditor } from "@/components/models/LaneEditor"
export function Cockpit() {
const qc = useQueryClient()
@@ -532,17 +533,22 @@ export function Cockpit() {
<span>Continue</span>
</div>
{/* COLUMN 2: Central Gateway Node */}
<div
className="absolute select-none z-10 w-36 py-3 px-4 -translate-y-1/2 -translate-x-1/2 rounded-2xl border border-primary/50 bg-card/90 backdrop-blur-md flex flex-col items-center justify-center text-center shadow-lg shadow-primary/5"
{/* COLUMN 2: Central Gateway Node — virtuelle Lanes (Router pickt das echte Modell) */}
<div
className="absolute select-none z-10 w-40 py-3 px-4 -translate-y-1/2 -translate-x-1/2 rounded-2xl border border-primary/50 bg-card/90 backdrop-blur-md flex flex-col items-center justify-center text-center shadow-lg shadow-primary/5"
style={{ left: "50%", top: "50%" }}
>
<div className="text-[10px] uppercase font-bold text-primary font-space tracking-wide">Gateway Auto</div>
<div className="text-[10px] font-mono text-muted-foreground mt-0.5">
Schwelle: &gt; {routing?.heavy_threshold_chars ? (routing.heavy_threshold_chars / 1000) : "4"}k Zeichen
<div className="text-[10px] uppercase font-bold text-primary font-space tracking-wide">Gateway · Lanes</div>
<div className="mt-1 flex flex-col gap-0.5 text-[9px] font-mono text-muted-foreground">
{routing?.lanes?.length ? routing.lanes.map((l) => (
<span key={l.name}>
<span className="text-foreground font-semibold">{l.name}</span>
{l.threshold_chars ? ` ${(l.threshold_chars / 1000).toFixed(0)}k` : l.escalate_chars ? `${(l.escalate_chars / 1000).toFixed(0)}k` : ""}
</span>
)) : <span>chat · coding</span>}
</div>
<div className="mt-1 px-1.5 py-0.5 rounded bg-primary/10 border border-primary/20 text-[9px] font-semibold text-primary uppercase font-mono">
Auto-Swap
Router
</div>
</div>
@@ -696,6 +702,9 @@ export function Cockpit() {
</div>
</div>
{/* ZONE B.4: Lane-Routing & Policy (UI-editierbar, hot-reload) */}
<LaneEditor />
{/* ZONE B.5: Slot-Belegung */}
<div className="rounded-2xl border border-border/60 bg-card/45 backdrop-blur-md p-5 shadow-lg shadow-black/10 space-y-3.5">
<div className="text-xs font-bold uppercase tracking-wider text-muted-foreground px-1">