| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { useState, useCallback, useEffect } from 'react'; |
| import { initVChartSemiTheme } from '@visactor/vchart-semi-theme'; |
| import { |
| modelColorMap, |
| renderNumber, |
| renderQuota, |
| modelToColor, |
| getQuotaWithUnit, |
| } from '../../helpers'; |
| import { |
| processRawData, |
| calculateTrendData, |
| aggregateDataByTimeAndModel, |
| generateChartTimePoints, |
| updateChartSpec, |
| updateMapValue, |
| initializeMaps, |
| } from '../../helpers/dashboard'; |
|
|
| export const useDashboardCharts = ( |
| dataExportDefaultTime, |
| setTrendData, |
| setConsumeQuota, |
| setTimes, |
| setConsumeTokens, |
| setPieData, |
| setLineData, |
| setModelColors, |
| t, |
| ) => { |
| |
| const [spec_pie, setSpecPie] = useState({ |
| type: 'pie', |
| data: [ |
| { |
| id: 'id0', |
| values: [{ type: 'null', value: '0' }], |
| }, |
| ], |
| outerRadius: 0.8, |
| innerRadius: 0.5, |
| padAngle: 0.6, |
| valueField: 'value', |
| categoryField: 'type', |
| pie: { |
| style: { |
| cornerRadius: 10, |
| }, |
| state: { |
| hover: { |
| outerRadius: 0.85, |
| stroke: '#000', |
| lineWidth: 1, |
| }, |
| selected: { |
| outerRadius: 0.85, |
| stroke: '#000', |
| lineWidth: 1, |
| }, |
| }, |
| }, |
| title: { |
| visible: true, |
| text: t('模型调用次数占比'), |
| subtext: `${t('总计')}:${renderNumber(0)}`, |
| }, |
| legends: { |
| visible: true, |
| orient: 'left', |
| }, |
| label: { |
| visible: true, |
| }, |
| tooltip: { |
| mark: { |
| content: [ |
| { |
| key: (datum) => datum['type'], |
| value: (datum) => renderNumber(datum['value']), |
| }, |
| ], |
| }, |
| }, |
| color: { |
| specified: modelColorMap, |
| }, |
| }); |
|
|
| const [spec_line, setSpecLine] = useState({ |
| type: 'bar', |
| data: [ |
| { |
| id: 'barData', |
| values: [], |
| }, |
| ], |
| xField: 'Time', |
| yField: 'Usage', |
| seriesField: 'Model', |
| stack: true, |
| legends: { |
| visible: true, |
| selectMode: 'single', |
| }, |
| title: { |
| visible: true, |
| text: t('模型消耗分布'), |
| subtext: `${t('总计')}:${renderQuota(0, 2)}`, |
| }, |
| bar: { |
| state: { |
| hover: { |
| stroke: '#000', |
| lineWidth: 1, |
| }, |
| }, |
| }, |
| tooltip: { |
| mark: { |
| content: [ |
| { |
| key: (datum) => datum['Model'], |
| value: (datum) => renderQuota(datum['rawQuota'] || 0, 4), |
| }, |
| ], |
| }, |
| dimension: { |
| content: [ |
| { |
| key: (datum) => datum['Model'], |
| value: (datum) => datum['rawQuota'] || 0, |
| }, |
| ], |
| updateContent: (array) => { |
| array.sort((a, b) => b.value - a.value); |
| let sum = 0; |
| for (let i = 0; i < array.length; i++) { |
| if (array[i].key == '其他') { |
| continue; |
| } |
| let value = parseFloat(array[i].value); |
| if (isNaN(value)) { |
| value = 0; |
| } |
| if (array[i].datum && array[i].datum.TimeSum) { |
| sum = array[i].datum.TimeSum; |
| } |
| array[i].value = renderQuota(value, 4); |
| } |
| array.unshift({ |
| key: t('总计'), |
| value: renderQuota(sum, 4), |
| }); |
| return array; |
| }, |
| }, |
| }, |
| color: { |
| specified: modelColorMap, |
| }, |
| }); |
|
|
| |
| const [spec_model_line, setSpecModelLine] = useState({ |
| type: 'line', |
| data: [ |
| { |
| id: 'lineData', |
| values: [], |
| }, |
| ], |
| xField: 'Time', |
| yField: 'Count', |
| seriesField: 'Model', |
| legends: { |
| visible: true, |
| selectMode: 'single', |
| }, |
| title: { |
| visible: true, |
| text: t('模型消耗趋势'), |
| subtext: '', |
| }, |
| tooltip: { |
| mark: { |
| content: [ |
| { |
| key: (datum) => datum['Model'], |
| value: (datum) => renderNumber(datum['Count']), |
| }, |
| ], |
| }, |
| }, |
| color: { |
| specified: modelColorMap, |
| }, |
| }); |
|
|
| |
| const [spec_rank_bar, setSpecRankBar] = useState({ |
| type: 'bar', |
| data: [ |
| { |
| id: 'rankData', |
| values: [], |
| }, |
| ], |
| xField: 'Model', |
| yField: 'Count', |
| seriesField: 'Model', |
| legends: { |
| visible: true, |
| selectMode: 'single', |
| }, |
| title: { |
| visible: true, |
| text: t('模型调用次数排行'), |
| subtext: '', |
| }, |
| bar: { |
| state: { |
| hover: { |
| stroke: '#000', |
| lineWidth: 1, |
| }, |
| }, |
| }, |
| tooltip: { |
| mark: { |
| content: [ |
| { |
| key: (datum) => datum['Model'], |
| value: (datum) => renderNumber(datum['Count']), |
| }, |
| ], |
| }, |
| }, |
| color: { |
| specified: modelColorMap, |
| }, |
| }); |
|
|
| |
| const generateModelColors = useCallback((uniqueModels, modelColors) => { |
| const newModelColors = {}; |
| Array.from(uniqueModels).forEach((modelName) => { |
| newModelColors[modelName] = |
| modelColorMap[modelName] || |
| modelColors[modelName] || |
| modelToColor(modelName); |
| }); |
| return newModelColors; |
| }, []); |
|
|
| const updateChartData = useCallback( |
| (data) => { |
| const processedData = processRawData( |
| data, |
| dataExportDefaultTime, |
| initializeMaps, |
| updateMapValue, |
| ); |
|
|
| const { |
| totalQuota, |
| totalTimes, |
| totalTokens, |
| uniqueModels, |
| timePoints, |
| timeQuotaMap, |
| timeTokensMap, |
| timeCountMap, |
| } = processedData; |
|
|
| const trendDataResult = calculateTrendData( |
| timePoints, |
| timeQuotaMap, |
| timeTokensMap, |
| timeCountMap, |
| dataExportDefaultTime, |
| ); |
| setTrendData(trendDataResult); |
|
|
| const newModelColors = generateModelColors(uniqueModels, {}); |
| setModelColors(newModelColors); |
|
|
| const aggregatedData = aggregateDataByTimeAndModel( |
| data, |
| dataExportDefaultTime, |
| ); |
|
|
| const modelTotals = new Map(); |
| for (let [_, value] of aggregatedData) { |
| updateMapValue(modelTotals, value.model, value.count); |
| } |
|
|
| const newPieData = Array.from(modelTotals) |
| .map(([model, count]) => ({ |
| type: model, |
| value: count, |
| })) |
| .sort((a, b) => b.value - a.value); |
|
|
| const chartTimePoints = generateChartTimePoints( |
| aggregatedData, |
| data, |
| dataExportDefaultTime, |
| ); |
|
|
| let newLineData = []; |
|
|
| chartTimePoints.forEach((time) => { |
| let timeData = Array.from(uniqueModels).map((model) => { |
| const key = `${time}-${model}`; |
| const aggregated = aggregatedData.get(key); |
| return { |
| Time: time, |
| Model: model, |
| rawQuota: aggregated?.quota || 0, |
| Usage: aggregated?.quota |
| ? getQuotaWithUnit(aggregated.quota, 4) |
| : 0, |
| }; |
| }); |
|
|
| const timeSum = timeData.reduce((sum, item) => sum + item.rawQuota, 0); |
| timeData.sort((a, b) => b.rawQuota - a.rawQuota); |
| timeData = timeData.map((item) => ({ ...item, TimeSum: timeSum })); |
| newLineData.push(...timeData); |
| }); |
|
|
| newLineData.sort((a, b) => a.Time.localeCompare(b.Time)); |
|
|
| updateChartSpec( |
| setSpecPie, |
| newPieData, |
| `${t('总计')}:${renderNumber(totalTimes)}`, |
| newModelColors, |
| 'id0', |
| ); |
|
|
| updateChartSpec( |
| setSpecLine, |
| newLineData, |
| `${t('总计')}:${renderQuota(totalQuota, 2)}`, |
| newModelColors, |
| 'barData', |
| ); |
|
|
| |
| let modelLineData = []; |
| chartTimePoints.forEach((time) => { |
| const timeData = Array.from(uniqueModels).map((model) => { |
| const key = `${time}-${model}`; |
| const aggregated = aggregatedData.get(key); |
| return { |
| Time: time, |
| Model: model, |
| Count: aggregated?.count || 0, |
| }; |
| }); |
| modelLineData.push(...timeData); |
| }); |
| modelLineData.sort((a, b) => a.Time.localeCompare(b.Time)); |
|
|
| |
| const rankData = Array.from(modelTotals) |
| .map(([model, count]) => ({ |
| Model: model, |
| Count: count, |
| })) |
| .sort((a, b) => b.Count - a.Count); |
|
|
| updateChartSpec( |
| setSpecModelLine, |
| modelLineData, |
| `${t('总计')}:${renderNumber(totalTimes)}`, |
| newModelColors, |
| 'lineData', |
| ); |
|
|
| updateChartSpec( |
| setSpecRankBar, |
| rankData, |
| `${t('总计')}:${renderNumber(totalTimes)}`, |
| newModelColors, |
| 'rankData', |
| ); |
|
|
| setPieData(newPieData); |
| setLineData(newLineData); |
| setConsumeQuota(totalQuota); |
| setTimes(totalTimes); |
| setConsumeTokens(totalTokens); |
| }, |
| [ |
| dataExportDefaultTime, |
| setTrendData, |
| generateModelColors, |
| setModelColors, |
| setPieData, |
| setLineData, |
| setConsumeQuota, |
| setTimes, |
| setConsumeTokens, |
| t, |
| ], |
| ); |
|
|
| |
| useEffect(() => { |
| initVChartSemiTheme({ |
| isWatchingThemeSwitch: true, |
| }); |
| }, []); |
|
|
| return { |
| |
| spec_pie, |
| spec_line, |
| spec_model_line, |
| spec_rank_bar, |
|
|
| |
| updateChartData, |
| generateModelColors, |
| }; |
| }; |
|
|