fix: dashboard colors, users rank
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
|
||||
import { converter, modeHsl, modeRgb, useMode } from 'culori/fn';
|
||||
|
||||
const toHsl = converter('hsl');
|
||||
|
||||
const DEFAULT_BRAND_HUE = 211; // brand color
|
||||
const COOL_HUE_START = 180;
|
||||
const COOL_HUE_END = 280;
|
||||
const COOL_HUE_RANGE = COOL_HUE_END - COOL_HUE_START;
|
||||
const GOLDEN_RATIO_CONJUGATE = 0.618033988749895;
|
||||
|
||||
function colorStringToHue(input?: string): number | undefined {
|
||||
if (!input) return undefined;
|
||||
const color = toHsl(input);
|
||||
if (!color || typeof color.h !== 'number') return undefined;
|
||||
return color.h;
|
||||
}
|
||||
|
||||
export default function useCoolColors() {
|
||||
// const brandHue = useMemo(
|
||||
// () => colorStringToHue(userSettings.colorPrimary) ?? DEFAULT_BRAND_HUE,
|
||||
// [userSettings.colorPrimary]
|
||||
// );
|
||||
useMode(modeRgb);
|
||||
useMode(modeHsl);
|
||||
|
||||
const brandHue = DEFAULT_BRAND_HUE;
|
||||
|
||||
return useMemoizedFn((count: number): string[] => {
|
||||
if (count <= 0) return [];
|
||||
|
||||
const colors: string[] = [];
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
let hue: number;
|
||||
|
||||
if (i === 0) {
|
||||
hue = brandHue - 5;
|
||||
} else {
|
||||
const offset = (i * GOLDEN_RATIO_CONJUGATE) % 1;
|
||||
hue = COOL_HUE_START + offset * COOL_HUE_RANGE;
|
||||
|
||||
if (Math.abs(hue - brandHue) < 5) {
|
||||
hue = (hue + 10) % COOL_HUE_END;
|
||||
}
|
||||
}
|
||||
|
||||
const s = i === 0 ? 100 : 75 + (i % 3) * 5;
|
||||
const l = i === 0 ? 50 : 55 + (i % 2) * 5;
|
||||
|
||||
colors.push(`hsl(${Math.round(hue)}, ${s}%, ${l}%)`);
|
||||
}
|
||||
|
||||
return colors;
|
||||
});
|
||||
}
|
||||
@@ -1,27 +1,10 @@
|
||||
import useCoolColors from '@/hooks/use-cool-colors';
|
||||
import { Chart } from '@gpustack/core-ui';
|
||||
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
||||
import { theme } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useEffect, useMemo, useRef } from 'react';
|
||||
|
||||
const COOL_HUE_START = 180;
|
||||
const COOL_HUE_END = 280;
|
||||
const COOL_HUE_RANGE = COOL_HUE_END - COOL_HUE_START;
|
||||
// golden angle (137.508) mod COOL_HUE_RANGE — coprime offset for even spread
|
||||
const HUE_STEP = 37.508;
|
||||
|
||||
export function generateCoolColors(count: number): string[] {
|
||||
if (count <= 0) return [];
|
||||
const colors: string[] = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const hue = COOL_HUE_START + ((i * HUE_STEP) % COOL_HUE_RANGE);
|
||||
const saturation = 55 + (i % 3) * 5;
|
||||
const lightness = 60 + (i % 2) * 6;
|
||||
colors.push(`hsl(${Math.round(hue)}, ${saturation}%, ${lightness}%)`);
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
export interface BarSeriesItem {
|
||||
name: string;
|
||||
data: any[];
|
||||
@@ -55,10 +38,11 @@ const BarChart: React.FC<BarChartProps> = (props) => {
|
||||
} = props;
|
||||
const { token } = theme.useToken();
|
||||
const chartRef = useRef<{ chart: any } | null>(null);
|
||||
const generateCoolColors = useCoolColors();
|
||||
|
||||
const dynamicColors = useMemo(
|
||||
() => generateCoolColors(seriesData.length),
|
||||
[seriesData.length]
|
||||
[seriesData.length, generateCoolColors]
|
||||
);
|
||||
|
||||
const options = useMemo(() => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import useCoolColors from '@/hooks/use-cool-colors';
|
||||
import { Chart } from '@gpustack/core-ui';
|
||||
import { formatLargeNumber } from '@gpustack/core-ui/utils';
|
||||
import { Empty, Spin, theme } from 'antd';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { generateCoolColors } from '../bar-chart';
|
||||
|
||||
export interface PieChartItem {
|
||||
name: string;
|
||||
@@ -30,11 +30,12 @@ const PieChart: React.FC<PieChartProps> = ({
|
||||
}) => {
|
||||
const { token } = theme.useToken();
|
||||
const chartRef = useRef<{ chart: any } | null>(null);
|
||||
const generateCoolColors = useCoolColors();
|
||||
|
||||
const colors = useMemo(() => {
|
||||
const generatedColors = generateCoolColors(data.length + colorOffset);
|
||||
return generatedColors.slice(colorOffset);
|
||||
}, [colorOffset, data.length]);
|
||||
}, [colorOffset, data.length, generateCoolColors]);
|
||||
|
||||
const options = useMemo(
|
||||
() => ({
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
import { activeModelsAtom } from '@/atoms/dashboard';
|
||||
import useQueryBreakdownList from '@/pages/usage/services/use-query-breakdown-list';
|
||||
import useQueryTimeSeriesData from '@/pages/usage/services/use-query-timeseries-data';
|
||||
import { formatLargeNumber } from '@/utils';
|
||||
import { PageTools } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Col, Row } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useContext, useEffect, useMemo } from 'react';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
baseColorMap,
|
||||
DashboardUsageCommonParams,
|
||||
getUsageSummary
|
||||
} from '../config';
|
||||
import { DashboardContext } from '../config/dashboard-context';
|
||||
import { DashboardUsageCommonParams } from '../config';
|
||||
import ApiRequestsByModel from './usage-charts/api-requests-by-model';
|
||||
import TokenUsageByModel from './usage-charts/token-usage-by-model';
|
||||
import TopTokenUsageByApiKey from './usage-charts/top-token-usage-by-api-key';
|
||||
@@ -26,9 +18,6 @@ const Section = styled.div`
|
||||
|
||||
const NewUsage = () => {
|
||||
const intl = useIntl();
|
||||
const activeModelsFromAtom = useAtomValue(activeModelsAtom);
|
||||
const { active_models: activeModelsFromContext = [] } =
|
||||
useContext(DashboardContext);
|
||||
const tokenByModelQuery = useQueryTimeSeriesData({
|
||||
key: 'tokenUsageByModelData'
|
||||
});
|
||||
@@ -54,10 +43,6 @@ const NewUsage = () => {
|
||||
[dateRange]
|
||||
);
|
||||
|
||||
const activeModels = activeModelsFromAtom.length
|
||||
? activeModelsFromAtom
|
||||
: activeModelsFromContext;
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
await Promise.all([
|
||||
@@ -66,15 +51,6 @@ const NewUsage = () => {
|
||||
metric: 'total_tokens',
|
||||
group_by: ['date', 'model'],
|
||||
sort_by: '-total_tokens'
|
||||
}),
|
||||
activeUsersQuery.fetchData({
|
||||
...dateRange,
|
||||
scope: 'all',
|
||||
group_by: ['user'],
|
||||
page: 1,
|
||||
perPage: 10,
|
||||
sort_by: '-total_tokens',
|
||||
filters: {}
|
||||
})
|
||||
]);
|
||||
};
|
||||
@@ -82,41 +58,6 @@ const NewUsage = () => {
|
||||
fetchData().catch(() => {});
|
||||
}, [commonParams, dateRange]);
|
||||
|
||||
const summaryCards = useMemo(() => {
|
||||
const summary = getUsageSummary(tokenByModelQuery.detailData) || {
|
||||
total_tokens: 0,
|
||||
api_requests: 0
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
label: formatLargeNumber(summary.total_tokens) as string,
|
||||
value: intl.formatMessage({ id: 'dashboard.tokens' }),
|
||||
color: baseColorMap.base
|
||||
},
|
||||
{
|
||||
label: formatLargeNumber(summary.api_requests) as string,
|
||||
value: intl.formatMessage({ id: 'dashboard.apirequest' }),
|
||||
color: baseColorMap.baseR1
|
||||
},
|
||||
{
|
||||
label: formatLargeNumber(activeModels.length) as string,
|
||||
value: intl.formatMessage({ id: 'dashboard.activeModels' }),
|
||||
color: baseColorMap.baseR2
|
||||
},
|
||||
{
|
||||
label: formatLargeNumber(activeUsersQuery.dataSource.total) as string,
|
||||
value: intl.formatMessage({ id: 'dashboard.activeUsers' }),
|
||||
color: baseColorMap.baseR3
|
||||
}
|
||||
];
|
||||
}, [
|
||||
activeModels.length,
|
||||
activeUsersQuery.dataSource.total,
|
||||
intl,
|
||||
tokenByModelQuery.detailData
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageTools
|
||||
@@ -143,10 +84,10 @@ const NewUsage = () => {
|
||||
<Section>
|
||||
<Row gutter={[20, 20]}>
|
||||
<Col xs={24} sm={24} md={24} lg={12}>
|
||||
<TopTokenUsageByUser commonParams={commonParams} height={450} />
|
||||
<TopTokenUsageByUser commonParams={commonParams} height={460} />
|
||||
</Col>
|
||||
<Col xs={24} sm={24} md={24} lg={12}>
|
||||
<TopTokenUsageByApiKey commonParams={commonParams} height={450} />
|
||||
<TopTokenUsageByApiKey commonParams={commonParams} height={460} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Section>
|
||||
|
||||
@@ -53,7 +53,7 @@ const ApiRequestsByModel: React.FC<ApiRequestsByModelProps> = ({
|
||||
data={chartData}
|
||||
height={usageChartHeight}
|
||||
loading={query.loading}
|
||||
colorOffset={1}
|
||||
colorOffset={5}
|
||||
total={total}
|
||||
/>
|
||||
</UsageChartCard>
|
||||
|
||||
@@ -38,7 +38,7 @@ const TokenUsageByModel: React.FC<TokenUsageByModelProps> = ({
|
||||
data={chartData}
|
||||
height={usageChartHeight}
|
||||
loading={loading}
|
||||
colorOffset={3}
|
||||
colorOffset={2}
|
||||
total={total}
|
||||
/>
|
||||
</UsageChartCard>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import BarChart, { generateCoolColors } from '@/pages/_components/bar-chart';
|
||||
import useCoolColors from '@/hooks/use-cool-colors';
|
||||
import BarChart from '@/pages/_components/bar-chart';
|
||||
import { BaseSelect, CardWrapper } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Segmented } from 'antd';
|
||||
@@ -78,6 +79,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
onGroupByChange,
|
||||
onGranularityChange
|
||||
} = props;
|
||||
const generateCoolColors = useCoolColors();
|
||||
|
||||
const labelFormatter = (v: any) => {
|
||||
if (granularity === 'month') {
|
||||
@@ -237,7 +239,7 @@ const DailyUsage: React.FC<DailyUsageProps> = (props) => {
|
||||
xAxisData: xAxis,
|
||||
legendData: dedupedLegend
|
||||
};
|
||||
}, [timeSeriesData, intl]);
|
||||
}, [timeSeriesData, intl, generateCoolColors]);
|
||||
|
||||
const handleOnGroupByChange = (value: string) => {
|
||||
onGroupByChange(value || null);
|
||||
|
||||
Reference in New Issue
Block a user