profileShare

rasmusjy / roundtable

Read-only snapshot

No repository description.

main default branch 181 files Expires Sep 13, 2026, 9:06 AM
cost-breakdown.tsx 2,944 bytes
1 'use client';
2
3 import { Bar, BarChart, Cell, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
4 import type { DebateView } from '@/lib/debate-view';
5 import { displayNameForModel } from '@/core/types';
6 import { participantColor } from '@/lib/model-visuals';
7 import { formatUsd } from '@/lib/utils';
8
9 /** Post-debate cost breakdown per model (in micro-dollars for readability). */
10 export function CostBreakdown({ view }: { view: DebateView }) {
11 const modelIndex = new Map(view.participants.map((p, i) => [p.model, i]));
12 const data = Object.entries(view.totals.costByModel)
13 .map(([model, cost]) => ({
14 model,
15 name: displayNameForModel(model),
16 cost,
17 index: modelIndex.get(model) ?? 5,
18 }))
19 .sort((a, b) => b.cost - a.cost);
20
21 if (data.length === 0) {
22 return <p className="text-sm text-muted-foreground">No cost recorded yet.</p>;
23 }
24
25 return (
26 <div className="space-y-4">
27 <div className="grid grid-cols-2 gap-3 sm:grid-cols-4">
28 <Stat label="Total cost" value={formatUsd(view.totals.costUsd)} />
29 <Stat label="Prompt tokens" value={view.totals.promptTokens.toLocaleString()} />
30 <Stat label="Completion tokens" value={view.totals.completionTokens.toLocaleString()} />
31 <Stat label="Models" value={String(data.length)} />
32 </div>
33
34 <div className="h-56 w-full">
35 <ResponsiveContainer width="100%" height="100%">
36 <BarChart data={data} layout="vertical" margin={{ left: 8, right: 16 }}>
37 <XAxis
38 type="number"
39 tickFormatter={(v: number) => formatUsd(v)}
40 tick={{ fontSize: 11, fill: 'hsl(var(--muted-foreground))' }}
41 stroke="hsl(var(--border))"
42 />
43 <YAxis
44 type="category"
45 dataKey="name"
46 width={110}
47 tick={{ fontSize: 11, fill: 'hsl(var(--muted-foreground))' }}
48 stroke="hsl(var(--border))"
49 />
50 <Tooltip
51 cursor={{ fill: 'hsl(var(--muted) / 0.4)' }}
52 contentStyle={{
53 background: 'hsl(var(--popover))',
54 border: '1px solid hsl(var(--border))',
55 borderRadius: 8,
56 fontSize: 12,
57 }}
58 formatter={(v: number) => [formatUsd(v), 'Cost']}
59 />
60 <Bar dataKey="cost" radius={[0, 4, 4, 0]}>
61 {data.map((d) => (
62 <Cell key={d.model} fill={participantColor(d.index)} />
63 ))}
64 </Bar>
65 </BarChart>
66 </ResponsiveContainer>
67 </div>
68 </div>
69 );
70 }
71
72 function Stat({ label, value }: { label: string; value: string }) {
73 return (
74 <div className="rounded-lg border bg-card p-3">
75 <div className="text-[11px] uppercase tracking-wide text-muted-foreground">{label}</div>
76 <div className="mt-0.5 text-lg font-semibold">{value}</div>
77 </div>
78 );
79 }
80