Skip to content

Commit 101a6e9

Browse files
c121914yuctrlz526
andauthored
Feat: ai proxy monitor (#4985)
* Aiproxy ModelBoard (#4983) * Aiproxy ModelBoard * Add components LineChartComponent and Make some revisions * perf: ai proxy dashboard * doc * remove invalid i18n * remove invalid i18n --------- Co-authored-by: Zhuangzai fa <[email protected]>
1 parent 01ff56b commit 101a6e9

File tree

10 files changed

+657
-10
lines changed

10 files changed

+657
-10
lines changed

docSite/content/zh-cn/docs/development/upgrading/4912.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ weight: 789
99

1010
## 🚀 新增内容
1111

12+
1. AI proxy 服务,支持以图表形式展示模型调用情况。
1213
1. 商业版支持知识库分块时,LLM 进行自动分段识别。
1314

1415
## ⚙️ 优化

packages/web/i18n/en/account_model.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"Hunyuan": "Tencent Hunyuan",
3+
"aipoint_usage": "AI points",
4+
"all": "All",
35
"api_key": "API key",
46
"azure": "Azure",
57
"base_url": "Base url",
@@ -16,13 +18,18 @@
1618
"confirm_delete_channel": "Confirm the deletion of the [{{name}}] channel?",
1719
"copy_model_id_success": "Copyed model id",
1820
"create_channel": "Added channels",
21+
"dashboard_error_calls": "Error Calls",
22+
"dashboard_model": "Model",
23+
"dashboard_points": "points",
24+
"dashboard_token_usage": "Tokens",
1925
"default_url": "Default address",
2026
"detail": "Detail",
2127
"duration": "Duration",
2228
"edit": "edit",
2329
"edit_channel": "Channel configuration",
2430
"enable_channel": "Enable",
2531
"forbid_channel": "Disabled",
32+
"input": "Input",
2633
"key_type": "API key format:",
2734
"log": "Call log",
2835
"log_detail": "Log details",
@@ -33,9 +40,14 @@
3340
"maxToken_tip": "Model max_tokens parameter",
3441
"max_temperature_tip": "If the model temperature parameter is not filled in, it means that the model does not support the temperature parameter.",
3542
"model": "Model",
43+
"model_error_rate": "Error rate",
44+
"model_error_request_times": "Number of failures",
3645
"model_name": "Model name",
46+
"model_request_times": "Request times",
3747
"model_test": "Model testing",
3848
"model_tokens": "Input/Output tokens",
49+
"monitoring": "Monitoring",
50+
"output": "Output",
3951
"request_at": "Request time",
4052
"request_duration": "Request duration: {{duration}}s",
4153
"retry_times": "Number of retry times",

packages/web/i18n/zh-CN/account_model.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"Hunyuan": "腾讯混元",
3+
"all": "全部",
34
"api_key": "API 密钥",
45
"azure": "微软 Azure",
56
"base_url": "代理地址",
@@ -16,13 +17,19 @@
1617
"confirm_delete_channel": "确认删除 【{{name}}】渠道?",
1718
"copy_model_id_success": "已复制模型id",
1819
"create_channel": "新增渠道",
20+
"aipoint_usage": "积分消耗",
21+
"dashboard_error_calls": "错误次数",
22+
"dashboard_model": "模型",
23+
"dashboard_points": "积分",
24+
"dashboard_token_usage": "Tokens 消耗",
1925
"default_url": "默认地址",
2026
"detail": "详情",
2127
"duration": "耗时",
2228
"edit": "编辑",
2329
"edit_channel": "渠道配置",
2430
"enable_channel": "启用",
2531
"forbid_channel": "禁用",
32+
"input": "输入",
2633
"key_type": "API key 格式: ",
2734
"log": "调用日志",
2835
"log_detail": "日志详情",
@@ -33,9 +40,14 @@
3340
"maxToken_tip": "模型 max_tokens 参数",
3441
"max_temperature_tip": "模型 temperature 参数,不填则代表模型不支持 temperature 参数。",
3542
"model": "模型",
43+
"model_error_rate": "失败率",
44+
"model_error_request_times": "失败次数",
3645
"model_name": "模型名",
46+
"model_request_times": "请求次数",
3747
"model_test": "模型测试",
3848
"model_tokens": "输入/输出 Tokens",
49+
"monitoring": "监控",
50+
"output": "输出",
3951
"request_at": "请求时间",
4052
"request_duration": "请求时长: {{duration}}s",
4153
"retry_times": "重试次数",

packages/web/i18n/zh-Hant/account_model.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{
22
"Hunyuan": "騰訊混元",
3+
"aipoint_usage": "積分消耗",
4+
"all": "全部",
35
"api_key": "API 金鑰",
46
"azure": "Azure",
57
"base_url": "代理地址",
@@ -16,13 +18,15 @@
1618
"confirm_delete_channel": "確認刪除【{{name}}】管道?",
1719
"copy_model_id_success": "已復制模型 id",
1820
"create_channel": "新增管道",
21+
"dashboard_token_usage": "Tokens 消耗",
1922
"default_url": "預設地址",
2023
"detail": "詳細資訊",
2124
"duration": "耗時",
2225
"edit": "編輯",
2326
"edit_channel": "管道設定",
2427
"enable_channel": "啟用",
2528
"forbid_channel": "停用",
29+
"input": "輸入",
2630
"key_type": "API key 格式:",
2731
"log": "呼叫日誌",
2832
"log_detail": "日誌詳細資訊",
@@ -33,9 +37,14 @@
3337
"maxToken_tip": "模型 max_tokens 參數",
3438
"max_temperature_tip": "模型 temperature 參數,不填則代表模型不支援 temperature 參數。",
3539
"model": "模型",
40+
"model_error_rate": "失敗率",
41+
"model_error_request_times": "失敗次數",
3642
"model_name": "模型名",
43+
"model_request_times": "請求次數",
3744
"model_test": "模型測試",
3845
"model_tokens": "輸入/輸出 Tokens",
46+
"monitoring": "監控",
47+
"output": "輸出",
3948
"request_at": "請求時間",
4049
"request_duration": "請求時長:{{duration}}s",
4150
"retry_times": "重試次數",

projects/app/src/global/aiproxy/type.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,13 @@ export type ChannelLogListItemType = {
5252
content?: string;
5353
retry_times?: number;
5454
};
55+
56+
export type DashboardDataItemType = {
57+
model: string;
58+
request_count: number;
59+
used_amount: number;
60+
exception_count: number;
61+
input_tokens?: number;
62+
output_tokens?: number;
63+
total_tokens?: number;
64+
};
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
import React, { useCallback, useMemo, useState } from 'react';
2+
import { Box, HStack, useTheme } from '@chakra-ui/react';
3+
import {
4+
ResponsiveContainer,
5+
AreaChart,
6+
Area,
7+
XAxis,
8+
YAxis,
9+
CartesianGrid,
10+
Tooltip,
11+
type TooltipProps
12+
} from 'recharts';
13+
import { type NameType, type ValueType } from 'recharts/types/component/DefaultTooltipContent';
14+
import { formatNumber } from '@fastgpt/global/common/math/tools';
15+
16+
type XAxisConfig = {
17+
dataKey: string;
18+
};
19+
20+
type LineConfig = {
21+
dataKey: string;
22+
name: string;
23+
color: string;
24+
gradient?: boolean;
25+
};
26+
27+
type TooltipItem = {
28+
label: string;
29+
dataKey: string;
30+
color: string;
31+
formatter?: (value: number) => string;
32+
customValue?: (data: any) => number;
33+
};
34+
35+
type LineChartComponentProps = {
36+
data: Record<string, any>[];
37+
title: string;
38+
HeaderRightChildren?: React.ReactNode;
39+
lines: LineConfig[];
40+
tooltipItems?: TooltipItem[];
41+
};
42+
43+
const CustomTooltip = ({
44+
active,
45+
payload,
46+
tooltipItems
47+
}: TooltipProps<ValueType, NameType> & { tooltipItems?: TooltipItem[] }) => {
48+
const data = payload?.[0]?.payload;
49+
50+
if (active && data && tooltipItems) {
51+
return (
52+
<Box bg={'white'} p={3} borderRadius={'md'} border={'base'} boxShadow={'sm'}>
53+
<Box fontSize={'sm'} color={'myGray.900'} mb={2}>
54+
{data.x}
55+
</Box>
56+
{tooltipItems.map((item, index) => {
57+
const value = (() => {
58+
if (item.customValue) {
59+
return item.customValue(data);
60+
} else {
61+
return data[item.dataKey];
62+
}
63+
})();
64+
65+
const displayValue = item.formatter ? item.formatter(value) : formatNumber(value);
66+
67+
return (
68+
<HStack key={index} fontSize={'sm'} _notLast={{ mb: 1 }}>
69+
<Box w={2} h={2} borderRadius={'full'} bg={item.color} />
70+
<Box>{item.label}</Box>
71+
<Box>{displayValue}</Box>
72+
</HStack>
73+
);
74+
})}
75+
</Box>
76+
);
77+
}
78+
return null;
79+
};
80+
81+
const LineChartComponent = ({
82+
data,
83+
title,
84+
HeaderRightChildren,
85+
lines,
86+
tooltipItems
87+
}: LineChartComponentProps) => {
88+
const theme = useTheme();
89+
90+
// Y-axis number formatter function
91+
const formatYAxisNumber = useCallback((value: number): string => {
92+
if (value >= 1000000) {
93+
return value / 1000000 + 'M';
94+
} else if (value >= 1000) {
95+
return value / 1000 + 'K';
96+
}
97+
return value.toString();
98+
}, []);
99+
100+
// Generate gradient definitions
101+
const gradientDefs = useMemo(() => {
102+
return (
103+
<defs>
104+
{lines.map((line, index) => (
105+
<linearGradient
106+
key={`gradient-${line.color}`}
107+
id={`gradient-${line.color}`}
108+
x1="0"
109+
y1="0"
110+
x2="0"
111+
y2="1"
112+
>
113+
<stop offset="0%" stopColor={line.color} stopOpacity={0.25} />
114+
<stop offset="100%" stopColor={line.color} stopOpacity={0.01} />
115+
</linearGradient>
116+
))}
117+
</defs>
118+
);
119+
}, [lines]);
120+
121+
return (
122+
<>
123+
<HStack mb={4} justifyContent={'space-between'} alignItems={'flex-start'}>
124+
<Box fontSize={'sm'} color={'myGray.900'} fontWeight={'medium'}>
125+
{title}
126+
</Box>
127+
{HeaderRightChildren && HeaderRightChildren}
128+
</HStack>
129+
<ResponsiveContainer width="100%" height={'100%'}>
130+
<AreaChart
131+
data={data}
132+
margin={{ top: 5, right: 30, left: 0, bottom: HeaderRightChildren ? 30 : 15 }}
133+
>
134+
{gradientDefs}
135+
<XAxis
136+
dataKey={'x'}
137+
tickMargin={10}
138+
tick={{ fontSize: '12px', color: theme.colors.myGray['500'], fontWeight: '500' }}
139+
interval={'preserveStartEnd'}
140+
/>
141+
<YAxis
142+
axisLine={false}
143+
tickSize={0}
144+
tickMargin={10}
145+
tick={{ fontSize: '12px', color: theme.colors.myGray['500'], fontWeight: '500' }}
146+
interval={'preserveStartEnd'}
147+
tickFormatter={formatYAxisNumber}
148+
/>
149+
<CartesianGrid strokeDasharray="3 3" horizontal={true} vertical={false} />
150+
{tooltipItems && <Tooltip content={<CustomTooltip tooltipItems={tooltipItems} />} />}
151+
{lines.map((line, index) => (
152+
<Area
153+
key={index}
154+
type="monotone"
155+
name={line.name}
156+
dataKey={line.dataKey}
157+
stroke={line.color}
158+
strokeWidth={2}
159+
fill={`url(#gradient-${line.color})`}
160+
dot={false}
161+
/>
162+
))}
163+
</AreaChart>
164+
</ResponsiveContainer>
165+
</>
166+
);
167+
};
168+
169+
export default LineChartComponent;

0 commit comments

Comments
 (0)