diff --git a/ui/litellm-dashboard/src/components/activity_metrics.tsx b/ui/litellm-dashboard/src/components/activity_metrics.tsx index 680c6004195..773822a7c8f 100644 --- a/ui/litellm-dashboard/src/components/activity_metrics.tsx +++ b/ui/litellm-dashboard/src/components/activity_metrics.tsx @@ -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; } -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 ( -
-

{label}

- {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 ( -
-
- -

- {formatCategoryName(dataKey)} -

-
-

- {formattedValue} -

-
- ); - })} -
- ); - } - 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 ( -
- {categories.map((category, idx) => { - const colorName = colors[idx] as keyof typeof colorNameToHex; - const hexColor = colorNameToHex[colorName] || colors[idx]; - return ( -
- -

- {formatCategoryName(category)} -

-
- ); - })} -
- ); -}; - const ModelSection = ({ modelName, metrics, @@ -215,14 +88,7 @@ const ModelSection = ({
Total Tokens - +
Requests per day - +
`$${formatNumberWithCommas(value, 2)}`} />
@@ -487,26 +348,14 @@ export const ActivityMetrics: React.FC = ({ /> -
- Total Requests Over Time - -
+ Total Requests Over Time 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; diff --git a/ui/litellm-dashboard/src/components/common_components/chartUtils.tsx b/ui/litellm-dashboard/src/components/common_components/chartUtils.tsx new file mode 100644 index 00000000000..b155637247d --- /dev/null +++ b/ui/litellm-dashboard/src/components/common_components/chartUtils.tsx @@ -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 ( +
+

{label}

+ {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 ( +
+
+ +

+ {formatCategoryName(dataKey)} +

+
+

+ {formattedValue} +

+
+ ); + })} +
+ ); + } + 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 ( +
+ {categories.map((category, idx) => { + const colorName = colors[idx] as keyof typeof colorNameToHex; + const hexColor = colorNameToHex[colorName] || colors[idx]; + return ( +
+ +

+ {formatCategoryName(category)} +

+
+ ); + })} +
+ ); +};