Compare commits
2
Commits
v2.2.2-lofyer
...
v2.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2767f883fc | ||
|
|
e98651da2b |
@@ -57,7 +57,7 @@ const UsageByModel: React.FC<UsageByModelProps> = ({
|
|||||||
.fetchData({
|
.fetchData({
|
||||||
...commonParams,
|
...commonParams,
|
||||||
metric: 'total_tokens',
|
metric: 'total_tokens',
|
||||||
group_by: ['date', 'route'],
|
group_by: ['route'],
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
sort_by: '-total_tokens'
|
sort_by: '-total_tokens'
|
||||||
@@ -67,7 +67,7 @@ const UsageByModel: React.FC<UsageByModelProps> = ({
|
|||||||
.fetchData({
|
.fetchData({
|
||||||
...commonParams,
|
...commonParams,
|
||||||
metric: 'api_requests',
|
metric: 'api_requests',
|
||||||
group_by: ['date', 'route'],
|
group_by: ['route'],
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
sort_by: '-api_requests'
|
sort_by: '-api_requests'
|
||||||
|
|||||||
@@ -225,29 +225,77 @@ export const toUsagePieData = (
|
|||||||
return aggregateUsageByGroup(data, groupBy, metric);
|
return aggregateUsageByGroup(data, groupBy, metric);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toUsageRankData = (
|
export type UsageTokenMetric = 'input_tokens' | 'output_tokens';
|
||||||
|
|
||||||
|
export interface UsageTokenSeriesDef {
|
||||||
|
name: string;
|
||||||
|
key: UsageTokenMetric;
|
||||||
|
color: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggregates each group's input/output tokens into a stacked HBarChart shape,
|
||||||
|
// so a single bar shows prompt (input) and completion (output) tokens side by
|
||||||
|
// side. Rows sharing a group (e.g. the same user across dates) are summed, then
|
||||||
|
// ranked by combined tokens and capped at the top 10.
|
||||||
|
export const toUsageTokenBreakdownData = (
|
||||||
data: UsageBreakdownResponse | null | undefined,
|
data: UsageBreakdownResponse | null | undefined,
|
||||||
groupBy: UsageGroupBy,
|
groupBy: UsageGroupBy,
|
||||||
seriesName: string,
|
seriesDefs: UsageTokenSeriesDef[]
|
||||||
color: string
|
|
||||||
) => {
|
) => {
|
||||||
const items = aggregateUsageByGroup(data, groupBy, 'total_tokens');
|
const itemMap = new Map<string, { total: number; values: number[] }>();
|
||||||
const names = items.map((item) => item.name);
|
const items = getUsageResponseItems(data);
|
||||||
|
|
||||||
|
items.forEach((item: BreakdownItem) => {
|
||||||
|
const name = buildUsageLabel(item, groupBy);
|
||||||
|
const entry = itemMap.get(name) || {
|
||||||
|
total: 0,
|
||||||
|
values: seriesDefs.map(() => 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
seriesDefs.forEach((def, index) => {
|
||||||
|
const value = Number((item as any)?.[def.key] ?? 0);
|
||||||
|
entry.values[index] += value;
|
||||||
|
entry.total += value;
|
||||||
|
});
|
||||||
|
|
||||||
|
itemMap.set(name, entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
const ranked = Array.from(itemMap.entries())
|
||||||
|
.filter(([, entry]) => entry.total > 0)
|
||||||
|
.sort((a, b) => b[1].total - a[1].total)
|
||||||
|
.slice(0, 10);
|
||||||
|
|
||||||
|
const names = ranked.map(([name]) => name);
|
||||||
|
|
||||||
|
// Round only the outer ends of the stacked bar; the seam where the input and
|
||||||
|
// output segments meet stays square ([topLeft, topRight, bottomRight, bottomLeft]).
|
||||||
|
const radius = 2;
|
||||||
|
const getBorderRadius = (index: number, count: number) => {
|
||||||
|
if (count <= 1) {
|
||||||
|
return [radius, radius, radius, radius];
|
||||||
|
}
|
||||||
|
if (index === 0) {
|
||||||
|
return [radius, 0, 0, radius];
|
||||||
|
}
|
||||||
|
if (index === count - 1) {
|
||||||
|
return [0, radius, radius, 0];
|
||||||
|
}
|
||||||
|
return [0, 0, 0, 0];
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
names,
|
names,
|
||||||
series: [
|
series: seriesDefs.map((def, index) => ({
|
||||||
{
|
name: def.name,
|
||||||
name: seriesName,
|
color: def.color,
|
||||||
color,
|
data: ranked.map(([name, entry]) => ({
|
||||||
data: items.map((item) => ({
|
name,
|
||||||
name: item.name,
|
value: entry.values[index],
|
||||||
value: item.value,
|
itemStyle: {
|
||||||
itemStyle: {
|
borderRadius: getBorderRadius(index, seriesDefs.length)
|
||||||
borderRadius: [2, 2, 2, 2]
|
}
|
||||||
}
|
}))
|
||||||
}))
|
}))
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,24 @@
|
|||||||
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
|
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
|
||||||
import { useIntl } from '@umijs/max';
|
|
||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
baseColorMap,
|
baseColorMap,
|
||||||
DashboardUsageCommonParams,
|
DashboardUsageCommonParams,
|
||||||
toUsageRankData
|
toUsageTokenBreakdownData
|
||||||
} from '../config';
|
} from '../config';
|
||||||
|
|
||||||
export default function useTopTokenUsageByUser(
|
export default function useTopTokenUsageByUser(
|
||||||
commonParams: DashboardUsageCommonParams
|
commonParams: DashboardUsageCommonParams
|
||||||
) {
|
) {
|
||||||
const intl = useIntl();
|
|
||||||
const query = useQueryTimeSeriesData({
|
const query = useQueryTimeSeriesData({
|
||||||
key: 'topTokenUsageByUserData'
|
key: 'topTokenUsageByUserData'
|
||||||
});
|
});
|
||||||
const tokenUsageText = intl.formatMessage({ id: 'dashboard.tokens' });
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
query
|
query
|
||||||
.fetchData({
|
.fetchData({
|
||||||
...commonParams,
|
...commonParams,
|
||||||
metric: 'total_tokens',
|
metric: 'total_tokens',
|
||||||
group_by: ['date', 'user'],
|
group_by: ['user'],
|
||||||
page: 1,
|
page: 1,
|
||||||
perPage: 10,
|
perPage: 10,
|
||||||
sort_by: '-total_tokens'
|
sort_by: '-total_tokens'
|
||||||
@@ -31,13 +28,19 @@ export default function useTopTokenUsageByUser(
|
|||||||
|
|
||||||
const rankData = useMemo(
|
const rankData = useMemo(
|
||||||
() =>
|
() =>
|
||||||
toUsageRankData(
|
toUsageTokenBreakdownData(query.detailData, 'user', [
|
||||||
query.detailData,
|
{
|
||||||
'user',
|
name: 'Prompt Tokens',
|
||||||
tokenUsageText,
|
key: 'input_tokens',
|
||||||
baseColorMap.base
|
color: baseColorMap.base
|
||||||
),
|
},
|
||||||
[query.detailData, tokenUsageText]
|
{
|
||||||
|
name: 'Completion Tokens',
|
||||||
|
key: 'output_tokens',
|
||||||
|
color: baseColorMap.baseR3
|
||||||
|
}
|
||||||
|
]),
|
||||||
|
[query.detailData]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user