diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 696eeb51a34..1ca26164431 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -24834,6 +24834,32 @@ "supports_vision": true, "tool_use_system_prompt_tokens": 159 }, + "eu.anthropic.claude-opus-4-5-20251101-v1:0": { + "cache_creation_input_token_cost": 6.25e-06, + "cache_read_input_token_cost": 5e-07, + "input_cost_per_token": 5e-06, + "litellm_provider": "bedrock_converse", + "max_input_tokens": 200000, + "max_output_tokens": 64000, + "max_tokens": 64000, + "mode": "chat", + "output_cost_per_token": 2.5e-05, + "search_context_cost_per_query": { + "search_context_size_high": 0.01, + "search_context_size_low": 0.01, + "search_context_size_medium": 0.01 + }, + "supports_assistant_prefill": true, + "supports_computer_use": true, + "supports_function_calling": true, + "supports_pdf_input": true, + "supports_prompt_caching": true, + "supports_reasoning": true, + "supports_response_schema": true, + "supports_tool_choice": true, + "supports_vision": true, + "tool_use_system_prompt_tokens": 159 + }, "us.anthropic.claude-sonnet-4-20250514-v1:0": { "cache_creation_input_token_cost": 3.75e-06, "cache_read_input_token_cost": 3e-07, diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.test.tsx index 14f53dd28a0..9a9fa68f762 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.test.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.test.tsx @@ -265,7 +265,7 @@ describe("EntityUsage", () => { }); expect(await screen.findByText("Tag Spend Overview")).toBeInTheDocument(); - expect(await screen.findByText("$-")).toBeInTheDocument(); + expect(await screen.findByText("$0.00")).toBeInTheDocument(); expect(screen.getByText("Total Spend")).toBeInTheDocument(); expect(screen.getAllByText("0")[0]).toBeInTheDocument(); }); @@ -310,4 +310,79 @@ describe("EntityUsage", () => { expect(screen.getByText("Top Agents")).toBeInTheDocument(); }); + + it("should use entityList label when entityList is provided and entity exists", async () => { + const customEntityList = [ + { label: "Custom Tag Label", value: "tag-1" }, + { label: "Tag 2", value: "tag-2" }, + ]; + + render(); + + await waitFor(() => { + expect(mockTagDailyActivityCall).toHaveBeenCalled(); + }); + + await waitFor(() => { + expect(screen.getByText("Custom Tag Label")).toBeInTheDocument(); + }); + }); + + it("should fallback to team_alias when entityList is provided but entity does not exist", async () => { + const customEntityList = [{ label: "Tag 2", value: "tag-2" }]; + + render(); + + await waitFor(() => { + expect(mockTagDailyActivityCall).toHaveBeenCalled(); + }); + + await waitFor(() => { + expect(screen.getByText("Tag 1")).toBeInTheDocument(); + }); + }); + + it("should fallback to team_alias when entityList is null", async () => { + render(); + + await waitFor(() => { + expect(mockTagDailyActivityCall).toHaveBeenCalled(); + }); + + await waitFor(() => { + expect(screen.getByText("Tag 1")).toBeInTheDocument(); + }); + }); + + it("should fallback to entity value when no entityList and no team_alias", async () => { + const spendDataWithoutAlias = { + ...mockSpendData, + results: [ + { + ...mockSpendData.results[0], + breakdown: { + ...mockSpendData.results[0].breakdown, + entities: { + "tag-1": { + ...mockSpendData.results[0].breakdown.entities["tag-1"], + metadata: {}, + }, + }, + }, + }, + ], + }; + + mockTagDailyActivityCall.mockResolvedValue(spendDataWithoutAlias); + + render(); + + await waitFor(() => { + expect(mockTagDailyActivityCall).toHaveBeenCalled(); + }); + + await waitFor(() => { + expect(screen.getByText("tag-1")).toBeInTheDocument(); + }); + }); }); diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx index 3ccda5aa047..8cb318b06b2 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx @@ -305,6 +305,20 @@ const EntityUsage: React.FC = ({ } }; + const getEntityLabel = (entity: string, metadata?: Record): string => { + if (entityList) { + const entityItem = entityList.find((item) => item.value === entity); + if (entityItem) { + return entityItem.label; + } + } + // Fallback to team_alias for backward compatibility + if (metadata?.team_alias) { + return metadata.team_alias; + } + return entity; + }; + const filterDataByTags = (data: EntityMetricWithMetadata[]) => { if (selectedTags.length === 0) return data; return data.filter((item) => selectedTags.includes(item.metadata.id)); @@ -328,7 +342,7 @@ const EntityUsage: React.FC = ({ cache_creation_input_tokens: 0, }, metadata: { - alias: (data.metadata as any).team_alias || entity, + alias: getEntityLabel(entity, data.metadata as any), id: entity, }, }; @@ -472,7 +486,7 @@ const EntityUsage: React.FC = ({ const metrics = entityData as EntityMetrics; return (

- {metrics.metadata.team_alias || entity}: $ + {getEntityLabel(entity, metrics.metadata)}: $ {formatNumberWithCommas(metrics.metrics.spend, 2)}

); diff --git a/ui/litellm-dashboard/src/utils/dataUtils.test.ts b/ui/litellm-dashboard/src/utils/dataUtils.test.ts index 24d9d7f1d8d..14eba8a7edd 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.test.ts @@ -72,8 +72,8 @@ describe("dataUtils", () => { }); it("should handle zero and non-finite values", () => { - expect(formatNumberWithCommas(0)).toBe("-"); - expect(formatNumberWithCommas(0, 2)).toBe("-"); + expect(formatNumberWithCommas(0)).toBe("0"); + expect(formatNumberWithCommas(0, 2)).toBe("0.00"); expect(formatNumberWithCommas(Infinity)).toBe("-"); expect(formatNumberWithCommas(Number.NaN)).toBe("-"); }); @@ -83,6 +83,18 @@ describe("dataUtils", () => { expect(formatNumberWithCommas(12_345, 2, true)).toBe("12.35K"); expect(formatNumberWithCommas(-1_200, 2, true)).toBe("-1.20K"); }); + + it("should show zero when showZero is true", () => { + expect(formatNumberWithCommas(0, 0, false, true)).toBe("0"); + expect(formatNumberWithCommas(0, 2, false, true)).toBe("0.00"); + expect(formatNumberWithCommas(0, 0, true, true)).toBe("0"); + }); + + it("should return '-' for zero when showZero is false", () => { + expect(formatNumberWithCommas(0, 0, false, false)).toBe("-"); + expect(formatNumberWithCommas(0, 2, false, false)).toBe("-"); + expect(formatNumberWithCommas(0, 0, true, false)).toBe("-"); + }); }); describe("getSpendString", () => { diff --git a/ui/litellm-dashboard/src/utils/dataUtils.ts b/ui/litellm-dashboard/src/utils/dataUtils.ts index f58ad074444..0f8d11a178e 100644 --- a/ui/litellm-dashboard/src/utils/dataUtils.ts +++ b/ui/litellm-dashboard/src/utils/dataUtils.ts @@ -16,8 +16,9 @@ export const formatNumberWithCommas = ( value: number | null | undefined, decimals: number = 0, abbreviate: boolean = false, + showZero: boolean = true, ): string => { - if (value === null || value === undefined || !Number.isFinite(value) || value === 0) { + if (value === null || value === undefined || !Number.isFinite(value) || (value === 0 && !showZero)) { return "-"; } @@ -51,7 +52,7 @@ export const getSpendString = (value: number | null | undefined, decimals: numbe return "-"; } - const formatted = formatNumberWithCommas(value, decimals); + const formatted = formatNumberWithCommas(value, decimals, false, false); const numericFormatted = Number(formatted.replace(/,/g, "")); if (numericFormatted === 0) {