Skip to content

Commit f00adcb

Browse files
authored
fix: surrender;perf: llm response (#6190)
* feat: workflow route to detail * llm response * fix: surrender * fix: surrender * fix: surrender * fix: test
1 parent 88ed97b commit f00adcb

File tree

22 files changed

+389
-136
lines changed

22 files changed

+389
-136
lines changed

document/content/docs/upgrading/4-14/4145.mdx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,19 @@ description: 'FastGPT V4.14.5 更新说明'
77
## 🚀 新增内容
88

99
1. 工作流画布增加演示模式,同时优化折叠模式样式。
10-
2. 对话记录使用侧改成软删除,增加从日志管理里删除对话记录。
11-
3. 更新Agent/工具时,会更新其上层所有目录的更新时间,以便其会排在列表前面。
12-
4. 门户页支持配置单个应用运行可见度配。
13-
5. 导出单个知识库集合分块接口。
14-
6. 升级 Mongo5.x 至 5.0.32 解决CVE-2025-14847。
10+
2. 工作流增加嵌套应用快速跳转按钮。
11+
3. 对话记录使用侧改成软删除,增加从日志管理里删除对话记录。
12+
4. 更新Agent/工具时,会更新其上层所有目录的更新时间,以便其会排在列表前面。
13+
5. 门户页支持配置单个应用运行可见度配。
14+
6. 导出单个知识库集合分块接口。
15+
7. 升级 Mongo5.x 至 5.0.32 解决CVE-2025-14847。
1516

1617
## ⚙️ 优化
1718

1819
1. 优化获取 redis 所有 key 的逻辑,避免大量获取时导致阻塞。
1920
2. MongoDB, Redis 和 MQ 的重连逻辑优化。
2021
3. 变量输入框禁用状态可复制。
22+
4. LLM 请求空响应判断,排除敏感过滤错误被误认为无响应。
2123

2224
## 🐛 修复
2325

document/data/doc-last-modified.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@
116116
"document/content/docs/upgrading/4-13/4131.mdx": "2025-09-30T15:47:06+08:00",
117117
"document/content/docs/upgrading/4-13/4132.mdx": "2025-12-15T11:50:00+08:00",
118118
"document/content/docs/upgrading/4-14/4140.mdx": "2025-11-06T15:43:00+08:00",
119-
"document/content/docs/upgrading/4-14/4141.mdx": "2025-11-19T10:15:27+08:00",
119+
"document/content/docs/upgrading/4-14/4141.mdx": "2025-12-31T09:54:29+08:00",
120120
"document/content/docs/upgrading/4-14/4142.mdx": "2025-11-18T19:27:14+08:00",
121121
"document/content/docs/upgrading/4-14/4143.mdx": "2025-11-26T20:52:05+08:00",
122122
"document/content/docs/upgrading/4-14/4144.mdx": "2025-12-16T14:56:04+08:00",
123-
"document/content/docs/upgrading/4-14/4145.mdx": "2026-01-05T11:19:08+08:00",
123+
"document/content/docs/upgrading/4-14/4145.mdx": "2026-01-05T13:44:33+08:00",
124124
"document/content/docs/upgrading/4-8/40.mdx": "2025-08-02T19:38:37+08:00",
125125
"document/content/docs/upgrading/4-8/41.mdx": "2025-08-02T19:38:37+08:00",
126126
"document/content/docs/upgrading/4-8/42.mdx": "2025-08-02T19:38:37+08:00",

packages/global/core/app/tool/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,10 @@ export function splitCombineToolId(id: string) {
4444
}
4545
return { source, pluginId: id };
4646
}
47+
48+
export const getToolRawId = (id: string) => {
49+
const toolId = splitCombineToolId(id).pluginId;
50+
51+
// 兼容 toolset
52+
return toolId.split('/')[0];
53+
};
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ObjectIdSchema } from '../../../../common/type/mongo';
2+
import { z } from 'zod';
3+
4+
/* Get App Permission */
5+
export const GetAppPermissionQuerySchema = z.object({
6+
appId: ObjectIdSchema.meta({
7+
example: '68ad85a7463006c963799a05',
8+
description: '应用 ID'
9+
})
10+
});
11+
export type GetAppPermissionQueryType = z.infer<typeof GetAppPermissionQuerySchema>;
12+
13+
export const GetAppPermissionResponseSchema = z.object({
14+
hasReadPer: z.boolean().meta({
15+
description: '是否有读权限'
16+
}),
17+
hasWritePer: z.boolean().meta({
18+
description: '是否有写权限'
19+
}),
20+
hasManagePer: z.boolean().meta({
21+
description: '是否有管理权限'
22+
}),
23+
hasReadChatLogPer: z.boolean().meta({
24+
description: '是否有读取对话日志权限'
25+
}),
26+
isOwner: z.boolean().meta({
27+
description: '是否为所有者'
28+
})
29+
});
30+
export type GetAppPermissionResponseType = z.infer<typeof GetAppPermissionResponseSchema>;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { OpenAPIPath } from '../../../type';
2+
import { TagsMap } from '../../../tag';
3+
import { GetAppPermissionQuerySchema, GetAppPermissionResponseSchema } from './api';
4+
5+
export const AppCommonPath: OpenAPIPath = {
6+
'/core/app/getPermission': {
7+
get: {
8+
summary: '获取应用权限',
9+
description: '根据应用 ID 获取当前用户对该应用的权限信息',
10+
tags: [TagsMap.appCommon],
11+
requestParams: {
12+
query: GetAppPermissionQuerySchema
13+
},
14+
responses: {
15+
200: {
16+
description: '成功获取应用权限',
17+
content: {
18+
'application/json': {
19+
schema: GetAppPermissionResponseSchema
20+
}
21+
}
22+
}
23+
}
24+
}
25+
}
26+
};
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { OpenAPIPath } from '../../type';
22
import { AppLogPath } from './log';
33
import { PublishChannelPath } from './publishChannel';
4+
import { AppCommonPath } from './common';
45

