import { useMemo } from 'react';
import {
ResponsiveContainer,
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
Legend,
} from 'recharts';
import type { MetricSnapshot, VariantFilter } from '../../lib/types';
interface TokenPanelProps {
data: MetricSnapshot[];
variant: VariantFilter;
height?: number;
}
interface ChartDataPoint {
timestamp: number;
time: string;
// Single-variant keys
token_rate_in?: number;
token_rate_out?: number;
token_rate_cache_read?: number;
token_rate_cache_write?: number;
// Both-variant keys (prod)
token_rate_in_prod?: number;
token_rate_out_prod?: number;
token_rate_cache_read_prod?: number;
token_rate_cache_write_prod?: number;
// Both-variant keys (canary)
token_rate_in_canary?: number;
token_rate_out_canary?: number;
token_rate_cache_read_canary?: number;
token_rate_cache_write_canary?: number;
}
const COLORS = {
input: '#06b6d4', // cyan
output: '#8b5cf6', // purple
cache_read: '#f59e0b', // amber
cache_write: '#10b981', // emerald
};
function formatTokenCount(n: number): string {
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}k`;
return n.toFixed(0);
}
const CustomTooltip = ({
active,
payload,
label,
}: {
active?: boolean;
payload?: Array<{ name: string; value: number; color: string }>;
label?: string;
}) => {
if (!active || !payload) return null;
return (
{label}
{payload.map((entry, i) => (
{entry.name}: {entry.value.toFixed(0)} tok/s
))}
);
};
/** Compute running totals over the visible window from cumulative counter values. */
function windowTotal(data: MetricSnapshot[], field: keyof MetricSnapshot): number {
if (data.length < 2) return 0;
const first = data[0][field] as number;
const last = data[data.length - 1][field] as number;
const delta = last - first;
return delta >= 0 ? delta : last; // guard against counter resets
}
export function TokenPanel({ data, variant, height = 180 }: TokenPanelProps) {
// Running totals over the visible window
const totals = useMemo(() => {
const filtered = variant === 'both' ? data : data.filter((s) => s.variant === variant);
return {
input: windowTotal(filtered, 'tokens_input'),
output: windowTotal(filtered, 'tokens_output'),
cache_read: windowTotal(filtered, 'tokens_cache_read'),
cache_write: windowTotal(filtered, 'tokens_cache_write'),
};
}, [data, variant]);
// Duration label for the summary (window span in hours)
const windowHours = useMemo(() => {
if (data.length < 2) return null;
const spanMs = data[data.length - 1].timestamp - data[0].timestamp;
const hrs = spanMs / 3_600_000;
return hrs >= 1 ? `${hrs.toFixed(0)}h` : `${Math.round(hrs * 60)}m`;
}, [data]);
const chartData = useMemo(() => {
if (variant === 'both') {
const grouped = new Map();
for (const s of data) {
const pt = grouped.get(s.timestamp) ?? {
timestamp: s.timestamp,
time: new Date(s.timestamp).toLocaleTimeString('en-US', {
hour: '2-digit', minute: '2-digit', hour12: false,
}),
};
if (s.variant === 'production') {
pt.token_rate_in_prod = s.token_rate_in;
pt.token_rate_out_prod = s.token_rate_out;
pt.token_rate_cache_read_prod = s.token_rate_cache_read;
pt.token_rate_cache_write_prod = s.token_rate_cache_write;
} else {
pt.token_rate_in_canary = s.token_rate_in;
pt.token_rate_out_canary = s.token_rate_out;
pt.token_rate_cache_read_canary = s.token_rate_cache_read;
pt.token_rate_cache_write_canary = s.token_rate_cache_write;
}
grouped.set(s.timestamp, pt);
}
return Array.from(grouped.values()).sort((a, b) => a.timestamp - b.timestamp);
}
return data.map((s) => ({
timestamp: s.timestamp,
time: new Date(s.timestamp).toLocaleTimeString('en-US', {
hour: '2-digit', minute: '2-digit', hour12: false,
}),
token_rate_in: s.token_rate_in,
token_rate_out: s.token_rate_out,
token_rate_cache_read: s.token_rate_cache_read,
token_rate_cache_write: s.token_rate_cache_write,
}));
}, [data, variant]);
const yAxisFormatter = (v: number) =>
v >= 1000 ? `${(v / 1000).toFixed(1)}k` : v.toFixed(0);
return (
{/* Header: title + running totals summary */}
Token Throughput
{windowHours && (
{windowHours} window
)}
{/* Running totals grid */}
Input
{formatTokenCount(totals.input)}
Output
{formatTokenCount(totals.output)}
Cache↑
{formatTokenCount(totals.cache_read)}
Cache↓
{formatTokenCount(totals.cache_write)}
{/* Rate time series */}
} />
{variant === 'both' ? (
<>
>
) : (
<>
>
)}
);
}