refactor: dashboard usage

This commit is contained in:
jialin
2026-05-06 12:27:22 +08:00
committed by jialin
parent 705b33e307
commit 898c7f1be2
23 changed files with 901 additions and 35 deletions
+189
View File
@@ -1,3 +1,8 @@
import {
BreakdownItem,
UsageBreakdownResponse
} from '@/pages/usage/config/types';
export const overviewConfigs = [
{
key: 'cluster_count',
@@ -35,3 +40,187 @@ export const baseColorMap = {
baseR2: 'rgba(48,0,255,0.8)',
baseR3: 'rgba(85,167,255,0.8)'
};
export type UsageGroupBy = 'model' | 'user' | 'api_key';
export type UsageMetric = 'total_tokens' | 'api_requests';
export interface UsageChartDatum {
name: string;
value: number;
}
export interface DashboardUsageCommonParams {
start_date: string;
end_date: string;
scope: string;
granularity: string;
filters: Record<string, any>;
}
export const usageChartHeight = 300;
export const usageChartCardHeight = usageChartHeight + 52;
export const buildUsageLabel = (item: BreakdownItem, groupBy: UsageGroupBy) => {
const rawItem = item as any;
const groupItem = rawItem[groupBy];
const identityValue = groupItem?.identity?.value;
const groupValue = groupItem?.value;
if (typeof groupItem === 'string') {
return groupItem;
}
if (groupBy === 'model') {
const providerName =
identityValue?.provider_name || groupValue?.provider_name;
const modelName =
identityValue?.model_name ||
groupValue?.model_name ||
rawItem.model_name ||
rawItem.model;
if (providerName && modelName) {
return `${providerName}/${modelName}`;
}
return groupItem?.label || modelName || '-';
}
return (
groupItem?.label ||
identityValue?.user_name ||
identityValue?.api_key_name ||
identityValue?.access_key ||
groupValue?.user_name ||
groupValue?.api_key_name ||
groupValue?.access_key ||
rawItem.user_name ||
rawItem.api_key_name ||
rawItem.access_key ||
rawItem[groupBy] ||
'-'
);
};
export const getUsageResponseItems = (
data: UsageBreakdownResponse | null | undefined
) => {
const response = data as any;
return response?.items || response?.data?.items || [];
};
export const getUsageResponseSeries = (
data: UsageBreakdownResponse | null | undefined
) => {
const response = data as any;
return response?.series || response?.data?.series || [];
};
export const getUsageMetricValue = (item: any, metric: UsageMetric) => {
return Number(
item?.[metric] ??
item?.metrics?.[metric] ??
item?.summary?.[metric] ??
item?.value ??
0
);
};
export const aggregateUsageByGroup = (
data: UsageBreakdownResponse | null | undefined,
groupBy: UsageGroupBy,
metric: UsageMetric
): UsageChartDatum[] => {
const itemMap = new Map<string, number>();
const items = getUsageResponseItems(data);
items.forEach((item: BreakdownItem) => {
const name = buildUsageLabel(item, groupBy);
itemMap.set(
name,
(itemMap.get(name) || 0) + getUsageMetricValue(item, metric)
);
});
if (!items.length) {
getUsageResponseSeries(data).forEach((item: any) => {
const name = item.label || buildUsageLabel(item, groupBy);
const value = (item.timeline || []).reduce(
(sum: number, point: any) => {
return sum + getUsageMetricValue(point, metric);
},
getUsageMetricValue(item, metric)
);
itemMap.set(name, (itemMap.get(name) || 0) + value);
});
}
if (!itemMap.size) {
return [];
}
const hasPositiveValue = Array.from(itemMap.values()).some(
(value) => value > 0
);
if (!hasPositiveValue) {
return [];
}
return Array.from(itemMap.entries())
.filter(([, value]) => value > 0)
.map(([name, value]) => ({ name, value }))
.sort((a, b) => b.value - a.value)
.slice(0, 10);
};
export const getUsageSummaryData = (
dataList: Array<UsageBreakdownResponse | null | undefined>
) => {
return dataList.find((item) => {
const summary = item?.summary || (item as any)?.data?.summary;
return summary;
});
};
export const getUsageSummary = (
data: UsageBreakdownResponse | null | undefined
) => {
return data?.summary || (data as any)?.data?.summary;
};
export const toUsagePieData = (
data: UsageBreakdownResponse | null | undefined,
groupBy: UsageGroupBy,
metric: UsageMetric
): UsageChartDatum[] => {
return aggregateUsageByGroup(data, groupBy, metric);
};
export const toUsageRankData = (
data: UsageBreakdownResponse | null | undefined,
groupBy: UsageGroupBy,
seriesName: string,
color: string
) => {
const items = aggregateUsageByGroup(data, groupBy, 'total_tokens');
const names = items.map((item) => item.name);
return {
names,
series: [
{
name: seriesName,
color,
data: items.map((item) => ({
name: item.name,
value: item.value,
itemStyle: {
borderRadius: [2, 2, 2, 2]
}
}))
}
]
};
};