📊 feat(detail): add model consumption trend & call ranking charts

Introduce two new visualizations to the “Model Data Analysis” panel:

1. Model Consumption Trend (line chart)
   • Added `spec_model_line` state and legend support.
   • Calculates per-model counts over time and updates via `updateChartData`.
2. Model Call Ranking (bar chart)
   • Added `spec_rank_bar` state with `seriesField` and legend enabled.
   • Ranks models by total call count.

Additional changes:
• Extended tab navigation with two new `TabPane`s and adjusted chart rendering logic.
• Swapped icons/texts to match new chart purposes.
• Reused existing color mapping to ensure consistent palette.

No breaking changes; UI now offers richer insights into model usage patterns.
This commit is contained in:
t0ng7u
2025-07-05 00:37:05 +08:00
parent 3049ad47e5
commit d40fb68500
2 changed files with 153 additions and 4 deletions

View File

@@ -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",

View File

@@ -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('消耗分布')}
</span>
} itemKey="1" />
<TabPane tab={
<span>
<IconPulse />
{t('消耗趋势')}
</span>
} itemKey="2" />
<TabPane tab={
<span>
<IconPieChart2Stroked />
{t('调用次数分布')}
</span>
} itemKey="2" />
} itemKey="3" />
<TabPane tab={
<span>
<IconHistogram />
{t('调用次数排行')}
</span>
} itemKey="4" />
</Tabs>
</div>
}
>
<div style={{ height: 400 }}>
{activeChartTab === '1' ? (
{activeChartTab === '1' && (
<VChart
spec={spec_line}
option={CHART_CONFIG}
/>
) : (
)}
{activeChartTab === '2' && (
<VChart
spec={spec_model_line}
option={CHART_CONFIG}
/>
)}
{activeChartTab === '3' && (
<VChart
spec={spec_pie}
option={CHART_CONFIG}
/>
)}
{activeChartTab === '4' && (
<VChart
spec={spec_rank_bar}
option={CHART_CONFIG}
/>
)}
</div>
</Card>