mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
fix(ui): guard usage breakdown aggregation against entries missing metrics
This commit is contained in:
parent
69a491e168
commit
2e391dcfd2
3 changed files with 54 additions and 1 deletions
|
|
@ -596,6 +596,53 @@ describe("UsagePage", () => {
|
|||
expect(screen.getByText("Top Virtual Keys")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should not crash when a breakdown entry is missing its metrics (partial/large dataset)", async () => {
|
||||
const malformedSpendData = {
|
||||
...mockSpendData,
|
||||
results: [
|
||||
{
|
||||
...mockSpendData.results[0],
|
||||
breakdown: {
|
||||
...mockSpendData.results[0].breakdown,
|
||||
api_keys: {
|
||||
...mockSpendData.results[0].breakdown.api_keys,
|
||||
"sk-broken": {
|
||||
metadata: { key_alias: "Broken Key", tags: [] },
|
||||
},
|
||||
},
|
||||
models: {
|
||||
...mockSpendData.results[0].breakdown.models,
|
||||
"broken-model": {
|
||||
metadata: {},
|
||||
api_key_breakdown: {},
|
||||
},
|
||||
},
|
||||
model_groups: {
|
||||
...mockSpendData.results[0].breakdown.model_groups,
|
||||
"broken-group": {
|
||||
metadata: {},
|
||||
api_key_breakdown: {},
|
||||
},
|
||||
},
|
||||
providers: {
|
||||
...mockSpendData.results[0].breakdown.providers,
|
||||
"broken-provider": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
mockUserDailyActivityAggregatedCall.mockResolvedValue(malformedSpendData as any);
|
||||
|
||||
renderWithProviders(<UsagePage {...defaultProps} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUserDailyActivityAggregatedCall).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
expect(screen.getByText("Top Virtual Keys")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should render the daily spend and top models charts with cyan bars", async () => {
|
||||
const { container } = renderWithProviders(<UsagePage {...defaultProps} />);
|
||||
|
||||
|
|
|
|||
|
|
@ -252,6 +252,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
const modelSpend: { [key: string]: MetricWithMetadata } = {};
|
||||
userSpendData.results.forEach((day) => {
|
||||
Object.entries(day.breakdown.models || {}).forEach(([model, metrics]) => {
|
||||
if (!metrics?.metrics) return;
|
||||
if (!modelSpend[model]) {
|
||||
modelSpend[model] = {
|
||||
metrics: {
|
||||
|
|
@ -298,6 +299,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
const modelGroupSpend: { [key: string]: MetricWithMetadata } = {};
|
||||
userSpendData.results.forEach((day) => {
|
||||
Object.entries(day.breakdown.model_groups || {}).forEach(([modelGroup, metrics]) => {
|
||||
if (!metrics?.metrics) return;
|
||||
if (!modelGroupSpend[modelGroup]) {
|
||||
modelGroupSpend[modelGroup] = {
|
||||
metrics: {
|
||||
|
|
@ -346,6 +348,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
const providerSpendMap: { [key: string]: MetricWithMetadata } = {};
|
||||
userSpendData.results.forEach((day) => {
|
||||
Object.entries(day.breakdown.providers || {}).forEach(([provider, metrics]) => {
|
||||
if (!metrics?.metrics) return;
|
||||
if (!providerSpendMap[provider]) {
|
||||
providerSpendMap[provider] = {
|
||||
metrics: {
|
||||
|
|
@ -391,6 +394,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ teams, organizations }) => {
|
|||
const keySpend: { [key: string]: KeyMetricWithMetadata } = {};
|
||||
userSpendData.results.forEach((day) => {
|
||||
Object.entries(day.breakdown.api_keys || {}).forEach(([key, metrics]) => {
|
||||
if (!metrics?.metrics) return;
|
||||
if (!keySpend[key]) {
|
||||
keySpend[key] = {
|
||||
metrics: {
|
||||
|
|
|
|||
|
|
@ -369,6 +369,7 @@ export const processActivityData = (
|
|||
|
||||
dailyActivity.results.forEach((day) => {
|
||||
Object.entries(day.breakdown[key] || {}).forEach(([model, modelData]) => {
|
||||
if (!modelData?.metrics) return;
|
||||
if (!modelMetrics[model]) {
|
||||
modelMetrics[model] = {
|
||||
label:
|
||||
|
|
@ -430,6 +431,7 @@ export const processActivityData = (
|
|||
const modelData = day.breakdown[key]?.[model];
|
||||
if (modelData && "api_key_breakdown" in modelData) {
|
||||
Object.entries(modelData.api_key_breakdown || {}).forEach(([apiKey, keyData]) => {
|
||||
if (!keyData?.metrics) return;
|
||||
if (!apiKeyBreakdown[apiKey]) {
|
||||
apiKeyBreakdown[apiKey] = {
|
||||
api_key: apiKey,
|
||||
|
|
@ -466,7 +468,7 @@ export const processActivityData = (
|
|||
Object.entries(day.breakdown.models || {}).forEach(([modelName, modelData]) => {
|
||||
if (modelData && "api_key_breakdown" in modelData) {
|
||||
const keyDataForModel = modelData.api_key_breakdown?.[apiKeyHash];
|
||||
if (keyDataForModel) {
|
||||
if (keyDataForModel?.metrics) {
|
||||
if (!modelBreakdown[modelName]) {
|
||||
modelBreakdown[modelName] = {
|
||||
model: modelName,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue