mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
Merge remote-tracking branch 'origin' into litellm_usage_date_pick_fix
This commit is contained in:
commit
94a0ff08b7
5 changed files with 135 additions and 7 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(<EntityUsage {...defaultProps} entityList={customEntityList} />);
|
||||
|
||||
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(<EntityUsage {...defaultProps} entityList={customEntityList} />);
|
||||
|
||||
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(<EntityUsage {...defaultProps} entityList={null} />);
|
||||
|
||||
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(<EntityUsage {...defaultProps} entityList={null} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockTagDailyActivityCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("tag-1")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -305,6 +305,20 @@ const EntityUsage: React.FC<EntityUsageProps> = ({
|
|||
}
|
||||
};
|
||||
|
||||
const getEntityLabel = (entity: string, metadata?: Record<string, any>): 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<EntityUsageProps> = ({
|
|||
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<EntityUsageProps> = ({
|
|||
const metrics = entityData as EntityMetrics;
|
||||
return (
|
||||
<p key={entity} className="text-sm text-gray-600">
|
||||
{metrics.metadata.team_alias || entity}: $
|
||||
{getEntityLabel(entity, metrics.metadata)}: $
|
||||
{formatNumberWithCommas(metrics.metrics.spend, 2)}
|
||||
</p>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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", () => {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue