Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions document/content/docs/upgrading/4-14/4145.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,11 @@ description: 'FastGPT V4.14.5 更新说明'
5. 加载默认模型时,maxTokens 字段未赋值,导致模型最大响应值配置为空。
6. S3 文件清理队列因网络稳定问题出现阻塞,导致删除任务不再执行。
7. 对话日志接口适配 mongo4.x 语法。
8. 变量更新节点将文件 URL 字符串数组错误转换为对象数组。
9. 多个表单输入节点共享 sessionStorage 导致默认值不显示。
10. 代码运行节点切换语言后,AI 仍使用旧语言生成代码。
11. 多个自定义反馈节点并发写入触发数据库写入冲突。
12. 交互节点后续的自定义反馈节点写入失败。


## 插件
4 changes: 2 additions & 2 deletions document/data/doc-last-modified.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
"document/content/docs/protocol/terms.en.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/protocol/terms.mdx": "2025-12-15T23:36:54+08:00",
"document/content/docs/toc.en.mdx": "2025-08-04T13:42:36+08:00",
"document/content/docs/toc.mdx": "2026-01-06T18:19:42+08:00",
"document/content/docs/toc.mdx": "2026-01-06T13:25:46+08:00",
"document/content/docs/upgrading/4-10/4100.mdx": "2025-08-02T19:38:37+08:00",
"document/content/docs/upgrading/4-10/4101.mdx": "2025-09-08T20:07:20+08:00",
"document/content/docs/upgrading/4-11/4110.mdx": "2025-08-05T23:20:39+08:00",
Expand Down Expand Up @@ -204,4 +204,4 @@
"document/content/docs/use-cases/external-integration/openapi.mdx": "2025-09-29T11:34:11+08:00",
"document/content/docs/use-cases/external-integration/wecom.mdx": "2025-12-10T20:07:05+08:00",
"document/content/docs/use-cases/index.mdx": "2025-07-24T14:23:04+08:00"
}
}
3 changes: 2 additions & 1 deletion packages/global/core/workflow/runtime/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ export enum DispatchNodeResponseKeyEnum {
interactive = 'INTERACTIVE', // is interactive
runTimes = 'runTimes', // run times
newVariables = 'newVariables', // new variables
memories = 'system_memories' // memories
memories = 'system_memories', // memories
customFeedbacks = 'customFeedbacks' // custom feedbacks
}

