diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/utils.test.ts b/ui/litellm-dashboard/src/components/EntityUsageExport/utils.test.ts index 67de4b69174..bb34b1b7975 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/utils.test.ts +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/utils.test.ts @@ -378,7 +378,7 @@ describe("EntityUsageExport utils", () => { } }); - it("should use dash when team id is not available", () => { + it("should fall back to entity key when team id is not available in metadata", () => { const spendDataWithoutTeamId: EntitySpendData = { ...mockSpendData, results: [ @@ -408,10 +408,55 @@ describe("EntityUsageExport utils", () => { const result = generateDailyData(spendDataWithoutTeamId, "Team"); const entry = result[0]; - expect(entry["Team ID"]).toBe("-"); + expect(entry["Team ID"]).toBe("entity1"); expect(entry["Team"]).toBe("-"); }); + it("should populate Customer ID from end_user when no team_id in metadata", () => { + const customerSpendData: EntitySpendData = { + ...mockSpendData, + results: [ + { + date: "2025-01-01", + breakdown: { + entities: { + "37": { + metrics: { + spend: 0.2735, + api_requests: 252, + successful_requests: 252, + failed_requests: 0, + total_tokens: 414370, + prompt_tokens: 300000, + completion_tokens: 114370, + }, + api_key_breakdown: { + "sk-key123": { + metrics: { + spend: 0.2735, + api_requests: 252, + successful_requests: 252, + failed_requests: 0, + total_tokens: 414370, + }, + metadata: {}, + }, + }, + }, + }, + }, + }, + ], + metadata: mockSpendData.metadata, + }; + + const result = generateDailyData(customerSpendData, "Customer"); + const entry = result[0]; + + expect(entry["Customer ID"]).toBe("37"); + expect(entry["Customer"]).toBe("-"); + }); + it("should format spend values correctly", () => { const result = generateDailyData(mockSpendData, "Team"); @@ -1149,6 +1194,64 @@ describe("EntityUsageExport utils", () => { } }); + it("should populate Customer ID from end_user when no team_id in metadata", () => { + const customerSpendDataWithModels: EntitySpendData = { + ...mockSpendDataWithModels, + results: [ + { + date: "2025-01-01", + breakdown: { + entities: { + "37": { + metrics: { + spend: 0.2735, + api_requests: 252, + successful_requests: 252, + failed_requests: 0, + total_tokens: 414370, + prompt_tokens: 300000, + completion_tokens: 114370, + cache_read_input_tokens: 0, + cache_creation_input_tokens: 0, + }, + api_key_breakdown: { + "sk-key123": { + metrics: { + spend: 0.2735, + api_requests: 252, + successful_requests: 252, + failed_requests: 0, + total_tokens: 414370, + }, + metadata: {}, + }, + }, + }, + }, + models: { + "vertex_ai/gemini-3-flash-preview": { + metrics: { + spend: 0.2735, + api_requests: 252, + successful_requests: 252, + failed_requests: 0, + total_tokens: 414370, + }, + }, + }, + }, + }, + ], + metadata: mockSpendDataWithModels.metadata, + }; + + const result = generateDailyWithModelsData(customerSpendDataWithModels, "Customer"); + const entry = result[0]; + + expect(entry["Customer ID"]).toBe("37"); + expect(entry["Customer"]).toBe("-"); + }); + it("should handle empty models breakdown", () => { const spendDataWithoutModels: EntitySpendData = { ...mockSpendDataWithModels, diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts b/ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts index ebef3da8a77..cc42aed9ef3 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/utils.ts @@ -82,8 +82,9 @@ export const generateDailyData = ( spendData.results.forEach((day) => { Object.entries(day.breakdown.entities || {}).forEach(([entity, data]: [string, any]) => { // Extract team_id from api_key_breakdown metadata (not data.metadata which is empty) - const teamId = extractTeamIdFromApiKeyBreakdown(data.api_key_breakdown); - const teamAlias = teamId ? teamAliasMap[teamId] || null : null; + // Fall back to entity key itself, which for customer entity type is the end_user value + const teamId = extractTeamIdFromApiKeyBreakdown(data.api_key_breakdown) || entity; + const teamAlias = teamAliasMap[teamId] || null; dailyBreakdown.push({ Date: day.date, @@ -232,8 +233,9 @@ export const generateDailyWithModelsData = ( Object.entries(dailyEntityModels).forEach(([entity, models]) => { const entityData = day.breakdown.entities?.[entity]; // Extract team_id from api_key_breakdown metadata (not entityData.metadata which is empty) - const teamId = extractTeamIdFromApiKeyBreakdown(entityData?.api_key_breakdown); - const teamAlias = teamId ? teamAliasMap[teamId] || null : null; + // Fall back to entity key itself, which for customer entity type is the end_user value + const teamId = extractTeamIdFromApiKeyBreakdown(entityData?.api_key_breakdown) || entity; + const teamAlias = teamAliasMap[teamId] || null; Object.entries(models).forEach(([model, metrics]: [string, any]) => { dailyModelBreakdown.push({