diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 0d49d3ba..80f7f3cd 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -876,7 +876,7 @@ "加载token失败": "Failed to load token", "配置聊天": "Configure chat", "模型消耗分布": "Model consumption distribution", - "模型调用次数占比": "Proportion of model calls", + "模型调用次数占比": "Model call ratio", "用户消耗分布": "User consumption distribution", "时间粒度": "Time granularity", "天": "day", @@ -1119,6 +1119,10 @@ "平均TPM": "Average TPM", "消耗分布": "Consumption distribution", "调用次数分布": "Models call distribution", + "消耗趋势": "Consumption trend", + "模型消耗趋势": "Model consumption trend", + "调用次数排行": "Models call ranking", + "模型调用次数排行": "Model call ranking", "添加渠道": "Add channel", "测试所有通道": "Test all channels", "删除禁用通道": "Delete disabled channels", diff --git a/web/src/pages/Detail/index.js b/web/src/pages/Detail/index.js index 2463db70..f455abb6 100644 --- a/web/src/pages/Detail/index.js +++ b/web/src/pages/Detail/index.js @@ -366,6 +366,86 @@ const Detail = (props) => { }, }); + // 模型消耗趋势折线图 + 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, + }, + }); + // ========== Hooks - Memoized Values ========== const performanceMetrics = useMemo(() => { const timeDiff = (Date.parse(end_timestamp) - Date.parse(start_timestamp)) / 60000; @@ -853,6 +933,46 @@ const Detail = (props) => { '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); @@ -1122,28 +1242,53 @@ const Detail = (props) => { {t('消耗分布')} } itemKey="1" /> + + + {t('消耗趋势')} + + } itemKey="2" /> {t('调用次数分布')} - } itemKey="2" /> + } itemKey="3" /> + + + {t('调用次数排行')} + + } itemKey="4" /> } >
- {activeChartTab === '1' ? ( + {activeChartTab === '1' && ( - ) : ( + )} + {activeChartTab === '2' && ( + + )} + {activeChartTab === '3' && ( )} + {activeChartTab === '4' && ( + + )}