export const needReplaceReferenceInputTypeList = [
Expand Down
2 changes: 1 addition & 1 deletion packages/global/core/workflow/runtime/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export type ChatDispatchProps = {
responseChatItemId?: string;
histories: ChatItemType[];
variables: Record<string, any>; // global variable
cloneVariables: Record<string, any>;
query: UserChatItemValueItemType[]; // trigger query
chatConfig: AppSchema['chatConfig'];
lastInteractive?: WorkflowInteractiveResponseType; // last interactive response
Expand Down Expand Up @@ -274,6 +273,7 @@ export type DispatchNodeResultType<T = {}, ERR = { [NodeOutputKeyEnum.errorText]
[DispatchNodeResponseKeyEnum.newVariables]?: Record<string, any>;
[DispatchNodeResponseKeyEnum.memories]?: Record<string, any>;
[DispatchNodeResponseKeyEnum.interactive]?: InteractiveNodeResponseType;
[DispatchNodeResponseKeyEnum.customFeedbacks]?: string[];

data?: T;
error?: ERR;
Expand Down
41 changes: 0 additions & 41 deletions packages/service/core/chat/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,47 +194,6 @@ export async function getChatItems({
return { histories, total, hasMorePrev, hasMoreNext };
}

export const addCustomFeedbacks = async ({
appId,
chatId,
dataId,
feedbacks
}: {
appId: string;
chatId?: string;
dataId?: string;
feedbacks: string[];
}) => {
if (!chatId || !dataId) return;

try {
await mongoSessionRun(async (session) => {
// Add custom feedbacks to ChatItem
await MongoChatItem.updateOne(
{
appId,
chatId,
dataId
},
{
$push: { customFeedbacks: { $each: feedbacks } }
},
{ session }
);

// Update ChatLog feedback statistics
await updateChatFeedbackCount({
appId,
chatId,
session
});
});
} catch (error) {
addLog.error('addCustomFeedbacks error', error);
throw error;
}
};

/**
* Update feedback count statistics for a chat in Chat table
* This method aggregates feedback data from chatItems and updates the Chat table
Expand Down
6 changes: 4 additions & 2 deletions packages/service/core/workflow/dispatch/child/runApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
assistantResponses,
runTimes,
workflowInteractiveResponse,
system_memories
system_memories,
customFeedbacks
} = await runWorkflow({
...props,
usageId: undefined,
Expand Down Expand Up @@ -205,7 +206,8 @@ export const dispatchRunAppNode = async (props: Props): Promise<Response> => {
totalPoints: usagePoints
}
],
[DispatchNodeResponseKeyEnum.toolResponses]: text
[DispatchNodeResponseKeyEnum.toolResponses]: text,
[DispatchNodeResponseKeyEnum.customFeedbacks]: customFeedbacks
};
} catch (error) {
return getNodeErrResponse({ error });
Expand Down
25 changes: 14 additions & 11 deletions packages/service/core/workflow/dispatch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type {
} from '@fastgpt/global/core/workflow/runtime/type';
import type { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type.d';
import { getErrText, UserError } from '@fastgpt/global/common/error/utils';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { filterPublicNodeResponseData } from '@fastgpt/global/core/chat/utils';
import {
checkNodeRunStatus,
Expand Down Expand Up @@ -57,13 +57,12 @@ import { addPreviewUrlToChatItems, presignVariablesFileUrls } from '../../chat/u
import type { MCPClient } from '../../app/mcp';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { i18nT } from '../../../../web/i18n/utils';
import { clone } from 'lodash';
import { validateFileUrlDomain } from '../../../common/security/fileUrlValidator';
import { delAgentRuntimeStopSign, shouldWorkflowStop } from './workflowStatus';

type Props = Omit<
ChatDispatchProps,
'checkIsStopping' | 'workflowDispatchDeep' | 'timezone' | 'externalProvider' | 'cloneVariables'
'checkIsStopping' | 'workflowDispatchDeep' | 'timezone' | 'externalProvider'
> & {
runtimeNodes: RuntimeNodeItemType[];
runtimeEdges: RuntimeEdgeItemType[];
Expand Down Expand Up @@ -182,7 +181,6 @@ export async function dispatchWorkFlow({
}

// Get default variables
const cloneVariables = clone(data.variables);
const defaultVariables = {
...externalProvider.externalWorkflowVariables,
...(await getSystemVariables({
Expand Down Expand Up @@ -228,8 +226,7 @@ export async function dispatchWorkFlow({
workflowDispatchDeep: 0,
usageId: newUsageId,
concatUsage,
mcpClientMemory,
cloneVariables
mcpClientMemory
}).finally(async () => {
if (streamCheckTimer) {
clearInterval(streamCheckTimer);
Expand Down Expand Up @@ -273,8 +270,7 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
usageId,
concatUsage,
runningUserInfo: { teamId },
mcpClientMemory,
cloneVariables
mcpClientMemory
} = data;

// Over max depth
Expand All @@ -296,7 +292,6 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
[DispatchNodeResponseKeyEnum.toolResponses]: null,
[DispatchNodeResponseKeyEnum.newVariables]: runtimeSystemVar2StoreType({
variables,
cloneVariables,
removeObj: externalProvider.externalWorkflowVariables,
userVariablesConfigs: data.chatConfig?.variables
}),
Expand Down Expand Up @@ -343,6 +338,7 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
}
| undefined;
system_memories: Record<string, any> = {}; // Workflow node memories
customFeedbackList: string[] = []; // Custom feedbacks collected from nodes

// Debug
debugNextStepRunNodes: RuntimeNodeItemType[] = []; // 记录 Debug 模式下,下一个阶段需要执行的节点。
Expand Down Expand Up @@ -720,7 +716,8 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
assistantResponses,
rewriteHistories,
runTimes = 1,
system_memories: newMemories
system_memories: newMemories,
customFeedbacks
}: NodeResponseCompleteType) => {
// Add run times
this.workflowRunTimes += runTimes;
Expand All @@ -737,6 +734,11 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
this.chatResponses.push(responseData);
}

// Collect custom feedbacks
if (customFeedbacks && Array.isArray(customFeedbacks)) {
this.customFeedbackList = this.customFeedbackList.concat(customFeedbacks);
}

// Push usage in real time. Avoid a workflow usage a large number of points
if (nodeDispatchUsages) {
if (usageId) {
Expand Down Expand Up @@ -1120,14 +1122,15 @@ export const runWorkflow = async (data: RunWorkflowProps): Promise<DispatchFlowR
[DispatchNodeResponseKeyEnum.toolResponses]: workflowQueue.toolRunResponse,
[DispatchNodeResponseKeyEnum.newVariables]: runtimeSystemVar2StoreType({
variables,
cloneVariables,
removeObj: externalProvider.externalWorkflowVariables,
userVariablesConfigs: data.chatConfig?.variables
}),
[DispatchNodeResponseKeyEnum.memories]:
Object.keys(workflowQueue.system_memories).length > 0
? workflowQueue.system_memories
: undefined,
[DispatchNodeResponseKeyEnum.customFeedbacks]:
workflowQueue.customFeedbackList.length > 0 ? workflowQueue.customFeedbackList : undefined,
durationSeconds
};
};
Expand Down
10 changes: 9 additions & 1 deletion packages/service/core/workflow/dispatch/loop/runLoop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
const outputValueArr = interactiveData ? interactiveData.loopResult : [];
const loopResponseDetail: ChatHistoryItemResType[] = [];
let assistantResponses: AIChatItemValueItemType[] = [];
const customFeedbacks: string[] = [];
let totalPoints = 0;
let newVariables: Record<string, any> = props.variables;
let interactiveResponse: WorkflowInteractiveResponseType | undefined = undefined;
Expand Down Expand Up @@ -116,6 +117,11 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
assistantResponses.push(...response.assistantResponses);
totalPoints += response.flowUsages.reduce((acc, usage) => acc + usage.totalPoints, 0);

// Collect custom feedbacks
if (response[DispatchNodeResponseKeyEnum.customFeedbacks]) {
customFeedbacks.push(...response[DispatchNodeResponseKeyEnum.customFeedbacks]);
}

// Concat new variables
newVariables = {
...newVariables,
Expand Down Expand Up @@ -163,6 +169,8 @@ export const dispatchLoop = async (props: Props): Promise<Response> => {
}
]
: [],
[DispatchNodeResponseKeyEnum.newVariables]: newVariables
[DispatchNodeResponseKeyEnum.newVariables]: newVariables,
[DispatchNodeResponseKeyEnum.customFeedbacks]:
customFeedbacks.length > 0 ? customFeedbacks : undefined
};
};
65 changes: 36 additions & 29 deletions packages/service/core/workflow/dispatch/plugin/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,35 +132,41 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
appId: String(plugin.id),
...(externalProvider ? externalProvider.externalWorkflowVariables : {})
};
const { flowResponses, flowUsages, assistantResponses, runTimes, system_memories } =
await runWorkflow({
...props,
usageId: undefined,
// Rewrite stream mode
...(system_forbid_stream
? {
stream: false,
workflowStreamResponse: undefined
}
: {}),
runningAppInfo: {
id: String(plugin.id),
name: plugin.name,
// 如果系统插件有 teamId 和 tmbId,则使用系统插件的 teamId 和 tmbId(管理员指定了插件作为系统插件)
teamId: plugin.teamId || runningAppInfo.teamId,
tmbId: plugin.tmbId || runningAppInfo.tmbId,
isChildApp: true
},
const {
flowResponses,
flowUsages,
assistantResponses,
runTimes,
system_memories,
[DispatchNodeResponseKeyEnum.customFeedbacks]: customFeedbacks
} = await runWorkflow({
...props,
usageId: undefined,
// Rewrite stream mode
...(system_forbid_stream
? {
stream: false,
workflowStreamResponse: undefined
}
: {}),
runningAppInfo: {
id: String(plugin.id),
name: plugin.name,
// 如果系统插件有 teamId 和 tmbId,则使用系统插件的 teamId 和 tmbId(管理员指定了插件作为系统插件)
teamId: plugin.teamId || runningAppInfo.teamId,
tmbId: plugin.tmbId || runningAppInfo.tmbId,
isChildApp: true
},
variables: runtimeVariables,
query: serverGetWorkflowToolRunUserQuery({
pluginInputs: getWorkflowToolInputsFromStoreNodes(plugin.nodes),
variables: runtimeVariables,
query: serverGetWorkflowToolRunUserQuery({
pluginInputs: getWorkflowToolInputsFromStoreNodes(plugin.nodes),
variables: runtimeVariables,
files
}).value,
chatConfig: {},
runtimeNodes,
runtimeEdges: storeEdges2RuntimeEdges(plugin.edges)
});
files
}).value,
chatConfig: {},
runtimeNodes,
runtimeEdges: storeEdges2RuntimeEdges(plugin.edges)
});
const output = flowResponses.find((item) => item.moduleType === FlowNodeTypeEnum.pluginOutput);

const usagePoints = await computedAppToolUsage({
Expand Down Expand Up @@ -200,7 +206,8 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
acc[key] = output.pluginOutput![key];
return acc;
}, {})
: null
: null,
[DispatchNodeResponseKeyEnum.customFeedbacks]: customFeedbacks
};
} catch (error) {
return getNodeErrResponse({
Expand Down
35 changes: 3 additions & 32 deletions packages/service/core/workflow/dispatch/tools/customFeedback.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import {
DispatchNodeResponseKeyEnum,
SseResponseEventEnum
} from '@fastgpt/global/core/workflow/runtime/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { type DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
import { addCustomFeedbacks } from '../../../chat/controller';
import { textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils';

type Props = ModuleDispatchProps<{
[NodeInputKeyEnum.textareaInput]: string;
Expand All @@ -15,37 +10,13 @@ type Response = DispatchNodeResultType<{}>;

export const dispatchCustomFeedback = (props: Record<string, any>): Response => {
const {
runningAppInfo: { id: appId },
chatId,
responseChatItemId: dataId,
stream,
workflowStreamResponse,
params: { system_textareaInput: feedbackText = '' }
} = props as Props;

setTimeout(() => {
addCustomFeedbacks({
appId,
chatId,
dataId,
feedbacks: [feedbackText]
});
}, 60000);

if (stream) {
if (!chatId || !dataId) {
workflowStreamResponse?.({
event: SseResponseEventEnum.fastAnswer,
data: textAdaptGptResponse({
text: `\n\n**自定义反馈成功: (仅调试模式下展示该内容)**: "${feedbackText}"\n\n`
})
});
}
}

return {
[DispatchNodeResponseKeyEnum.nodeResponse]: {
textOutput: feedbackText
}
},
[DispatchNodeResponseKeyEnum.customFeedbacks]: [feedbackText]
};
};
Loading
Loading