Skip to content

Commit e927b94

Browse files
authored
perf: quote code (#6977)
* perf: quote code * fix: type * doc * doc * add zod schema
1 parent 22ebfac commit e927b94

32 files changed

Lines changed: 551 additions & 659 deletions

File tree

document/content/self-host/upgrading/4-15/41502.mdx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ description: 'FastGPT V4.15.0-beta2 更新说明'
2424
1. 支持 Skill 编辑,Agent 支持 Skill 使用,目前仅支持静态 Skill,无法反向调用系统工具。
2525
2. 重写 agentV2 loop 逻辑。
2626
3. 知识库搜索支持原生多模态 embedding 模型以及图搜图。
27+
4. Chat API dataId 校验:`/v1/chat/completions``/v2/chat/completions``chatTest` 在工作流执行前校验本轮 `dataId` 是否与请求内或当前会话已有记录重复;重复时直接返回业务错误,避免脏数据进入工作流与流恢复合并逻辑。
2728

2829
## ⚙️ 优化
2930

@@ -44,13 +45,14 @@ description: 'FastGPT V4.15.0-beta2 更新说明'
4445
1. 工作流,单节点调试,存在异常默认值。
4546
2. 模型配置,defaultConfig 覆盖异常。
4647
3. 切换团队时,清除本地 chat 缓存。
47-
4. 流恢复表单输入:刷新或断线续传后,已提交的表单输入值(含 `fileSelect` 文件列表)能正确回填到交互节点内,不再出现空表单或文件消失。
48-
5. 流恢复内容保留:自动续传开始时保留已加载的 AI 输出与节点响应;completed 记录覆盖时不再丢失已恢复的交互表单值与 flow 节点响应。
49-
6. 流恢复交互状态:已提交表单后不再重复追加过期未提交交互;恢复过程中表单默认值能随 `formInputResult` 同步更新。
50-
7. 流恢复历史标题:新对话发起后,侧栏临时历史项优先展示用户输入生成的标题,服务端标题落库后再覆盖,避免长时间显示「新对话」。
51-
8. 切换应用历史串线:修复切换不同应用时,侧栏或会话内容短暂展示其他应用聊天记录的问题。
52-
9. 停止对话提示:移除停止时的 warning toast,改为与后端生成态同步的状态提示。
53-
10. Chat API dataId 校验:`/v1/chat/completions``/v2/chat/completions``chatTest` 在工作流执行前校验本轮 `dataId` 是否与请求内或当前会话已有记录重复;重复时直接返回业务错误,避免脏数据进入工作流与流恢复合并逻辑。
48+
4. 对话流恢复:
49+
- 刷新或断线续传后,已提交的表单输入值(含 `fileSelect` 文件列表)能正确回填到交互节点内,不再出现空表单或文件消失。
50+
- 自动续传开始时保留已加载的 AI 输出与节点响应;completed 记录覆盖时不再丢失已恢复的交互表单值与 flow 节点响应。
51+
- 已提交表单后不再重复追加过期未提交交互;恢复过程中表单默认值能随 `formInputResult` 同步更新。
52+
- 新对话发起后,侧栏临时历史项优先展示用户输入生成的标题,服务端标题落库后再覆盖,避免长时间显示「新对话」。
53+
- 切换不同应用时,侧栏或会话内容短暂展示其他应用聊天记录的问题。
54+
5. 停止对话提示:移除停止时的 warning toast,改为与后端生成态同步的状态提示。
55+
6. v1/completions 接口,nodeResponse 中,quoteList 未返回 `q` , `a`
5456

5557
## 代码优化
5658

document/data/doc-last-modified.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@
187187
"content/self-host/custom-models/ollama.mdx": "2026-04-26T21:08:47+08:00",
188188
"content/self-host/custom-models/xinference.en.mdx": "2026-04-26T21:08:47+08:00",
189189
"content/self-host/custom-models/xinference.mdx": "2026-04-26T21:08:47+08:00",
190-
"content/self-host/deploy/docker.en.mdx": "2026-05-07T15:08:21+08:00",
191-
"content/self-host/deploy/docker.mdx": "2026-05-07T15:08:21+08:00",
190+
"content/self-host/deploy/docker.en.mdx": "2026-05-24T00:53:50+08:00",
191+
"content/self-host/deploy/docker.mdx": "2026-05-24T00:53:50+08:00",
192192
"content/self-host/deploy/sealos.en.mdx": "2026-04-26T21:08:47+08:00",
193193
"content/self-host/deploy/sealos.mdx": "2026-04-26T21:08:47+08:00",
194194
"content/self-host/design/dataset.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -233,8 +233,8 @@
233233
"content/self-host/upgrading/4-14/4140.mdx": "2026-04-26T21:08:47+08:00",
234234
"content/self-host/upgrading/4-14/4141.en.mdx": "2026-04-26T21:08:47+08:00",
235235
"content/self-host/upgrading/4-14/4141.mdx": "2026-04-26T21:08:47+08:00",
236-
"content/self-host/upgrading/4-14/41410.en.mdx": "2026-04-26T21:08:47+08:00",
237-
"content/self-host/upgrading/4-14/41410.mdx": "2026-05-23T22:47:02+08:00",
236+
"content/self-host/upgrading/4-14/41410.en.mdx": "2026-05-24T00:53:50+08:00",
237+
"content/self-host/upgrading/4-14/41410.mdx": "2026-05-24T00:53:50+08:00",
238238
"content/self-host/upgrading/4-14/41411.en.mdx": "2026-04-26T21:08:47+08:00",
239239
"content/self-host/upgrading/4-14/41411.mdx": "2026-04-26T21:28:27+08:00",
240240
"content/self-host/upgrading/4-14/41412.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -278,13 +278,13 @@
278278
"content/self-host/upgrading/4-14/4149.en.mdx": "2026-04-26T21:08:47+08:00",
279279
"content/self-host/upgrading/4-14/4149.mdx": "2026-04-26T21:08:47+08:00",
280280
"content/self-host/upgrading/4-15/4150.mdx": "2026-05-20T17:52:26+08:00",
281-
"content/self-host/upgrading/4-15/41502.mdx": "2026-05-23T23:33:02+08:00",
281+
"content/self-host/upgrading/4-15/41502.mdx": "2026-05-24T17:01:11+08:00",
282282
"content/self-host/upgrading/outdated/40.en.mdx": "2026-04-26T21:08:47+08:00",
283283
"content/self-host/upgrading/outdated/40.mdx": "2026-04-26T21:08:47+08:00",
284284
"content/self-host/upgrading/outdated/41.en.mdx": "2026-04-26T21:08:47+08:00",
285285
"content/self-host/upgrading/outdated/41.mdx": "2026-04-26T21:08:47+08:00",
286-
"content/self-host/upgrading/outdated/4100.en.mdx": "2026-05-07T15:06:40+08:00",
287-
"content/self-host/upgrading/outdated/4100.mdx": "2026-05-07T15:06:40+08:00",
286+
"content/self-host/upgrading/outdated/4100.en.mdx": "2026-05-24T00:53:50+08:00",
287+
"content/self-host/upgrading/outdated/4100.mdx": "2026-05-24T00:53:50+08:00",
288288
"content/self-host/upgrading/outdated/4101.en.mdx": "2026-04-26T21:08:47+08:00",
289289
"content/self-host/upgrading/outdated/4101.mdx": "2026-04-26T21:08:47+08:00",
290290
"content/self-host/upgrading/outdated/4110.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -363,8 +363,8 @@
363363
"content/self-host/upgrading/outdated/4818.mdx": "2026-04-26T21:08:47+08:00",
364364
"content/self-host/upgrading/outdated/4819.en.mdx": "2026-04-26T21:08:47+08:00",
365365
"content/self-host/upgrading/outdated/4819.mdx": "2026-04-26T21:08:47+08:00",
366-
"content/self-host/upgrading/outdated/482.en.mdx": "2026-04-26T21:08:47+08:00",
367-
"content/self-host/upgrading/outdated/482.mdx": "2026-04-26T21:08:47+08:00",
366+
"content/self-host/upgrading/outdated/482.en.mdx": "2026-05-24T00:53:50+08:00",
367+
"content/self-host/upgrading/outdated/482.mdx": "2026-05-24T00:53:50+08:00",
368368
"content/self-host/upgrading/outdated/4820.en.mdx": "2026-04-26T21:08:47+08:00",
369369
"content/self-host/upgrading/outdated/4820.mdx": "2026-04-26T21:08:47+08:00",
370370
"content/self-host/upgrading/outdated/4821.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -373,8 +373,8 @@
373373
"content/self-host/upgrading/outdated/4822.mdx": "2026-04-26T21:08:47+08:00",
374374
"content/self-host/upgrading/outdated/4823.en.mdx": "2026-04-26T21:08:47+08:00",
375375
"content/self-host/upgrading/outdated/4823.mdx": "2026-04-26T21:08:47+08:00",
376-
"content/self-host/upgrading/outdated/483.en.mdx": "2026-04-26T21:08:47+08:00",
377-
"content/self-host/upgrading/outdated/483.mdx": "2026-04-26T21:08:47+08:00",
376+
"content/self-host/upgrading/outdated/483.en.mdx": "2026-05-24T00:53:50+08:00",
377+
"content/self-host/upgrading/outdated/483.mdx": "2026-05-24T00:53:50+08:00",
378378
"content/self-host/upgrading/outdated/484.en.mdx": "2026-04-26T21:08:47+08:00",
379379
"content/self-host/upgrading/outdated/484.mdx": "2026-04-26T21:08:47+08:00",
380380
"content/self-host/upgrading/outdated/485.en.mdx": "2026-04-26T21:08:47+08:00",
@@ -387,8 +387,8 @@
387387
"content/self-host/upgrading/outdated/488.mdx": "2026-04-26T21:08:47+08:00",
388388
"content/self-host/upgrading/outdated/489.en.mdx": "2026-04-26T21:08:47+08:00",
389389
"content/self-host/upgrading/outdated/489.mdx": "2026-04-26T21:08:47+08:00",
390-
"content/self-host/upgrading/outdated/490.en.mdx": "2026-04-26T21:08:47+08:00",
391-
"content/self-host/upgrading/outdated/490.mdx": "2026-04-26T21:08:47+08:00",
390+
"content/self-host/upgrading/outdated/490.en.mdx": "2026-05-24T00:53:50+08:00",
391+
"content/self-host/upgrading/outdated/490.mdx": "2026-05-24T00:53:50+08:00",
392392
"content/self-host/upgrading/outdated/491.en.mdx": "2026-04-26T21:08:47+08:00",
393393
"content/self-host/upgrading/outdated/491.mdx": "2026-04-26T21:08:47+08:00",
394394
"content/self-host/upgrading/outdated/4910.en.mdx": "2026-04-26T21:08:47+08:00",

packages/global/common/error/code/common.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,19 @@ const datasetErr = [
2222
},
2323
{
2424
statusText: CommonErrEnum.fileNotFound,
25-
message: 'error.fileNotFound'
25+
message: i18nT('common:error.fileNotFound')
2626
},
2727
{
2828
statusText: CommonErrEnum.unAuthFile,
29-
message: 'error.unAuthFile'
29+
message: i18nT('common:error.unAuthFile')
3030
},
3131
{
3232
statusText: CommonErrEnum.missingParams,
33-
message: 'error.missingParams'
33+
message: i18nT('common:error.missingParams')
3434
},
3535
{
3636
statusText: CommonErrEnum.inheritPermissionError,
37-
message: 'error.inheritPermissionError'
37+
message: i18nT('common:error.inheritPermissionError')
3838
}
3939
];
4040
export default datasetErr.reduce((acc, cur, index) => {

packages/global/common/error/code/dataset.ts

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ export enum DatasetErrEnum {
2020
const datasetErr = [
2121
{
2222
statusText: DatasetErrEnum.sameApiCollection,
23-
message: i18nT('dataset:same_api_collection')
23+
message: i18nT('common:core.dataset.error.sameApiCollection')
2424
},
2525
{
2626
statusText: DatasetErrEnum.notSupportSync,
27-
message: i18nT('dataset:collection_not_support_sync')
27+
message: i18nT('common:core.dataset.error.notSupportSync')
2828
},
2929
{
3030
statusText: DatasetErrEnum.unExist,
@@ -36,35 +36,39 @@ const datasetErr = [
3636
},
3737
{
3838
statusText: DatasetErrEnum.unAuthDataset,
39-
message: 'core.dataset.error.unAuthDataset'
39+
message: i18nT('common:core.dataset.error.unAuthDataset')
4040
},
4141
{
4242
statusText: DatasetErrEnum.unAuthDatasetCollection,
43-
message: 'core.dataset.error.unAuthDatasetCollection'
43+
message: i18nT('common:core.dataset.error.unAuthDatasetCollection')
4444
},
4545
{
4646
statusText: DatasetErrEnum.unAuthDatasetData,
47-
message: 'core.dataset.error.unAuthDatasetData'
47+
message: i18nT('common:core.dataset.error.unAuthDatasetData')
4848
},
4949
{
5050
statusText: DatasetErrEnum.unAuthDatasetFile,
51-
message: 'core.dataset.error.unAuthDatasetFile'
51+
message: i18nT('common:core.dataset.error.unAuthDatasetFile')
5252
},
5353
{
5454
statusText: DatasetErrEnum.unCreateCollection,
55-
message: 'core.dataset.error.unCreateCollection'
55+
message: i18nT('common:core.dataset.error.unCreateCollection')
5656
},
5757
{
5858
statusText: DatasetErrEnum.unLinkCollection,
59-
message: 'core.dataset.error.unLinkCollection'
59+
message: i18nT('common:core.dataset.error.unLinkCollection')
6060
},
6161
{
6262
statusText: DatasetErrEnum.invalidVectorModelOrQAModel,
63-
message: 'core.dataset.error.invalidVectorModelOrQAModel'
63+
message: i18nT('common:core.dataset.error.invalidVectorModelOrQAModel')
6464
},
6565
{
6666
statusText: DatasetErrEnum.canNotEditAdminPermission,
67-
message: 'core.dataset.error.canNotEditAdminPermission'
67+
message: i18nT('common:core.dataset.error.canNotEditAdminPermission')
68+
},
69+
{
70+
statusText: DatasetErrEnum.noApiServer,
71+
message: i18nT('common:core.dataset.error.noApiServer')
6872
}
6973
];
7074
export default datasetErr.reduce((acc, cur, index) => {

packages/global/core/chat/type.ts

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import { SearchDataResponseItemSchema } from '../dataset/type';
1+
import { SearchDataResponseQuoteListItemSchema } from '../dataset/type';
22
import {
33
ChatFileTypeEnum,
44
ChatGenerateStatusEnum,
55
ChatRoleEnum,
6-
type ChatSourceEnum
6+
ChatSourceEnum
77
} from './constants';
88
import { FlowNodeTypeEnum } from '../workflow/node/constant';
99
import { DispatchNodeResponseKeyEnum } from '../workflow/runtime/constants';
10-
import { AppSchemaTypeSchema, type AppSchemaType, type VariableItemType } from '../app/type';
10+
import { AppSchemaTypeSchema, VariableItemTypeSchema } from '../app/type';
1111
import { DispatchNodeResponseSchema } from '../workflow/runtime/type';
1212
import { WorkflowInteractiveResponseTypeSchema } from '../workflow/template/system/interactive/type';
13-
import type { FlowNodeInputItemType } from '../workflow/type/io';
13+
import { FlowNodeInputItemTypeSchema } from '../workflow/type/io';
1414
import z from 'zod';
1515
import {
1616
AgentLoopAskSchema,
@@ -19,6 +19,7 @@ import {
1919
AgentPlanSchema,
2020
AgentPlanStatusSchema
2121
} from '../ai/agent/type';
22+
import { ObjectIdSchema } from '../../common/type/mongo';
2223

2324
export const ChatHistoryItemResSchema = DispatchNodeResponseSchema.extend({
2425
nodeId: z.string(),
@@ -80,49 +81,56 @@ export const SkillModuleResponseItemSchema = z.object({
8081
export type SkillModuleResponseItemType = z.infer<typeof SkillModuleResponseItemSchema>;
8182

8283
/* --------- chat ---------- */
83-
export type ChatSchemaType = {
84-
_id: string;
85-
chatId: string;
86-
userId: string;
87-
teamId: string;
88-
tmbId: string;
89-
appId: string;
90-
appVersionId?: string;
91-
createTime: Date;
92-
updateTime: Date;
93-
title: string;
94-
customTitle: string;
95-
top: boolean;
96-
source: `${ChatSourceEnum}`;
97-
sourceName?: string;
98-
99-
shareId?: string;
100-
outLinkUid?: string;
101-
102-
variableList?: VariableItemType[];
103-
welcomeText?: string;
104-
variables: Record<string, any>;
105-
pluginInputs?: FlowNodeInputItemType[];
106-
metadata?: Record<string, any>;
84+
export const ChatSchema = z.object({
85+
_id: ObjectIdSchema,
86+
chatId: z.string(),
87+
userId: ObjectIdSchema,
88+
teamId: ObjectIdSchema,
89+
tmbId: ObjectIdSchema,
90+
appId: ObjectIdSchema.meta({
91+
description: '目前已经变成 sourceId,可能是 app 的,也可能是 skill 的'
92+
}),
93+
appVersionId: ObjectIdSchema.optional().meta({ description: 'appId 为 app 时候才有' }),
94+
createTime: z.coerce.date(),
95+
updateTime: z.coerce.date(),
96+
title: z.string(),
97+
customTitle: z.string().optional(),
98+
top: z.boolean().default(false),
99+
source: z.enum(ChatSourceEnum),
100+
sourceName: z.string().optional(),
101+
102+
shareId: z.string().optional(),
103+
outLinkUid: z.string().optional(),
104+
105+
variableList: z.array(VariableItemTypeSchema).optional(),
106+
welcomeText: z.string().optional(),
107+
variables: z.record(z.string(), z.any()),
108+
pluginInputs: z.array(FlowNodeInputItemTypeSchema).optional(),
109+
metadata: z.record(z.string(), z.any()).optional(),
107110

108111
// Boolean flags for efficient filtering
109-
hasGoodFeedback?: boolean;
110-
hasBadFeedback?: boolean;
111-
hasUnreadGoodFeedback?: boolean;
112-
hasUnreadBadFeedback?: boolean;
112+
hasGoodFeedback: z.boolean().optional(),
113+
hasBadFeedback: z.boolean().optional(),
114+
hasUnreadGoodFeedback: z.boolean().optional(),
115+
hasUnreadBadFeedback: z.boolean().optional(),
113116
// Error count (redundant field for performance)
114-
errorCount?: number;
117+
errorCount: z.number().optional(),
115118

116119
/** 旧数据可能无此字段;业务上按 done 处理 */
117-
chatGenerateStatus?: ChatGenerateStatusEnum;
118-
hasBeenRead: boolean;
120+
chatGenerateStatus: z
121+
.enum(ChatGenerateStatusEnum)
122+
.default(ChatGenerateStatusEnum.done)
123+
.meta({ description: '生成状态' }),
124+
hasBeenRead: z.boolean().default(true),
119125

120-
deleteTime?: Date | null;
121-
};
126+
deleteTime: z.coerce.date().nullish()
127+
});
128+
export type ChatSchemaType = z.infer<typeof ChatSchema>;
122129

123-
export type ChatWithAppSchema = Omit<ChatSchemaType, 'appId'> & {
124-
appId: AppSchemaType;
125-
};
130+
export const ChatWithAppSchema = ChatSchema.omit({ appId: true }).extend({
131+
appId: AppSchemaTypeSchema
132+
});
133+
export type ChatWithAppSchema = z.infer<typeof ChatWithAppSchema>;
126134

127135
/* --------- chat item ---------- */
128136
// User
@@ -320,7 +328,7 @@ export type ToolCiteLinksType = z.infer<typeof ToolCiteLinksSchema>;
320328

321329
export const ResponseTagItemSchema = z.object({
322330
useAgentSandbox: z.boolean().optional(),
323-
totalQuoteList: z.array(SearchDataResponseItemSchema).optional(),
331+
totalQuoteList: z.array(SearchDataResponseQuoteListItemSchema).optional(),
324332
toolCiteLinks: z.array(ToolCiteLinksSchema).optional(),
325333
errorText: ErrorTextItemSchema.optional(),
326334
llmModuleAccount: z.number().optional().meta({ deprecated: true }),

packages/global/core/dataset/type.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,25 @@ export const SearchDataResponseItemSchema = DatasetDataItemSchema.omit({
428428
.meta({ description: '搜索数据响应项' });
429429
export type SearchDataResponseItemType = z.infer<typeof SearchDataResponseItemSchema>;
430430

431+
export const SearchDataResponseQuoteItemSchema = SearchDataResponseItemSchema.pick({
432+
id: true,
433+
chunkIndex: true,
434+
datasetId: true,
435+
collectionId: true,
436+
sourceId: true,
437+
sourceName: true,
438+
score: true
439+
}).meta({ description: '搜索数据引用响应项(精简)' });
440+
export type SearchDataResponseQuoteItemType = z.infer<typeof SearchDataResponseQuoteItemSchema>;
441+
442+
export const SearchDataResponseQuoteListItemSchema = z.union([
443+
SearchDataResponseItemSchema,
444+
SearchDataResponseQuoteItemSchema
445+
]);
446+
export type SearchDataResponseQuoteListItemType = z.infer<
447+
typeof SearchDataResponseQuoteListItemSchema
448+
>;
449+
431450
export const DatasetCiteItemSchema = z
432451
.object({
433452
_id: ObjectIdSchema.meta({ description: '数据 ID' }),

packages/global/core/workflow/runtime/type.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import type {
2424
InteractiveNodeResponseType,
2525
WorkflowInteractiveResponseType
2626
} from '../template/system/interactive/type';
27-
import { SearchDataResponseItemSchema } from '../../dataset/type';
27+
import { SearchDataResponseQuoteListItemSchema } from '../../dataset/type';
2828
import type { localeType } from '../../../common/i18n/type';
2929
import { type ChatFileStoreValue, type UserChatItemValueItemType } from '../../chat/type';
3030
import { DatasetSearchModeEnum } from '../../dataset/constants';
@@ -198,7 +198,7 @@ export const DispatchNodeResponseSchema = z
198198
temperature: z.number().optional().meta({ description: '温度' }),
199199
maxToken: z.number().optional().meta({ description: '最大 token' }),
200200
quoteList: z
201-
.array(SearchDataResponseItemSchema)
201+
.array(SearchDataResponseQuoteListItemSchema)
202202
.optional()
203203
.meta({ description: '知识库引用列表' }),
204204
reasoningText: z.string().optional().meta({ description: '思考文本' }),

0 commit comments

Comments
 (0)