拓冰建站拓冰建站
首页 / 资讯中心 / 正文

FastGPT 流恢复场景下「表单输入」节点已提交文件列表丢失问题的组合修复方案

FastGPT 流恢复场景下「表单输入」节点已提交文件列表丢失问题的组合修复方案【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT导读本技术指南聚焦 FastGPT 聊天应用projects/app中一个典型的流式恢复stream resume边界问题当用户在「表单输入」userInput/formInput交互节点中提交了文件fileSelect后刷新页面自动流恢复会把表单输入节点渲染出来但已提交的字段值——尤其是文件列表——会在恢复过程中丢失。本文从数据流与根因入手讲解前端采用的「恢复事件回填 completed 覆盖保护 过期交互去重 渲染层兜底」组合修复方案覆盖核心源码位置、数据结构、匹配规则与测试验证可直接作为同类流恢复问题的排查与修复参考。问题背景流恢复后已提交表单值消失FastGPT 支持在会话中断如页面刷新后自动恢复仍在服务端生成中的工作流auto resume。恢复过程中「表单输入」节点可以恢复出交互节点本身但已提交的表单值没有完整恢复其中fileSelect文件列表最容易暴露。典型表现见 设计文档刷新页面后历史记录接口getRecords_v2返回的交互节点中有interactive.params.inputForm[].value已提交表单值理论上可以从历史数据恢复。一旦自动流恢复开始页面上仍能看到表单输入交互节点但文件上传区域只剩禁用上传框已提交字段值消失文件字段表现为文件列表消失。恢复流中有flowNodeResponse事件其formInputResult数据形如{ moduleType: formInput, formInputResult: { File: [ http://localhost:3000/api/system/file/download/xxx?filenameH6%E4%BA%A7%E5%93%81%E6%A6%82%E8%BF%B0V1.5_tBF8kj.docx ] }, nodeId: j1Ifb41hX176ezmo }用户期望表单输入节点的已提交字段值恢复到「用户交互表单节点」内部其中fileSelect应恢复为文件列表而不是展示在 AI 普通回复气泡或文本区域里。相关数据结构两种形态的字段值历史记录里的表单交互节点getRecords_v2返回的交互节点中字段值已经存在其中文件字段是FileSelector可渲染的结构{ interactive: { type: userInput, params: { submitted: true, inputForm: [ { type: fileSelect, key: File, value: [ { key: chat/xxx.docx, name: H6产品概述V1.5.docx, type: file, url: http://localhost:3000/api/system/file/download/xxx } ] } ] }, entryNodeIds: [j1Ifb41hX176ezmo] } }恢复流里的节点响应flowNodeResponse.formInputResult只包含字段结果不是完整的表单渲染结构{ formInputResult: { File: [http://localhost:3000/api/system/file/download/xxx?filenamefile.docx] } }因此前端不能直接把formInputResult当成 AI 文本渲染也不能只展示在响应详情里需要把它转换并回填到交互表单的inputForm[].value。根因分析1. 表单控件本身不是根因以fileSelect为例FileSelector接收以下两类值都能渲染文件列表[{ name: file.docx, url: https://example.com/file.docx }]或[{ name: file.docx, key: chat/xxx/file.docx }]页面能看到禁用上传区域说明交互节点已经渲染出来submittedtrue已经生效真正缺失的是交互节点inputForm[].value——fileSelect只是表现为传给FileSelector的 value 为空。2. 恢复事件没有天然回填表单值恢复流的flowNodeResponse会进入generatingMessage并追加到当前 AI 记录的responseData。但原始实现只保存节点响应详情没有把formInputResult.File回填到已提交的interactive.params.inputForm[].value。结果交互节点还在但字段值仍是空数组。3. completed records 会覆盖当前恢复态流恢复结束时后端会返回completedChat.records前端用它覆盖当前chatRecords。这个覆盖有两个风险恢复过程中刚回填到交互节点的字段值被 completed records 里空的inputForm.value覆盖当前已回填的交互节点和 completed records 里的交互节点dataId不一定完全一致只按dataId合并可能漏掉。因此需要在 completed 覆盖阶段保留 submitted interactive 中已经恢复出来的字段值。4. 仍需要渲染层兜底即使状态层做了回填和 merge仍可能出现中间态或边界情况responseData已经有formInputResultinteractive.params.inputForm[].value被后续 records 替换成空渲染表单时只看inputForm.value导致已提交表单值仍然不显示。所以渲染表单时也应当能从同一条 AI 消息的responseData.formInputResult还原字段默认值。修复方案四个修复点的组合本次修复不是单个方案而是一个组合修复最终采用恢复事件回填 completed 覆盖保护 过期交互去重 渲染层兜底四个修复点分别覆盖不同阶段的问题恢复流事件到达时把节点结果写回表单交互节点恢复完成 records 覆盖时保留已经写回的表单值恢复流重复推送交互节点时避免过期未提交交互覆盖已提交交互渲染表单时如果状态值仍为空从同条消息的responseData.formInputResult做最后兜底。修复点一恢复事件回填 submitted 表单交互节点源码位置触发入口projects/app/src/components/core/chat/ChatContainer/ChatBox/hooks/useChatGenerate.tsgeneratingMessage收到flowNodeResponse核心实现projects/app/src/components/core/chat/ChatContainer/ChatBox/utils/interactive.ts中的refreshSubmittedFormInteractiveValues逻辑generatingMessage收到flowNodeResponse如果nodeResponse.formInputResult存在调用refreshSubmittedFormInteractiveValues在当前chatRecords里寻找已提交的userInput交互节点匹配方式优先用interactive.entryNodeIds.includes(nodeResponse.nodeId)如果只有一个 submitted 表单交互节点并且字段 key 能匹配也允许兜底匹配覆盖dataId漂移场景对fileSelect字段把formInputResult.File: string[]转成[ { name: file.docx, url: http://localhost:3000/api/system/file/download/xxx?filenamefile.docx } ]从源码看转换复用 FormInputResult.tsx 中的normalizeFormInputResultFile保证文件名从filenamequery 或 URL path 中取而 interactive.ts 中fileSelect字段还通过resolveFormInputFileValues优先保留历史中持久化的key/url name/type仅当原始值缺失时才用运行结果 URL 兜底避免短签名链接覆盖文件名与类型。修复点二completed records 覆盖时保留已回填交互值源码位置projects/app/src/components/core/chat/ChatContainer/ChatBox/utils/resume.ts中的mergeResumeCompletedChatRecordsresume.ts逻辑建立当前 AI record map对 completed records 的 AI 消息做合并保留恢复过程中 replay 出来的responseData通过appendNodeResponseByParent/mergeNodeResponseDataByIdAndParent去重合并保留当前 submitteduserInput交互节点里的inputForm.value如果 completed record 能按dataId找到当前 record则使用对应 current values如果按dataId找不到则从当前所有 AI records 中寻找 submitted interactive并按交互身份匹配。交互身份匹配规则见 resume.ts 的areSameInteractivetype 相同 (usageId 相同 || entryNodeIds 数组相同)新数据还会优先使用每次暂停生成的interactiveId区分同一节点的不同触发轮次旧数据没有interactiveId时回退到上述历史规则。这个逻辑覆盖 completed records 中交互节点dataId变化的情况。修复点三跳过过期的未提交恢复交互源码位置projects/app/src/components/core/chat/ChatContainer/ChatBox/utils/resume.ts中的shouldAppendResumeInteractiveresume.ts逻辑如果当前已经有同一个 submitteduserInput或agentAsk交互节点恢复流里又来了同一个未提交 interactive则不再 append。作用避免恢复过程中重新插入一个空表单避免 submitted 表单被未提交表单视觉上覆盖。其中「同一个交互」同样通过extractDeepestInteractive取出最内层交互后用areSameInteractive判定避免 child 包装层造成的误判。修复点四渲染层从responseData.formInputResult兜底恢复表单值源码位置projects/app/src/components/core/chat/ChatContainer/ChatBox/components/ChatItem.tsxprojects/app/src/components/core/chat/components/AIResponseBox/index.tsxprojects/app/src/components/core/chat/components/AIResponseBox/RenderUserFormInteractive.tsx调用链ChatItem - AIContentCard - AIResponseBox - RenderUserFormInteractive - FormInputComponent - InputRender - FileSelector新增传参ChatItem把当前 AI 消息的chat.responseData传给AIContentCardAIContentCard传给AIResponseBoxAIResponseBox在渲染userInput时传给RenderUserFormInteractive。从 RenderUserFormInteractive.tsx 的源码看getInputFormValueFromResponseData生成defaultValues时从responseData里倒序查找带formInputResult的节点响应优先匹配nodeId与interactive.entryNodeIds未指定nodeId或entryNodeIds为空时取最近一条取同名字段例如File如果formInputResult中存在该字段则优先使用该字段值作为 submitted 表单的渲染默认值对普通输入、选择、数字等字段直接使用formInputResult[key]对fileSelect字段额外把 URL 数组归一化为FileSelector可渲染的{ name, url }[]复用resolveFormInputFileValues。作用即使状态层inputForm.value被 completed records 覆盖为空只要同一条 AI 消息还带有responseData.formInputResult表单节点仍然能渲染出已提交表单值。文件结果归一化与展示文件归一化逻辑集中在projects/app/src/components/core/chat/components/FormInputResult.tsxgetFilenameFromFormInputFileUrlFormInputResult.tsxFastGPT 签名下载链接通常把真实文件名放在filenamequery 中path 段只是不可读的 token。解析优先级为query filename URL path 最后一段 原 URL 字符串非法 URL 直接返回入参避免展示层抛错。normalizeFormInputResultFile兼容string仅存 URL与{ name?, url }两种形态name缺失时从 URL 推导无效输入返回undefined便于.filter(Boolean)。resolveFormInputFileValues首次提交时保存的key/url name/type是唯一真源工作流节点生成的签名 URL 仅在旧历史缺少原始存储值时兜底。此外projects/app/src/components/core/chat/components/WholeResponseModal.tsx在响应详情弹窗里挂载FormInputResult以格式化 JSON 展示formInputResult。设计文档特别注明详情弹窗展示不是本 bug 的核心修复核心修复是交互节点内已提交表单值恢复文件列表只是fileSelect字段的展示结果。非目标本 PR 明确不做以下事情见 设计文档不把formInputResult渲染到 AI 普通文字回复气泡里不改变FileSelector的基础交互行为不改变后端formInputResult的输出结构不迁移历史数据不处理非userInput类型的假想表单交互类型当前类型定义中不存在agentPlanAskUserForm。测试覆盖与验证新增/涉及的测试文件projects/app/test/components/core/chat/ChatContainer/ChatBox/utils.test.tsprojects/app/test/components/core/chat/components/FormInputResult.test.tsprojects/app/test/components/core/app/FileSelector/utils.test.ts已覆盖场景flowNodeResponse.formInputResult.File能回填到 submitteduserInput的inputForm[].valuenodeId不匹配但只有一个 submitted 表单交互节点时可以按字段 key 兜底回填completed records 覆盖时保留已恢复的 submitted interactive 字段值completed records 中交互节点dataId变化时仍能按交互身份保留字段值FormInputResult能从签名 URL 的filenamequery 中解析文件名FileSelector的值清洗函数能保留可渲染 URL 字段值。从 utils.test.ts 的refreshSubmittedFormInteractiveValues用例可以看到典型断言当运行结果只包含短签名 URL 时若历史已持久化key/url name/type回填后仍保留原始文件元数据report.pdf当历史value为空时则写入从签名 URL 解析出的{ name: H6产品概述V1.5_tBF8kj.docx, url: signedUrl }当entryNodeIds完全不匹配时函数返回原histories引用避免多余渲染。局部测试命令source ~/.zshrc /dev/null 21; pnpm --filter fastgpt/app test test/components/core/chat/ChatContainer/ChatBox/utils.test.ts test/components/core/chat/components/FormInputResult.test.ts test/components/core/app/FileSelector/utils.test.ts设计文档记录当前结果Test Files 3 passed Tests 32 passed已知验证情况用户本地验证恢复流开始后表单输入节点内文件列表已能正常展示touched files eslint 无 error仅剩utils.ts中error is defined but never used一条已有 warningfastgpt/app typecheck在当前分支仍有无关类型错误ChatItem.tsx的stepId/stepTitle类型声明缺失、ResponseTags.tsx/RenderResponseDetail.tsx缺chatTime参数、WholeResponseModal.tsx中queryExtensionResult类型名与当前 schema 不一致这些不是本修复新增逻辑引入的问题。结论与排查启示该问题本质是「恢复流节点输出」与「交互节点持久化值」两条数据通道在恢复时序上没有对齐节点输出只进了responseData而表单渲染只看inputForm.value加上 completed records 的覆盖时机最终造成已提交文件列表在页面上消失。修复从四个层面织成防线——事件到达时回填、覆盖时保护、重复交互去重、渲染时兜底每一层都聚焦一个可独立验证的阶段。对于 FastGPT 中其他交互恢复类问题如 agentAsk 答案回写、dataId漂移、过期交互覆盖utils/interactive.ts 与 utils/resume.ts 中的areSameInteractive身份匹配、extractDeepestInteractive深交互提取等模式同样可以复用是排查流恢复前端状态丢失问题的首选切入点。【免费下载链接】FastGPTFastGPT is a knowledge-based platform built on the LLMs, offers a comprehensive suite of out-of-the-box capabilities such as data processing, RAG retrieval, and visual AI workflow orchestration, letting you easily develop and deploy complex question-answering systems without the need for extensive setup or configuration.项目地址: https://gitcode.com/GitHub_Trending/fa/FastGPT创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门