diff --git a/docSite/content/zh-cn/docs/development/upgrading/4812.md b/docSite/content/zh-cn/docs/development/upgrading/4812.md index ca97781b20be..45dc70c84736 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4812.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4812.md @@ -9,6 +9,26 @@ weight: 812 ## 更新指南 +### 1. 做好数据备份 + +### 2. 修改镜像 + +- 更新 FastGPT 镜像 tag: v4.8.12-beta +- 更新 FastGPT 商业版镜像 tag: v4.8.12-beta +- Sandbox 镜像,可以不更新 + + +### 3. 商业版执行初始化 + +从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 商业版域名**。 + +```bash +curl --location --request POST 'https://{{host}}/api/admin/init/4812' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` + +会初始化应用和知识库的成员组数据。 ## 更新说明 @@ -29,3 +49,6 @@ weight: 812 15. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。 16. 修复 - 用户交互节点未阻塞流程。 17. 修复 - 新建 APP,有时候会导致空指针报错。 +18. 修复 - 拥有多个循环节点时,错误运行。 +19. 修复 - 循环节点中修改变量,无法传递。 +20. 修复 - 非 stream 模式,嵌套子应用/插件执行时无法获取子应用响应。 diff --git a/packages/global/core/workflow/template/system/variableUpdate/index.tsx b/packages/global/core/workflow/template/system/variableUpdate/index.tsx index 82b486d00ad0..3ad693f67541 100644 --- a/packages/global/core/workflow/template/system/variableUpdate/index.tsx +++ b/packages/global/core/workflow/template/system/variableUpdate/index.tsx @@ -18,7 +18,7 @@ export const VariableUpdateNode: FlowNodeTemplateType = { name: i18nT('workflow:variable_update'), intro: i18nT('workflow:update_specified_node_output_or_global_variable'), showStatus: false, - isTool: false, + isTool: true, version: '481', inputs: [ { diff --git a/packages/service/core/workflow/dispatch/agent/runTool/index.ts b/packages/service/core/workflow/dispatch/agent/runTool/index.ts index 325f1260b016..2b34f86dd8c3 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/index.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/index.ts @@ -211,18 +211,7 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< }); // flat child tool response - let newVariables: Record = props.variables; - const childToolResponse = dispatchFlowResponse - .map((item) => { - // Computed new variables - newVariables = { - ...newVariables, - ...item.newVariables - }; - - return item.flowResponses; - }) - .flat(); + const childToolResponse = dispatchFlowResponse.map((item) => item.flowResponses).flat(); // concat tool usage const totalPointsUsage = @@ -261,7 +250,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise< }, ...flatUsages ], - [DispatchNodeResponseKeyEnum.newVariables]: newVariables, [DispatchNodeResponseKeyEnum.interactive]: toolWorkflowInteractiveResponse }; }; diff --git a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts index 09fb79ed21d3..b730ae66a41c 100644 --- a/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts +++ b/packages/service/core/workflow/dispatch/agent/runTool/toolChoice.ts @@ -27,7 +27,7 @@ import { getNanoid, sliceStrStartEnd } from '@fastgpt/global/common/string/tools import { addLog } from '../../../../../common/system/log'; import { toolValueTypeList } from '@fastgpt/global/core/workflow/constants'; import { WorkflowInteractiveResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; -import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants'; +import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; type ToolRunResponseType = { toolRunResponse: DispatchFlowResponse; diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index 39a26ff10c62..9d1f9fa51e3b 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -41,7 +41,8 @@ import { dispatchPluginOutput } from './plugin/runOutput'; import { removeSystemVariable, valueTypeFormat } from './utils'; import { filterWorkflowEdges, - checkNodeRunStatus + checkNodeRunStatus, + textAdaptGptResponse } from '@fastgpt/global/core/workflow/runtime/utils'; import { ChatNodeUsageType } from '@fastgpt/global/support/wallet/bill/type'; import { dispatchRunTools } from './agent/runTool/index'; @@ -161,6 +162,20 @@ export async function dispatchWorkFlow(data: Props): Promise { + setTimeout(() => { + props?.workflowStreamResponse?.({ + event: SseResponseEventEnum.answer, + data: textAdaptGptResponse({ + text: '' + }) + }); + sendStreamTimerSign(); + }, 10000); + }; + sendStreamTimerSign(); } variables = { @@ -592,56 +607,60 @@ export async function dispatchWorkFlow(data: Props): Promise item.isEntry); - // reset entry - runtimeNodes.forEach((item) => { - // Interactive node is not the entry node, return interactive result - if ( - item.flowNodeType !== FlowNodeTypeEnum.userSelect && - item.flowNodeType !== FlowNodeTypeEnum.formInput && - item.flowNodeType !== FlowNodeTypeEnum.tools - ) { - item.isEntry = false; - } - }); - await Promise.all(entryNodes.map((node) => checkNodeCanRun(node))); - - // focus try to run pluginOutput - const pluginOutputModule = runtimeNodes.find( - (item) => item.flowNodeType === FlowNodeTypeEnum.pluginOutput - ); - if (pluginOutputModule && props.mode !== 'debug') { - await nodeRunWithActive(pluginOutputModule); - } + try { + // start process width initInput + const entryNodes = runtimeNodes.filter((item) => item.isEntry); + // reset entry + runtimeNodes.forEach((item) => { + // Interactive node is not the entry node, return interactive result + if ( + item.flowNodeType !== FlowNodeTypeEnum.userSelect && + item.flowNodeType !== FlowNodeTypeEnum.formInput && + item.flowNodeType !== FlowNodeTypeEnum.tools + ) { + item.isEntry = false; + } + }); + await Promise.all(entryNodes.map((node) => checkNodeCanRun(node))); - // Interactive node - const interactiveResult = (() => { - if (nodeInteractiveResponse) { - const interactiveAssistant = handleInteractiveResult({ - entryNodeIds: nodeInteractiveResponse.entryNodeIds, - interactiveResponse: nodeInteractiveResponse.interactiveResponse - }); - chatAssistantResponse.push(interactiveAssistant); - return interactiveAssistant.interactive; + // focus try to run pluginOutput + const pluginOutputModule = runtimeNodes.find( + (item) => item.flowNodeType === FlowNodeTypeEnum.pluginOutput + ); + if (pluginOutputModule && props.mode !== 'debug') { + await nodeRunWithActive(pluginOutputModule); } - })(); - return { - flowResponses: chatResponses, - flowUsages: chatNodeUsages, - debugResponse: { - finishedNodes: runtimeNodes, - finishedEdges: runtimeEdges, - nextStepRunNodes: debugNextStepRunNodes - }, - workflowInteractiveResponse: interactiveResult, - [DispatchNodeResponseKeyEnum.runTimes]: workflowRunTimes, - [DispatchNodeResponseKeyEnum.assistantResponses]: - mergeAssistantResponseAnswerText(chatAssistantResponse), - [DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse, - newVariables: removeSystemVariable(variables) - }; + // Interactive node + const interactiveResult = (() => { + if (nodeInteractiveResponse) { + const interactiveAssistant = handleInteractiveResult({ + entryNodeIds: nodeInteractiveResponse.entryNodeIds, + interactiveResponse: nodeInteractiveResponse.interactiveResponse + }); + chatAssistantResponse.push(interactiveAssistant); + return interactiveAssistant.interactive; + } + })(); + + return { + flowResponses: chatResponses, + flowUsages: chatNodeUsages, + debugResponse: { + finishedNodes: runtimeNodes, + finishedEdges: runtimeEdges, + nextStepRunNodes: debugNextStepRunNodes + }, + workflowInteractiveResponse: interactiveResult, + [DispatchNodeResponseKeyEnum.runTimes]: workflowRunTimes, + [DispatchNodeResponseKeyEnum.assistantResponses]: + mergeAssistantResponseAnswerText(chatAssistantResponse), + [DispatchNodeResponseKeyEnum.toolResponses]: toolRunResponse, + newVariables: removeSystemVariable(variables) + }; + } catch (error) { + return Promise.reject(error); + } } /* get system variable */ diff --git a/packages/service/core/workflow/dispatch/loop/runLoop.ts b/packages/service/core/workflow/dispatch/loop/runLoop.ts index d6c25b761e27..58711c834047 100644 --- a/packages/service/core/workflow/dispatch/loop/runLoop.ts +++ b/packages/service/core/workflow/dispatch/loop/runLoop.ts @@ -7,6 +7,7 @@ import { import { dispatchWorkFlow } from '..'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { AIChatItemValueItemType, ChatHistoryItemResType } from '@fastgpt/global/core/chat/type'; +import { cloneDeep } from 'lodash'; type Props = ModuleDispatchProps<{ [NodeInputKeyEnum.loopInputArray]: Array; @@ -19,6 +20,7 @@ type Response = DispatchNodeResultType<{ export const dispatchLoop = async (props: Props): Promise => { const { params, + runtimeEdges, runtimeNodes, user, node: { name } @@ -28,7 +30,10 @@ export const dispatchLoop = async (props: Props): Promise => { if (!Array.isArray(loopInputArray)) { return Promise.reject('Input value is not an array'); } - if (loopInputArray.length > 50) { + const maxLength = process.env.WORKFLOW_MAX_LOOP_TIMES + ? Number(process.env.WORKFLOW_MAX_LOOP_TIMES) + : 50; + if (loopInputArray.length > maxLength) { return Promise.reject('Input array length cannot be greater than 50'); } @@ -39,27 +44,25 @@ export const dispatchLoop = async (props: Props): Promise => { let newVariables: Record = props.variables; for await (const item of loopInputArray) { + runtimeNodes.forEach((node) => { + if ( + childrenNodeIdList.includes(node.nodeId) && + node.flowNodeType === FlowNodeTypeEnum.loopStart + ) { + node.isEntry = true; + node.inputs = node.inputs.map((input) => + input.key === NodeInputKeyEnum.loopStartInput + ? { + ...input, + value: item + } + : input + ); + } + }); const response = await dispatchWorkFlow({ ...props, - runtimeNodes: runtimeNodes.map((node) => - node.flowNodeType === FlowNodeTypeEnum.loopStart - ? { - ...node, - isEntry: true, - inputs: node.inputs.map((input) => - input.key === NodeInputKeyEnum.loopStartInput - ? { - ...input, - value: item - } - : input - ) - } - : { - ...node, - isEntry: false - } - ) + runtimeEdges: cloneDeep(runtimeEdges) }); const loopOutputValue = response.flowResponses.find( diff --git a/packages/service/core/workflow/dispatch/plugin/run.ts b/packages/service/core/workflow/dispatch/plugin/run.ts index 19f53aed021a..32dad35aeffd 100644 --- a/packages/service/core/workflow/dispatch/plugin/run.ts +++ b/packages/service/core/workflow/dispatch/plugin/run.ts @@ -113,11 +113,10 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise => { const usagePoints = flowUsages.reduce((sum, item) => sum + (item.totalPoints || 0), 0); return { - assistantResponses: childStreamResponse ? assistantResponses : [], + assistantResponses: system_forbid_stream ? [] : assistantResponses, [DispatchNodeResponseKeyEnum.runTimes]: runTimes, [DispatchNodeResponseKeyEnum.nodeResponse]: { moduleLogo: appData.avatar, diff --git a/projects/app/.env.template b/projects/app/.env.template index 4d8d8175c510..0bc4cbbc13f6 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -39,4 +39,6 @@ STORE_LOG_LEVEL=warn # 安全配置 # 工作流最大运行次数,避免极端的死循环情况 -WORKFLOW_MAX_RUN_TIMES=500 \ No newline at end of file +WORKFLOW_MAX_RUN_TIMES=500 +# 循环最大运行次数,避免极端的死循环情况 +WORKFLOW_MAX_LOOP_TIMES=50 \ No newline at end of file diff --git a/projects/app/package.json b/projects/app/package.json index 6d104964e6cb..51b42cf002a3 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.8.11", + "version": "4.8.12", "private": false, "scripts": { "dev": "next dev", diff --git a/projects/app/src/components/common/folder/MoveModal.tsx b/projects/app/src/components/common/folder/MoveModal.tsx index b89ab6bd491a..a1d6f42c1805 100644 --- a/projects/app/src/components/common/folder/MoveModal.tsx +++ b/projects/app/src/components/common/folder/MoveModal.tsx @@ -172,7 +172,11 @@ const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose, moveHint onClose={onClose} > - {moveHint && } + {moveHint && ( + + + + )} diff --git a/projects/app/src/pages/app/detail/components/Workflow/Header.tsx b/projects/app/src/pages/app/detail/components/Workflow/Header.tsx index bb4f7af612ea..53d54ac70a39 100644 --- a/projects/app/src/pages/app/detail/components/Workflow/Header.tsx +++ b/projects/app/src/pages/app/detail/components/Workflow/Header.tsx @@ -34,7 +34,11 @@ const Header = () => { const { t } = useTranslation(); const { isPc } = useSystem(); const router = useRouter(); - const { toast } = useToast(); + const { toast: backSaveToast } = useToast({ + containerStyle: { + mt: '60px' + } + }); const { appDetail, onSaveApp, currentTab } = useContextSelector(AppContext, (v) => v); const isV2Workflow = appDetail?.version === 'v2'; @@ -273,7 +277,7 @@ const Header = () => { await onClickSave({}); onCloseBackConfirm(); onBack(); - toast({ + backSaveToast({ status: 'success', title: t('app:saved_success'), position: 'top-right'