56
export const AppPath: OpenAPIPath = {
67
...AppLogPath,
7-
...PublishChannelPath
8+
...PublishChannelPath,
9+
...AppCommonPath
810
};

packages/global/openapi/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export const openAPIDocument = createDocument({
2424
'x-tagGroups': [
2525
{
2626
name: 'Agent 应用',
27-
tags: [TagsMap.appLog, TagsMap.publishChannel]
27+
tags: [TagsMap.appCommon, TagsMap.appLog, TagsMap.publishChannel]
2828
},
2929
{
3030
name: '对话管理',

packages/global/openapi/tag.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ export const TagsMap = {
22
/* Core */
33
// Agent - log
44
appLog: 'Agent 日志',
5+
// Agent - common
6+
appCommon: 'Agent 管理',
57

68
// Chat - home
79
chatPage: '对话页',

packages/service/core/ai/llm/agentCall/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ export const runAgentCall = async ({
211211
answerText: answer,
212212
toolCalls = [],
213213
usage,
214-
getEmptyResponseTip,
214+
responseEmptyTip,
215215
assistantMessage: llmAssistantMessage,
216216
finish_reason: finishReason
217217
} = await createLLMResponse({
@@ -235,8 +235,8 @@ export const runAgentCall = async ({
235235

236236
finish_reason = finishReason;
237237

238-
if (!answer && !reasoningContent && !toolCalls.length) {
239-
return Promise.reject(getEmptyResponseTip());
238+
if (responseEmptyTip) {
239+
return Promise.reject(responseEmptyTip);
240240
}
241241

242242
// 3. 更新 messages

packages/service/core/ai/llm/request.ts

Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type LLMResponse = {
5151
reasoningText: string;
5252
toolCalls?: ChatCompletionMessageToolCall[];
5353
finish_reason: CompletionFinishReason;
54-
getEmptyResponseTip: () => string;
54+
responseEmptyTip?: string;
5555
usage: {
5656
inputTokens: number;
5757
outputTokens: number;
@@ -92,7 +92,7 @@ export const createLLMResponse = async <T extends CompletionsBodyType>(
9292
});
9393

9494
// console.log(JSON.stringify(requestBody, null, 2));
95-
const { response, isStreamResponse, getEmptyResponseTip } = await createChatCompletion({
95+
const { response, isStreamResponse } = await createChatCompletion({
9696
body: requestBody,
9797
modelData,
9898
userKey,
@@ -151,9 +151,33 @@ export const createLLMResponse = async <T extends CompletionsBodyType>(
151151
usage?.prompt_tokens || (await countGptMessagesTokens(requestBody.messages, requestBody.tools));
152152
const outputTokens = usage?.completion_tokens || (await countGptMessagesTokens(assistantMessage));
153153

154+
const getEmptyResponseTip = () => {
155+
if (userKey?.baseUrl) {
156+
addLog.warn(`User LLM response empty`, {
157+
baseUrl: userKey?.baseUrl,
158+
requestBody,
159+
finish_reason
160+
});
161+
return `您的 OpenAI key 没有响应: ${JSON.stringify(body)}`;
162+
} else {
163+
addLog.error(`LLM response empty`, {
164+
message: '',
165+
data: requestBody,
166+
finish_reason
167+
});
168+
}
169+
return i18nT('chat:LLM_model_response_empty');
170+
};
171+
const isNotResponse =
172+
!answerText &&
173+
!reasoningText &&
174+
!toolCalls?.length &&
175+
(finish_reason === 'stop' || !finish_reason);
176+
const responseEmptyTip = isNotResponse ? getEmptyResponseTip() : undefined;
177+
154178
return {
155179
isStreamResponse,
156-
getEmptyResponseTip,
180+
responseEmptyTip,
157181
answerText,
158182
reasoningText,
159183
toolCalls,
@@ -535,7 +559,8 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
535559
maxToken: body.max_tokens || undefined
536560
});
537561

538-
const requestBody = {
562+
const formatStop = stop?.split('|').filter((item) => !!item.trim());
563+
let requestBody = {
539564
...body,
540565
max_tokens: maxTokens,
541566
model: modelData.model,
@@ -546,16 +571,20 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
546571
temperature: body.temperature
547572
})
548573
: undefined,
549-
...modelData?.defaultConfig,
550574
response_format,
551-
stop: stop?.split('|').filter((item) => !!item.trim()),
575+
stop: formatStop?.length ? formatStop : undefined,
552576
...(toolCallMode === 'toolChoice' && {
553577
tools,
554578
tool_choice,
555579
parallel_tool_calls
556580
})
557581
} as T;
558582

583+
// Filter null value
584+
requestBody = Object.fromEntries(
585+
Object.entries(requestBody).filter(([_, value]) => value !== null)
586+
) as T;
587+
559588
// field map
560589
if (modelData.fieldMap) {
561590
Object.entries(modelData.fieldMap).forEach(([sourceKey, targetKey]) => {
@@ -566,6 +595,11 @@ const llmCompletionsBodyFormat = async <T extends CompletionsBodyType>({
566595
});
567596
}
568597

598+
requestBody = {
599+
...requestBody,
600+
...modelData?.defaultConfig
601+
};
602+
569603
return {
570604
requestBody: requestBody as unknown as InferCompletionsBody<T>,
571605
modelData
@@ -584,18 +618,14 @@ const createChatCompletion = async ({
584618
timeout?: number;
585619
options?: OpenAI.RequestOptions;
586620
}): Promise<
587-
{
588-
getEmptyResponseTip: () => string;
589-
} & (
590-
| {
591-
response: StreamChatType;
592-
isStreamResponse: true;
593-
}
594-
| {
595-
response: UnStreamChatType;
596-
isStreamResponse: false;
597-
}
598-
)
621+
| {
622+
response: StreamChatType;
623+
isStreamResponse: true;
624+
}
625+
| {
626+
response: UnStreamChatType;
627+
isStreamResponse: false;
628+
}
599629
> => {
600630
try {
601631
if (!modelData) {
@@ -627,34 +657,16 @@ const createChatCompletion = async ({
627657
response !== null &&
628658
('iterator' in response || 'controller' in response);
629659

630-
const getEmptyResponseTip = () => {
631-
if (userKey?.baseUrl) {
632-
addLog.warn(`User LLM response empty`, {
633-
baseUrl: userKey?.baseUrl,
634-
requestBody: body
635-
});
636-
return `您的 OpenAI key 没有响应: ${JSON.stringify(body)}`;
637-
} else {
638-
addLog.error(`LLM response empty`, {
639-
message: '',
640-
data: body
641-
});
642-
}
643-
return i18nT('chat:LLM_model_response_empty');
644-
};
645-
646660
if (isStreamResponse) {
647661
return {
648662
response,
649-
isStreamResponse: true,
650-
getEmptyResponseTip
663+
isStreamResponse: true
651664
};
652665
}
653666

654667
return {
655668
response,
656-
isStreamResponse: false,
657-
getEmptyResponseTip
669+
isStreamResponse: false
658670
};
659671
} catch (error) {
660672
if (userKey?.baseUrl) {

0 commit comments

Comments
 (0)