Enhance chart readability with short-form notation for large numbers (#12370)

* format y-axis value for total tokens

* format y-axis for the rest of the charts on model-activity

* revert changes for requests per day

* labels modified to plain text

* added plain text label for api_requests and spend

* minor

* move components to utils
This commit is contained in:
tanjiro 2025-08-13 09:24:03 +09:00 committed by GitHub
parent 88e4d302a2
commit ebae03cf93
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 138 additions and 163 deletions

View file

@ -1,143 +1,16 @@
import React from 'react';
import { Card, Grid, Text, Title, Accordion, AccordionHeader, AccordionBody } from '@tremor/react';
import { Card, Grid, Text, Title } from '@tremor/react';
import { AreaChart, BarChart } from '@tremor/react';
import { SpendMetrics, DailyData, ModelActivityData, MetricWithMetadata, KeyMetricWithMetadata, TopApiKeyData } from './usage/types';
import { DailyData, ModelActivityData, KeyMetricWithMetadata, TopApiKeyData } from './usage/types';
import { Collapse } from 'antd';
import { formatNumberWithCommas } from '@/utils/dataUtils';
import type { CustomTooltipProps } from "@tremor/react";
import {
valueFormatter,
valueFormatterSpend,
} from "../components/usage/utils/value_formatters";
import { valueFormatter } from '../components/usage/utils/value_formatters';
import { CustomTooltip, CustomLegend } from './common_components/chartUtils';
interface ActivityMetricsProps {
modelMetrics: Record<string, ModelActivityData>;
}
interface ChartDataPoint {
date: string;
metrics: SpendMetrics;
}
const colorNameToHex: { [key: string]: string } = {
blue: "#3b82f6",
cyan: "#06b6d4",
indigo: "#6366f1",
green: "#22c55e",
red: "#ef4444",
purple: "#8b5cf6",
};
export const CustomTooltip = ({
active,
payload,
label,
}: CustomTooltipProps) => {
if (active && payload && payload.length) {
const formatCategoryName = (name: string): string => {
return name
.replace("metrics.", "")
.replace(/_/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
};
const getRawValue = (
dataPoint: ChartDataPoint,
key: string
): number | undefined => {
// key is like "metrics.total_tokens"
const metricKey = key.substring(
key.indexOf(".") + 1
) as keyof SpendMetrics;
if (dataPoint.metrics && metricKey in dataPoint.metrics) {
return dataPoint.metrics[metricKey];
}
return undefined;
};
return (
<div className="w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown">
<p className="text-tremor-content-strong">{label}</p>
{payload.map((item) => {
const dataKey = item.dataKey?.toString();
if (!dataKey || !item.payload) return null;
const rawValue = getRawValue(item.payload, dataKey);
const isSpend = dataKey.includes("spend");
const formattedValue =
rawValue !== undefined
? isSpend
? `$${rawValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
: rawValue.toLocaleString()
: "N/A";
const colorName = item.color as keyof typeof colorNameToHex;
const hexColor = colorNameToHex[colorName] || item.color;
return (
<div
key={dataKey}
className="flex items-center justify-between space-x-4"
>
<div className="flex items-center space-x-2">
<span
className={`h-2 w-2 shrink-0 rounded-full ring-2 ring-white drop-shadow-md`}
style={{ backgroundColor: hexColor }}
/>
<p className="font-medium text-tremor-content dark:text-dark-tremor-content">
{formatCategoryName(dataKey)}
</p>
</div>
<p className="font-medium text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis">
{formattedValue}
</p>
</div>
);
})}
</div>
);
}
return null;
};
const CustomLegend = ({
categories,
colors,
}: {
categories: string[];
colors: string[];
}) => {
const formatCategoryName = (name: string): string => {
return name
.replace("metrics.", "")
.replace(/_/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
};
return (
<div className="flex items-center justify-end space-x-4">
{categories.map((category, idx) => {
const colorName = colors[idx] as keyof typeof colorNameToHex;
const hexColor = colorNameToHex[colorName] || colors[idx];
return (
<div key={category} className="flex items-center space-x-2">
<span
className={`h-2 w-2 shrink-0 rounded-full ring-4 ring-white`}
style={{ backgroundColor: hexColor }}
/>
<p className="text-sm text-tremor-content dark:text-dark-tremor-content">
{formatCategoryName(category)}
</p>
</div>
);
})}
</div>
);
};
const ModelSection = ({
modelName,
metrics,
@ -215,14 +88,7 @@ const ModelSection = ({
<Card>
<div className="flex justify-between items-center">
<Title>Total Tokens</Title>
<CustomLegend
categories={[
"metrics.prompt_tokens",
"metrics.completion_tokens",
"metrics.total_tokens",
]}
colors={["blue", "cyan", "indigo"]}
/>
<CustomLegend categories={["metrics.prompt_tokens", "metrics.completion_tokens", "metrics.total_tokens"]} colors={["blue", "cyan", "indigo"]} />
</div>
<AreaChart
className="mt-4"
@ -243,10 +109,7 @@ const ModelSection = ({
<Card>
<div className="flex justify-between items-center">
<Title>Requests per day</Title>
<CustomLegend
categories={["metrics.api_requests"]}
colors={["blue"]}
/>
<CustomLegend categories={["metrics.api_requests"]} colors={["blue"]} />
</div>
<BarChart
className="mt-4"
@ -271,9 +134,7 @@ const ModelSection = ({
index="date"
categories={["metrics.spend"]}
colors={["green"]}
valueFormatter={valueFormatterSpend}
customTooltip={CustomTooltip}
showLegend={false}
valueFormatter={(value: number) => `$${formatNumberWithCommas(value, 2)}`}
/>
</Card>
@ -487,26 +348,14 @@ export const ActivityMetrics: React.FC<ActivityMetricsProps> = ({
/>
</Card>
<Card>
<div className="flex justify-between items-center">
<Title>Total Requests Over Time</Title>
<CustomLegend
categories={[
"metrics.successful_requests",
"metrics.failed_requests",
]}
colors={["green", "red"]}
/>
</div>
<Title>Total Requests Over Time</Title>
<AreaChart
className="mt-4"
data={sortedDailyData}
index="date"
categories={[
"metrics.successful_requests",
"metrics.failed_requests",
]}
colors={["green", "red"]}
valueFormatter={valueFormatter}
categories={["metrics.successful_requests", "metrics.failed_requests"]}
colors={["emerald", "red"]}
valueFormatter={(number: number) => number.toLocaleString()}
stack
customTooltip={CustomTooltip}
showLegend={false}
@ -585,7 +434,6 @@ export const processActivityData = (dailyActivity: { results: DailyData[] }, key
daily_data: []
};
}
// Update totals
modelMetrics[model].total_requests += modelData.metrics.api_requests;
modelMetrics[model].prompt_tokens += modelData.metrics.prompt_tokens;

View file

@ -0,0 +1,127 @@
import React from "react";
import type { CustomTooltipProps } from "@tremor/react";
import { SpendMetrics } from "../usage/types";
interface ChartDataPoint {
date: string;
metrics: SpendMetrics;
}
const colorNameToHex: { [key: string]: string } = {
blue: "#3b82f6",
cyan: "#06b6d4",
indigo: "#6366f1",
green: "#22c55e",
red: "#ef4444",
purple: "#8b5cf6",
};
export const CustomTooltip = ({
active,
payload,
label,
}: CustomTooltipProps) => {
if (active && payload && payload.length) {
const formatCategoryName = (name: string): string => {
return name
.replace("metrics.", "")
.replace(/_/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
};
const getRawValue = (
dataPoint: ChartDataPoint,
key: string
): number | undefined => {
// key is like "metrics.total_tokens"
const metricKey = key.substring(
key.indexOf(".") + 1
) as keyof SpendMetrics;
if (dataPoint.metrics && metricKey in dataPoint.metrics) {
return dataPoint.metrics[metricKey];
}
return undefined;
};
return (
<div className="w-56 rounded-tremor-default border border-tremor-border bg-tremor-background p-2 text-tremor-default shadow-tremor-dropdown">
<p className="text-tremor-content-strong">{label}</p>
{payload.map((item) => {
const dataKey = item.dataKey?.toString();
if (!dataKey || !item.payload) return null;
const rawValue = getRawValue(item.payload, dataKey);
const isSpend = dataKey.includes("spend");
const formattedValue =
rawValue !== undefined
? isSpend
? `$${rawValue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`
: rawValue.toLocaleString()
: "N/A";
const colorName = item.color as keyof typeof colorNameToHex;
const hexColor = colorNameToHex[colorName] || item.color;
return (
<div
key={dataKey}
className="flex items-center justify-between space-x-4"
>
<div className="flex items-center space-x-2">
<span
className={`h-2 w-2 shrink-0 rounded-full ring-2 ring-white drop-shadow-md`}
style={{ backgroundColor: hexColor }}
/>
<p className="font-medium text-tremor-content dark:text-dark-tremor-content">
{formatCategoryName(dataKey)}
</p>
</div>
<p className="font-medium text-tremor-content-emphasis dark:text-dark-tremor-content-emphasis">
{formattedValue}
</p>
</div>
);
})}
</div>
);
}
return null;
};
export const CustomLegend = ({
categories,
colors,
}: {
categories: string[];
colors: string[];
}) => {
const formatCategoryName = (name: string): string => {
return name
.replace("metrics.", "")
.replace(/_/g, " ")
.split(" ")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
};
return (
<div className="flex items-center justify-end space-x-4">
{categories.map((category, idx) => {
const colorName = colors[idx] as keyof typeof colorNameToHex;
const hexColor = colorNameToHex[colorName] || colors[idx];
return (
<div key={category} className="flex items-center space-x-2">
<span
className={`h-2 w-2 shrink-0 rounded-full ring-4 ring-white`}
style={{ backgroundColor: hexColor }}
/>
<p className="text-sm text-tremor-content dark:text-dark-tremor-content">
{formatCategoryName(category)}
</p>
</div>
);
})}
</div>
);
};