Skip to content

Commit

Permalink
chore: some minor design changes
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasgriffintn committed Dec 13, 2024
1 parent ecdb0dc commit 3b1c3f7
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 70 deletions.
60 changes: 48 additions & 12 deletions apps/web/app/ai-metrics/components/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ResponsiveContainer,
XAxis,
YAxis,
Cell,
} from "recharts";

import {
Expand All @@ -17,6 +18,8 @@ import {
ChartTooltipContent,
} from "@/components/ui/chart";

import { getGradient } from "@/lib/utils";

interface CombinedMetricsChartProps {
data: Array<{
name: string;
Expand All @@ -31,6 +34,24 @@ export function CombinedMetricsChart({ data }: CombinedMetricsChartProps) {
const formatLatency = (value: number) => `${value.toLocaleString()}ms`;
const formatTokens = (value: number) => `${value.toLocaleString()}`;

const barColors = [
"#FF6B6B", // Coral Red
"#4ECDC4", // Turquoise
"#45B7D1", // Sky Blue
"#96CEB4", // Sage Green
"#FFEEAD", // Cream Yellow
"#D4A5A5", // Dusty Rose
"#9B5DE5", // Purple
"#F15BB5", // Pink
"#00BBF9", // Bright Blue
"#00F5D4", // Mint
"#FEE440", // Yellow
"#FF99C8", // Light Pink
"#A8E6CF", // Mint Green
"#FFB3BA", // Light Red
"#BFCFF7", // Lavender
];

return (
<ChartContainer
config={{
Expand Down Expand Up @@ -58,34 +79,34 @@ export function CombinedMetricsChart({ data }: CombinedMetricsChartProps) {
data={data}
margin={{ top: 40, right: 60, left: 30, bottom: 40 }}
>
<CartesianGrid strokeDasharray="3 3" opacity={0.3} />
<CartesianGrid strokeDasharray="3 3" opacity={0.2} />
<XAxis
dataKey="name"
angle={-45}
textAnchor="end"
height={60}
interval={0}
tick={{ fontSize: 11 }}
tick={{ fontSize: 11, fill: "var(--foreground)" }}
/>
<YAxis
yAxisId="left"
orientation="left"
stroke="var(--color-latency)"
tick={{ fontSize: 11 }}
stroke="var(--foreground)"
tick={{ fontSize: 11, fill: "var(--foreground)" }}
tickFormatter={formatLatency}
width={80}
/>
<YAxis
yAxisId="right"
orientation="right"
stroke="var(--color-totalTokens)"
tick={{ fontSize: 11 }}
stroke="var(--foreground)"
tick={{ fontSize: 11, fill: "var(--foreground)" }}
tickFormatter={formatTokens}
width={80}
/>
<ChartTooltip
content={<ChartTooltipContent />}
cursor={{ fill: "rgba(0, 0, 0, 0.1)" }}
cursor={{ fill: "rgba(255, 255, 255, 0.1)" }}
/>
<Legend
verticalAlign="top"
Expand All @@ -99,30 +120,45 @@ export function CombinedMetricsChart({ data }: CombinedMetricsChartProps) {
<Bar
yAxisId="left"
dataKey="latency"
fill="var(--color-latency)"
name="Latency (ms)"
/>
opacity={0.9}
>
{data.map((entry, index) => (
<Cell
key={`cell-${index}-${entry.name}`}
fill={barColors[index % barColors.length]}
style={{
filter: "brightness(1.1)",
}}
/>
))}
</Bar>
<Line
yAxisId="right"
type="monotone"
dataKey="promptTokens"
stroke="var(--color-promptTokens)"
stroke="#00F5D4"
strokeWidth={2}
name="Prompt Tokens"
dot={false}
/>
<Line
yAxisId="right"
type="monotone"
dataKey="completionTokens"
stroke="var(--color-completionTokens)"
stroke="#FF6B6B"
strokeWidth={2}
name="Completion Tokens"
dot={false}
/>
<Line
yAxisId="right"
type="monotone"
dataKey="totalTokens"
stroke="var(--color-totalTokens)"
stroke="#FEE440"
strokeWidth={2}
name="Total Tokens"
dot={false}
/>
</ComposedChart>
</ResponsiveContainer>
Expand Down
188 changes: 130 additions & 58 deletions apps/web/app/ai-metrics/components/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ const parseMetadata = (metadataString: string) => {
export function MetricsDashboard({ metrics }: { metrics: Metric[] }) {
const [selectedMetric, setSelectedMetric] = useState<Metric | null>(null);

const totalRequests = metrics.length;
const totalTokens = metrics.reduce((sum, metric) => {
const metadata = parseMetadata(metric.metadata);
return sum + (metadata.tokenUsage?.total_tokens || 0);
}, 0);
const totalCost = metrics.reduce((sum, metric) => {
const metadata = parseMetadata(metric.metadata);
return sum + (metadata.cost || 0);
}, 0);
const cachedRequests = metrics.filter((m) => {
const metadata = parseMetadata(m.metadata);
return metadata.cached === "true";
}).length;
const cachedPercentage = ((cachedRequests / totalRequests) * 100).toFixed(2);
const errorRequests = metrics.filter((m) => m.status === "error").length;

const combinedChartData = metrics.map((metric) => {
const metadata = parseMetadata(metric.metadata);
const tokenUsage = metadata.tokenUsage || {};
Expand All @@ -50,65 +66,121 @@ export function MetricsDashboard({ metrics }: { metrics: Metric[] }) {
});

return (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<Card className="md:col-span-1">
<CardHeader>
<CardTitle>AI Provider Metrics</CardTitle>
<CardDescription>
Latency and token usage for different AI providers and models
</CardDescription>
</CardHeader>
<CardContent className="h-[600px]">
<CombinedMetricsChart data={combinedChartData} />
</CardContent>
</Card>
<div className="space-y-6">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<Card>
<CardHeader className="p-4">
<CardTitle className="text-sm font-medium text-muted-foreground">
Requests
</CardTitle>
<CardDescription className="text-2xl font-bold">
{totalRequests}
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader className="p-4">
<CardTitle className="text-sm font-medium text-muted-foreground">
Tokens
</CardTitle>
<CardDescription className="text-2xl font-bold">
{(totalTokens / 1000).toFixed(1)}k
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader className="p-4">
<CardTitle className="text-sm font-medium text-muted-foreground">
Cost
</CardTitle>
<CardDescription className="text-2xl font-bold">
${totalCost.toFixed(2)}
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader className="p-4">
<CardTitle className="text-sm font-medium text-muted-foreground">
Cached
</CardTitle>
<CardDescription className="text-2xl font-bold">
{cachedPercentage}%
</CardDescription>
</CardHeader>
</Card>
<Card>
<CardHeader className="p-4">
<CardTitle className="text-sm font-medium text-muted-foreground">
Errors
</CardTitle>
<CardDescription className="text-2xl font-bold">
{errorRequests}
</CardDescription>
</CardHeader>
</Card>
</div>

<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>AI Provider Metrics</CardTitle>
<CardDescription>
Latency and token usage for different AI providers and models
</CardDescription>
</CardHeader>
<CardContent className="h-[400px]">
<CombinedMetricsChart data={combinedChartData} />
</CardContent>
</Card>

<Card>
<CardHeader>
<CardTitle>Metrics Details</CardTitle>
<CardDescription>
Detailed information for each metric entry
</CardDescription>
</CardHeader>
<CardContent className="h-[400px] p-0">
<ScrollArea className="h-full">
<div className="p-4">
<Table>
<TableHeader>
<TableRow>
<TableHead>Provider (Model)</TableHead>
<TableHead>Latency (ms)</TableHead>
<TableHead>Total Tokens</TableHead>
<TableHead>Status</TableHead>
<TableHead>Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{metrics.map((metric) => {
const metadata = parseMetadata(metric.metadata);
const tokenUsage = metadata.tokenUsage || {};
return (
<TableRow
key={metric.traceId}
className="cursor-pointer hover:bg-muted/50"
onClick={() => setSelectedMetric(metric)}
>
<TableCell>{`${metadata.provider} (${metadata.model})`}</TableCell>
<TableCell>{metric.value}</TableCell>
<TableCell>
{tokenUsage.total_tokens || "N/A"}
</TableCell>
<TableCell>{metric.status}</TableCell>
<TableCell>{metric.timestamp}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</ScrollArea>
</CardContent>
</Card>
</div>

<Card className="md:col-span-1">
<CardHeader>
<CardTitle>Metrics Details</CardTitle>
<CardDescription>
Detailed information for each metric entry
</CardDescription>
</CardHeader>
<CardContent className="h-[600px] p-0">
<ScrollArea className="h-full">
<div className="p-4">
<Table>
<TableHeader>
<TableRow>
<TableHead>Provider (Model)</TableHead>
<TableHead>Latency (ms)</TableHead>
<TableHead>Total Tokens</TableHead>
<TableHead>Status</TableHead>
<TableHead>Timestamp</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{metrics.map((metric) => {
const metadata = parseMetadata(metric.metadata);
const tokenUsage = metadata.tokenUsage || {};
return (
<TableRow
key={metric.traceId}
className="cursor-pointer hover:bg-muted/50"
onClick={() => setSelectedMetric(metric)}
>
<TableCell>{`${metadata.provider} (${metadata.model})`}</TableCell>
<TableCell>{metric.value}</TableCell>
<TableCell>
{tokenUsage.total_tokens || "N/A"}
</TableCell>
<TableCell>{metric.status}</TableCell>
<TableCell>{metric.timestamp}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</div>
</ScrollArea>
</CardContent>
</Card>
{selectedMetric && (
<MetricDetails
metric={selectedMetric}
Expand Down
23 changes: 23 additions & 0 deletions apps/web/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,26 @@ import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

export function getGradient(index: number, total: number, colors: string[]) {
const defaultColor = "hsl(var(--primary))";
if (colors.length < 2) return colors[0] ?? defaultColor;

const position = index / Math.max(1, total - 1);
const colorIndex = position * (colors.length - 1);
const start = Math.floor(colorIndex);
const end = Math.min(start + 1, colors.length - 1);
const t = colorIndex - start;

const startColor = colors[start] ?? defaultColor;
const endColor = colors[end] ?? defaultColor;

return interpolateColor(startColor, endColor, t);
}

function interpolateColor(color1: string, color2: string, t: number) {
const c1 = color1.startsWith("#") ? color1 : color1;
const c2 = color2.startsWith("#") ? color2 : color2;

return t < 0.5 ? c1 : c2;
}

0 comments on commit 3b1c3f7

Please sign in to comment.