"use client"; import { useMemo, useState } from "react"; import { RotateCcw, Save } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge, Spinner } from "@/components/ui/primitives"; import { cn } from "@/lib/utils"; import { hasBench, ms, pct, usd, type ModelRow, type RouteDef } from "./format"; const EFFORTS = ["low", "medium", "high"] as const; export function RoutingTable({ routes, defaults, labels, models, onSave, saving }: { routes: Record; defaults: Record; labels: Record; models: ModelRow[]; onSave: (r: Record) => Promise; saving: boolean }) { const [draft, setDraft] = useState>(routes); const dirty = JSON.stringify(draft) !== JSON.stringify(routes); const options = useMemo(() => models.filter((m) => m.available && m.category !== "embedding" && m.probes?.text?.status === "ok").sort((a, b) => a.provider.localeCompare(b.provider) || ((b.benchmark?.mean_score ?? -1) - (a.benchmark?.mean_score ?? -1)) || a.id.localeCompare(b.id)), [models]); const byId = useMemo(() => new Map(models.map((m) => [m.id, m])), [models]); const set = (task: string, patch: Partial) => setDraft((d) => ({ ...d, [task]: { ...d[task], ...patch } })); const Picker = ({ value, onChange, task }: { value: string; onChange: (v: string) => void; task: string }) => ( ); const why = (id: string, cats: string[]) => { const m = byId.get(id); if (!m || !hasBench(m)) return untested; const by = m.benchmark!.by_category ?? {}; const s = cats.map((c) => by[c]?.score).filter((x): x is number => typeof x === "number"); const mean = s.length ? s.reduce((a, b) => a + b, 0) / s.length : null; return {mean != null ? `${pct(mean)} on ${cats.length} cat.` : pct(m.benchmark!.pass_rate)} · {ms(m.benchmark!.total_median_ms)} · {usd(m.benchmark!.cost_mean)}; }; return (
{["Task class", "Primary", "Fallback", "Effort", "Measured basis (primary)", "Categories"].map((h) => )}{Object.keys(defaults).map((task) => { const r = draft[task] ?? defaults[task]; const changed = JSON.stringify(r) !== JSON.stringify(defaults[task]); return ( ); })}
{h}
{labels[task] ?? task}
{task}{changed && custom}
set(task, { primary: v })} /> set(task, { fallback: v })} />
{EFFORTS.map((e) => )}
{why(r.primary, r.categories ?? [])}
{(r.categories ?? []).map((c) => {c})}{r.avoid?.length ? avoid {r.avoid.length} : null}
Auto routing picks the primary when it is available and probes.text = ok, else the fallback, else the best measured model for the class. Saved to setting ai.routes.
); }