diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx index 8b0d5ffac05..f32026dcafd 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx @@ -176,7 +176,7 @@ const UsagePage: React.FC = ({ teams, organizations }) => { const totalSpend = userSpendData.metadata?.total_spend || 0; // Calculate top models from the breakdown data - const getTopModels = (limit: number = 5) => { + const topModels = useMemo(() => { const modelSpend: { [key: string]: MetricWithMetadata } = {}; userSpendData.results.forEach((day) => { Object.entries(day.breakdown.models || {}).forEach(([model, metrics]) => { @@ -219,10 +219,10 @@ const UsagePage: React.FC = ({ teams, organizations }) => { tokens: metrics.metrics.total_tokens, })) .sort((a, b) => b.spend - a.spend) - .slice(0, limit); - }; + .slice(0, topModelsLimit); + }, [userSpendData.results, topModelsLimit]); - const getTopModelGroups = (limit: number = 5) => { + const topModelGroups = useMemo(() => { const modelGroupSpend: { [key: string]: MetricWithMetadata } = {}; userSpendData.results.forEach((day) => { Object.entries(day.breakdown.model_groups || {}).forEach(([modelGroup, metrics]) => { @@ -266,16 +266,16 @@ const UsagePage: React.FC = ({ teams, organizations }) => { tokens: metrics.metrics.total_tokens, })) .sort((a, b) => b.spend - a.spend) - .slice(0, limit); - }; + .slice(0, topModelsLimit); + }, [userSpendData.results, topModelsLimit]); // Calculate provider spend from the breakdown data - const getProviderSpend = () => { - const providerSpend: { [key: string]: MetricWithMetadata } = {}; + const providerSpend = useMemo(() => { + const providerSpendMap: { [key: string]: MetricWithMetadata } = {}; userSpendData.results.forEach((day) => { Object.entries(day.breakdown.providers || {}).forEach(([provider, metrics]) => { - if (!providerSpend[provider]) { - providerSpend[provider] = { + if (!providerSpendMap[provider]) { + providerSpendMap[provider] = { metrics: { spend: 0, prompt_tokens: 0, @@ -291,19 +291,19 @@ const UsagePage: React.FC = ({ teams, organizations }) => { api_key_breakdown: {}, }; } - providerSpend[provider].metrics.spend += metrics.metrics.spend; - providerSpend[provider].metrics.prompt_tokens += metrics.metrics.prompt_tokens; - providerSpend[provider].metrics.completion_tokens += metrics.metrics.completion_tokens; - providerSpend[provider].metrics.total_tokens += metrics.metrics.total_tokens; - providerSpend[provider].metrics.api_requests += metrics.metrics.api_requests; - providerSpend[provider].metrics.successful_requests += metrics.metrics.successful_requests || 0; - providerSpend[provider].metrics.failed_requests += metrics.metrics.failed_requests || 0; - providerSpend[provider].metrics.cache_read_input_tokens += metrics.metrics.cache_read_input_tokens || 0; - providerSpend[provider].metrics.cache_creation_input_tokens += metrics.metrics.cache_creation_input_tokens || 0; + providerSpendMap[provider].metrics.spend += metrics.metrics.spend; + providerSpendMap[provider].metrics.prompt_tokens += metrics.metrics.prompt_tokens; + providerSpendMap[provider].metrics.completion_tokens += metrics.metrics.completion_tokens; + providerSpendMap[provider].metrics.total_tokens += metrics.metrics.total_tokens; + providerSpendMap[provider].metrics.api_requests += metrics.metrics.api_requests; + providerSpendMap[provider].metrics.successful_requests += metrics.metrics.successful_requests || 0; + providerSpendMap[provider].metrics.failed_requests += metrics.metrics.failed_requests || 0; + providerSpendMap[provider].metrics.cache_read_input_tokens += metrics.metrics.cache_read_input_tokens || 0; + providerSpendMap[provider].metrics.cache_creation_input_tokens += metrics.metrics.cache_creation_input_tokens || 0; }); }); - return Object.entries(providerSpend).map(([provider, metrics]) => ({ + return Object.entries(providerSpendMap).map(([provider, metrics]) => ({ provider, spend: metrics.metrics.spend, requests: metrics.metrics.api_requests, @@ -311,10 +311,10 @@ const UsagePage: React.FC = ({ teams, organizations }) => { failed_requests: metrics.metrics.failed_requests, tokens: metrics.metrics.total_tokens, })); - }; + }, [userSpendData.results]); // Calculate top API keys from the breakdown data - const getTopKeys = (limit: number = 5) => { + const topKeys = useMemo(() => { const keySpend: { [key: string]: KeyMetricWithMetadata } = {}; userSpendData.results.forEach((day) => { Object.entries(day.breakdown.api_keys || {}).forEach(([key, metrics]) => { @@ -334,7 +334,7 @@ const UsagePage: React.FC = ({ teams, organizations }) => { metadata: { key_alias: metrics.metadata.key_alias, team_id: null, - tags: metrics.metadata.tags || [], // This gets key-level tags + tags: metrics.metadata.tags || [], }, }; } @@ -350,18 +350,16 @@ const UsagePage: React.FC = ({ teams, organizations }) => { }); }); - console.log("debugTags", { keySpend, userSpendData }); - return Object.entries(keySpend) .map(([api_key, metrics]) => ({ api_key, - key_alias: metrics.metadata.key_alias || "-", // Using truncated key as alias - tags: metrics.metadata.tags || [], // This will show key-level tags + key_alias: metrics.metadata.key_alias || "-", + tags: metrics.metadata.tags || [], spend: metrics.metrics.spend, })) .sort((a, b) => b.spend - a.spend) - .slice(0, limit); - }; + .slice(0, topKeysLimit); + }, [userSpendData.results, topKeysLimit]); const fetchUserSpendData = useCallback(async () => { if (!accessToken || !dateValue.from || !dateValue.to) return; @@ -440,9 +438,13 @@ const UsagePage: React.FC = ({ teams, organizations }) => { return () => clearTimeout(timeoutId); }, [fetchUserSpendData]); - const modelMetrics = processActivityData(userSpendData, "models", teams); - const keyMetrics = processActivityData(userSpendData, "api_keys", teams); - const mcpServerMetrics = processActivityData(userSpendData, "mcp_servers", teams); + const sortedDailyResults = useMemo( + () => [...userSpendData.results].sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()), + [userSpendData.results], + ); + const modelMetrics = useMemo(() => processActivityData(userSpendData, "models", teams), [userSpendData, teams]); + const keyMetrics = useMemo(() => processActivityData(userSpendData, "api_keys", teams), [userSpendData, teams]); + const mcpServerMetrics = useMemo(() => processActivityData(userSpendData, "mcp_servers", teams), [userSpendData, teams]); return (
@@ -654,9 +656,7 @@ const UsagePage: React.FC = ({ teams, organizations }) => { ) : ( new Date(a.date).getTime() - new Date(b.date).getTime(), - )} + data={sortedDailyResults} index="date" categories={["metrics.spend"]} colors={["cyan"]} @@ -688,7 +688,7 @@ const UsagePage: React.FC = ({ teams, organizations }) => { Top Virtual Keys = ({ teams, organizations }) => { {(() => { const modelData = modelViewType === "groups" - ? getTopModelGroups(topModelsLimit) - : getTopModels(topModelsLimit); + ? topModelGroups + : topModels; return ( = ({ teams, organizations }) => {