perf: ai proxy log remove retry log;perf: workflow type auto parse;add chunk spliter test (#4296)

* sync collection

* remove lock

* perf: workflow type auto parse

* add chunk spliter test

* perf: ai proxy log remove retry log

* udpate ai proxy field
This commit is contained in:
Archer 2025-03-24 17:16:49 +08:00 committed by archer
parent 2fcf421672
commit 6ea57e4609
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
12 changed files with 475 additions and 18 deletions

View File

@ -26,6 +26,9 @@ weight: 799
3. 无 SSL 证书时复制失败,会提示弹窗用于手动复制。 3. 无 SSL 证书时复制失败,会提示弹窗用于手动复制。
4. FastGPT 未内置 ai proxy 渠道时,也能正常展示其名称。 4. FastGPT 未内置 ai proxy 渠道时,也能正常展示其名称。
5. 升级 nextjs 版本至 14.2.25。 5. 升级 nextjs 版本至 14.2.25。
6. 工作流节点数组字符串类型,自动适配 string 输入。
7. 工作流节点数组类型,自动进行 JSON parse 解析 string 输入。
8. AI proxy 日志优化,去除重试失败的日志,仅保留最后一份错误日志。
## 🐛 修复 ## 🐛 修复

View File

@ -20,6 +20,7 @@ export enum WorkflowIOValueTypeEnum {
number = 'number', number = 'number',
boolean = 'boolean', boolean = 'boolean',
object = 'object', object = 'object',
arrayString = 'arrayString', arrayString = 'arrayString',
arrayNumber = 'arrayNumber', arrayNumber = 'arrayNumber',
arrayBoolean = 'arrayBoolean', arrayBoolean = 'arrayBoolean',

View File

@ -78,7 +78,7 @@ export const Input_Template_Text_Quote: FlowNodeInputItemType = {
export const Input_Template_File_Link: FlowNodeInputItemType = { export const Input_Template_File_Link: FlowNodeInputItemType = {
key: NodeInputKeyEnum.fileUrlList, key: NodeInputKeyEnum.fileUrlList,
renderTypeList: [FlowNodeInputTypeEnum.reference], renderTypeList: [FlowNodeInputTypeEnum.reference, FlowNodeInputTypeEnum.input],
label: i18nT('app:workflow.user_file_input'), label: i18nT('app:workflow.user_file_input'),
debugLabel: i18nT('app:workflow.user_file_input'), debugLabel: i18nT('app:workflow.user_file_input'),
description: i18nT('app:workflow.user_file_input_desc'), description: i18nT('app:workflow.user_file_input_desc'),

View File

@ -117,6 +117,9 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
return Boolean(value); return Boolean(value);
} }
try { try {
if (WorkflowIOValueTypeEnum.arrayString && typeof value === 'string') {
return [value];
}
if ( if (
type && type &&
[ [
@ -124,7 +127,12 @@ export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
WorkflowIOValueTypeEnum.chatHistory, WorkflowIOValueTypeEnum.chatHistory,
WorkflowIOValueTypeEnum.datasetQuote, WorkflowIOValueTypeEnum.datasetQuote,
WorkflowIOValueTypeEnum.selectApp, WorkflowIOValueTypeEnum.selectApp,
WorkflowIOValueTypeEnum.selectDataset WorkflowIOValueTypeEnum.selectDataset,
WorkflowIOValueTypeEnum.arrayString,
WorkflowIOValueTypeEnum.arrayNumber,
WorkflowIOValueTypeEnum.arrayBoolean,
WorkflowIOValueTypeEnum.arrayObject,
WorkflowIOValueTypeEnum.arrayAny
].includes(type) && ].includes(type) &&
typeof value !== 'object' typeof value !== 'object'
) { ) {

View File

@ -37,6 +37,7 @@
"model_tokens": "Input/Output tokens", "model_tokens": "Input/Output tokens",
"request_at": "Request time", "request_at": "Request time",
"request_duration": "Request duration: {{duration}}s", "request_duration": "Request duration: {{duration}}s",
"retry_times": "Number of retry times",
"running_test": "In testing", "running_test": "In testing",
"search_model": "Search for models", "search_model": "Search for models",
"select_channel": "Select a channel name", "select_channel": "Select a channel name",

View File

@ -37,6 +37,7 @@
"model_tokens": "输入/输出 Tokens", "model_tokens": "输入/输出 Tokens",
"request_at": "请求时间", "request_at": "请求时间",
"request_duration": "请求时长: {{duration}}s", "request_duration": "请求时长: {{duration}}s",
"retry_times": "重试次数",
"running_test": "测试中", "running_test": "测试中",
"search_model": "搜索模型", "search_model": "搜索模型",
"select_channel": "选择渠道名", "select_channel": "选择渠道名",

View File

@ -35,6 +35,7 @@
"model_tokens": "輸入/輸出 Tokens", "model_tokens": "輸入/輸出 Tokens",
"request_at": "請求時間", "request_at": "請求時間",
"request_duration": "請求時長: {{duration}}s", "request_duration": "請求時長: {{duration}}s",
"retry_times": "重試次數",
"running_test": "測試中", "running_test": "測試中",
"search_model": "搜索模型", "search_model": "搜索模型",
"select_channel": "選擇渠道名", "select_channel": "選擇渠道名",

View File

@ -30,6 +30,13 @@ export type CreateChannelProps = {
}; };
// Log // Log
export type ChannelLogUsageType = {
cache_creation_tokens?: number;
cached_tokens?: number;
input_tokens?: number;
output_tokens?: number;
total_tokens?: number;
};
export type ChannelLogListItemType = { export type ChannelLogListItemType = {
token_name: string; token_name: string;
model: string; model: string;
@ -40,8 +47,8 @@ export type ChannelLogListItemType = {
created_at: number; created_at: number;
request_at: number; request_at: number;
code: number; code: number;
prompt_tokens: number; usage?: ChannelLogUsageType;
completion_tokens: number;
endpoint: string; endpoint: string;
content?: string; content?: string;
retry_times?: number;
}; };

View File

@ -33,6 +33,7 @@ import { formatTime2YMDHMS } from '@fastgpt/global/common/string/time';
import MyModal from '@fastgpt/web/components/common/MyModal'; import MyModal from '@fastgpt/web/components/common/MyModal';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
import { ChannelLogUsageType } from '@/global/aiproxy/type';
type LogDetailType = { type LogDetailType = {
id: number; id: number;
@ -42,10 +43,10 @@ type LogDetailType = {
duration: number; duration: number;
request_at: string; request_at: string;
code: number; code: number;
prompt_tokens: number; usage?: ChannelLogUsageType;
completion_tokens: number;
endpoint: string; endpoint: string;
retry_times?: number;
content?: string; content?: string;
request_body?: string; request_body?: string;
response_body?: string; response_body?: string;
@ -159,8 +160,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
duration: durationSecond, duration: durationSecond,
request_at: formatTime2YMDHMS(item.request_at), request_at: formatTime2YMDHMS(item.request_at),
code: item.code, code: item.code,
prompt_tokens: item.prompt_tokens, usage: item.usage,
completion_tokens: item.completion_tokens,
request_id: item.request_id, request_id: item.request_id,
endpoint: item.endpoint, endpoint: item.endpoint,
content: item.content content: item.content
@ -260,7 +260,7 @@ const ChannelLog = ({ Tab }: { Tab: React.ReactNode }) => {
<Td>{item.channelName}</Td> <Td>{item.channelName}</Td>
<Td>{item.model}</Td> <Td>{item.model}</Td>
<Td> <Td>
{item.prompt_tokens} / {item.completion_tokens} {item.usage?.input_tokens} / {item.usage?.output_tokens}
</Td> </Td>
<Td color={item.duration > 10 ? 'red.600' : ''}>{item.duration.toFixed(2)}s</Td> <Td color={item.duration > 10 ? 'red.600' : ''}>{item.duration.toFixed(2)}s</Td>
<Td color={item.code === 200 ? 'green.600' : 'red.600'}> <Td color={item.code === 200 ? 'green.600' : 'red.600'}>
@ -297,6 +297,7 @@ const LogDetail = ({ data, onClose }: { data: LogDetailType; onClose: () => void
const { t } = useTranslation(); const { t } = useTranslation();
const { data: detailData } = useRequest2( const { data: detailData } = useRequest2(
async () => { async () => {
console.log(data);
if (data.code === 200) return data; if (data.code === 200) return data;
try { try {
const res = await getLogDetail(data.id); const res = await getLogDetail(data.id);
@ -363,7 +364,7 @@ const LogDetail = ({ data, onClose }: { data: LogDetailType; onClose: () => void
<Title>RequestID</Title> <Title>RequestID</Title>
<Container>{detailData?.request_id}</Container> <Container>{detailData?.request_id}</Container>
</GridItem> </GridItem>
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px"> <GridItem display={'flex'} borderBottomWidth="1px">
<Title>{t('account_model:channel_status')}</Title> <Title>{t('account_model:channel_status')}</Title>
<Container color={detailData.code === 200 ? 'green.600' : 'red.600'}> <Container color={detailData.code === 200 ? 'green.600' : 'red.600'}>
{detailData?.code} {detailData?.code}
@ -373,7 +374,7 @@ const LogDetail = ({ data, onClose }: { data: LogDetailType; onClose: () => void
<Title>Endpoint</Title> <Title>Endpoint</Title>
<Container>{detailData?.endpoint}</Container> <Container>{detailData?.endpoint}</Container>
</GridItem> </GridItem>
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px"> <GridItem display={'flex'} borderBottomWidth="1px">
<Title>{t('account_model:channel_name')}</Title> <Title>{t('account_model:channel_name')}</Title>
<Container>{detailData?.channelName}</Container> <Container>{detailData?.channelName}</Container>
</GridItem> </GridItem>
@ -381,7 +382,7 @@ const LogDetail = ({ data, onClose }: { data: LogDetailType; onClose: () => void
<Title>{t('account_model:request_at')}</Title> <Title>{t('account_model:request_at')}</Title>
<Container>{detailData?.request_at}</Container> <Container>{detailData?.request_at}</Container>
</GridItem> </GridItem>
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px"> <GridItem display={'flex'} borderBottomWidth="1px">
<Title>{t('account_model:duration')}</Title> <Title>{t('account_model:duration')}</Title>
<Container>{detailData?.duration.toFixed(2)}s</Container> <Container>{detailData?.duration.toFixed(2)}s</Container>
</GridItem> </GridItem>
@ -389,20 +390,26 @@ const LogDetail = ({ data, onClose }: { data: LogDetailType; onClose: () => void
<Title>{t('account_model:model')}</Title> <Title>{t('account_model:model')}</Title>
<Container>{detailData?.model}</Container> <Container>{detailData?.model}</Container>
</GridItem> </GridItem>
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px"> <GridItem display={'flex'} borderBottomWidth="1px">
<Title flex={'0 0 150px'}>{t('account_model:model_tokens')}</Title> <Title flex={'0 0 150px'}>{t('account_model:model_tokens')}</Title>
<Container> <Container>
{detailData?.prompt_tokens} / {detailData?.completion_tokens} {detailData?.usage?.input_tokens} / {detailData?.usage?.output_tokens}
</Container> </Container>
</GridItem> </GridItem>
{detailData?.retry_times !== undefined && (
<GridItem display={'flex'} borderBottomWidth="1px" colSpan={2}>
<Title>{t('account_model:retry_times')}</Title>
<Container>{detailData?.retry_times}</Container>
</GridItem>
)}
{detailData?.content && ( {detailData?.content && (
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px" colSpan={2}> <GridItem display={'flex'} borderBottomWidth="1px" colSpan={2}>
<Title>Content</Title> <Title>Content</Title>
<Container>{detailData?.content}</Container> <Container>{detailData?.content}</Container>
</GridItem> </GridItem>
)} )}
{detailData?.request_body && ( {detailData?.request_body && (
<GridItem display={'flex'} borderBottomWidth="1px" borderRightWidth="1px" colSpan={2}> <GridItem display={'flex'} borderBottomWidth="1px" colSpan={2}>
<Title>Request Body</Title> <Title>Request Body</Title>
<Container userSelect={'all'}>{detailData?.request_body}</Container> <Container userSelect={'all'}>{detailData?.request_body}</Container>
</GridItem> </GridItem>

View File

@ -247,9 +247,9 @@ const MultipleReferenceSelector = ({
// Get valid item and remove invalid item // Get valid item and remove invalid item
const formatList = useMemo(() => { const formatList = useMemo(() => {
if (!value) return []; if (!value || !Array.isArray(value)) return [];
return value?.map((item) => { return value.map((item) => {
const [nodeName, outputName] = getSelectValue(item); const [nodeName, outputName] = getSelectValue(item);
return { return {
rawValue: item, rawValue: item,

View File

@ -166,6 +166,7 @@ export const getChannelLog = (params: {
logs: ChannelLogListItemType[]; logs: ChannelLogListItemType[];
total: number; total: number;
}>(`/logs/search`, { }>(`/logs/search`, {
result_only: true,
request_id: params.request_id, request_id: params.request_id,
channel: params.channel, channel: params.channel,
model_name: params.model_name, model_name: params.model_name,

File diff suppressed because one or more lines are too long