From 21ab855871aad32ebf167f12951d50ce806a92fa Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Thu, 26 Sep 2024 18:09:01 +0800 Subject: [PATCH] perf: stream response (#2813) * perf: stream response * fold code --- .../zh-cn/docs/development/upgrading/4811.md | 41 ++++++++--------- .../dispatch/interactive/formInput.ts | 3 ++ .../core/chat/ChatContainer/ChatBox/index.tsx | 12 +++-- .../Flow/nodes/render/NodeCard.tsx | 21 +++------ projects/app/src/web/common/api/fetch.ts | 45 +++++++++++-------- .../src/web/core/chat/context/chatContext.tsx | 2 +- 6 files changed, 66 insertions(+), 58 deletions(-) diff --git a/docSite/content/zh-cn/docs/development/upgrading/4811.md b/docSite/content/zh-cn/docs/development/upgrading/4811.md index 26bb9f3f735..d669bc7dc07 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4811.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4811.md @@ -84,24 +84,25 @@ weight: 813 ## V4.8.11 更新说明 -1. +1. 新增 - 表单输入节点,允许用户在工作流中让用户输入一些信息。 2. 新增 - 循环运行节点,可传入数组进行批量调用,目前最多支持 50 长度的数组串行执行。 -3. 新增 - 聊天记录滚动加载,不再只加载 30 条。 -4. 新增 - 工作流增加触摸板优先模式。 -5. 新增 - 沙盒增加字符串转 base64 全局方法。 -6. 新增 - 支持 Openai o1 模型,需增加模型的 `defaultConfig` 配置,覆盖 `temperature`、`max_tokens` 和 `stream`配置,o1 不支持 stream 模式, 详细可重新拉取 `config.json` 配置文件查看。 -7. 新增 - AI 对话节点知识库引用,支持配置 role=system 和 role=user,已配置的过自定义提示词的节点将会保持 user 模式,其余用户将转成 system 模式。 -8. 新增 - 插件支持上传系统文件。 -9. 新增 - 插件输出,支持指定字段作为工具响应。 -10. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。 -11. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。 -12. 新增 - 保留所有模式下子应用嵌套调用的日志。 -13. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。 -14. 优化 - 工作流 handler 性能优化。 -15. 优化 - 工作流快捷键,避免调试测试时也会触发。 -16. 优化 - 流输出,切换 tab 时仍可以继续输出。 -17. 优化 - 完善外部文件知识库相关 API -18. 修复 - 知识库选择权限问题。 -19. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。 -20. 修复 - createDataset 接口,intro 为赋值。 -21. 修复 - 对话框渲染性能问题。 +3. 新增 - 节点支持折叠。 +4. 新增 - 聊天记录滚动加载,不再只加载 30 条。 +5. 新增 - 工作流增加触摸板优先模式,可以通过工作流右下角按键进行切换。 +6. 新增 - 沙盒增加字符串转 base64 全局方法(全局变量 strToBase64)。 +7. 新增 - 支持 Openai o1 模型,需增加模型的 `defaultConfig` 配置,覆盖 `temperature`、`max_tokens` 和 `stream`配置,o1 不支持 stream 模式, 详细可重新拉取 `config.json` 配置文件查看。 +8. 新增 - AI 对话节点知识库引用,支持配置 role=system 和 role=user,已配置的过自定义提示词的节点将会保持 user 模式,其余用户将转成 system 模式。 +9. 新增 - 插件支持上传系统文件。 +10. 新增 - 插件输出,支持指定字段作为工具响应。 +11. 新增 - 支持工作流嵌套子应用时,可以设置`非流模式`,同时简易模式也可以选择工作流作为插件了,简易模式调用子应用时,都将强制使用非流模式。 +12. 新增 - 调试模式下,子应用调用,支持返回详细运行数据。 +13. 新增 - 保留所有模式下子应用嵌套调用的日志。 +14. 优化 - 工作流嵌套层级限制 20 层,避免因编排不合理导致的无限死循环。 +15. 优化 - 工作流 handler 性能优化。 +16. 优化 - 工作流快捷键,避免调试测试时也会触发复制和回退。 +17. 优化 - 流输出,切换浏览器 Tab 后仍可以继续输出。 +18. 优化 - 完善外部文件知识库相关 API +19. 修复 - 知识库选择权限问题。 +20. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。 +21. 修复 - createDataset 接口,intro 为赋值。 +22. 修复 - 对话框渲染性能问题。 diff --git a/packages/service/core/workflow/dispatch/interactive/formInput.ts b/packages/service/core/workflow/dispatch/interactive/formInput.ts index 2235603e3c5..9e575feccd5 100644 --- a/packages/service/core/workflow/dispatch/interactive/formInput.ts +++ b/packages/service/core/workflow/dispatch/interactive/formInput.ts @@ -20,6 +20,9 @@ type FormInputResponse = DispatchNodeResultType<{ [NodeOutputKeyEnum.formInputResult]?: Record; }>; +/* + 用户输入都内容,将会以 JSON 字符串格式进入工作流,可以从 query 的 text 中获取。 +*/ export const dispatchFormInput = async (props: Props): Promise => { const { histories, diff --git a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx index 4eabba978ef..e00bac7b52c 100644 --- a/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx +++ b/projects/app/src/components/core/chat/ChatContainer/ChatBox/index.tsx @@ -199,13 +199,15 @@ const ChatBox = ( // 聊天信息生成中……获取当前滚动条位置,判断是否需要滚动到底部 const { run: generatingScroll } = useThrottleFn( - () => { + (force?: boolean) => { if (!ChatBoxRef.current) return; const isBottom = ChatBoxRef.current.scrollTop + ChatBoxRef.current.clientHeight + 150 >= ChatBoxRef.current.scrollHeight; - isBottom && scrollToBottom('auto'); + if (isBottom || force) { + scrollToBottom('auto'); + } }, { wait: 100 @@ -321,7 +323,9 @@ const ChatBox = ( return item; }) ); - generatingScroll(); + + const forceScroll = event === SseResponseEventEnum.interactive; + generatingScroll(forceScroll); } ); @@ -529,7 +533,7 @@ const ChatBox = ( }); } - generatingScroll(); + generatingScroll(true); isPc && TextareaDom.current?.focus(); }, 100); diff --git a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/NodeCard.tsx b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/NodeCard.tsx index 2f23788d1ed..458817787d6 100644 --- a/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/NodeCard.tsx +++ b/projects/app/src/pages/app/detail/components/WorkflowComponents/Flow/nodes/render/NodeCard.tsx @@ -180,21 +180,12 @@ const NodeCard = (props: Props) => { }); }} > - {!isFolded ? ( - - ) : ( - - )} + )} diff --git a/projects/app/src/web/common/api/fetch.ts b/projects/app/src/web/common/api/fetch.ts index e03f87640fe..0719d0666cc 100644 --- a/projects/app/src/web/common/api/fetch.ts +++ b/projects/app/src/web/common/api/fetch.ts @@ -22,6 +22,16 @@ export type StreamResponseType = { responseText: string; [DispatchNodeResponseKeyEnum.nodeResponse]: ChatHistoryItemResType[]; }; +type ResponseQueueItemType = + | { event: SseResponseEventEnum.fastAnswer | SseResponseEventEnum.answer; text: string } + | { event: SseResponseEventEnum.interactive; [key: string]: any } + | { + event: + | SseResponseEventEnum.toolCall + | SseResponseEventEnum.toolParams + | SseResponseEventEnum.toolResponse; + [key: string]: any; + }; class FatalError extends Error {} export const streamFetch = ({ @@ -31,22 +41,14 @@ export const streamFetch = ({ abortCtrl }: StreamFetchProps) => new Promise(async (resolve, reject) => { + // First res const timeoutId = setTimeout(() => { abortCtrl.abort('Time out'); }, 60000); // response data let responseText = ''; - let responseQueue: ( - | { event: SseResponseEventEnum.fastAnswer | SseResponseEventEnum.answer; text: string } - | { - event: - | SseResponseEventEnum.toolCall - | SseResponseEventEnum.toolParams - | SseResponseEventEnum.toolResponse; - [key: string]: any; - } - )[] = []; + let responseQueue: ResponseQueueItemType[] = []; let errMsg: string | undefined; let responseData: ChatHistoryItemResType[] = []; let finished = false; @@ -84,7 +86,7 @@ export const streamFetch = ({ } if (responseQueue.length > 0) { - const fetchCount = Math.max(1, Math.round(responseQueue.length / 20)); + const fetchCount = Math.max(1, Math.round(responseQueue.length / 30)); for (let i = 0; i < fetchCount; i++) { const item = responseQueue[i]; onMessage(item); @@ -100,13 +102,20 @@ export const streamFetch = ({ return finish(); } - document.hidden - ? setTimeout(animateResponseText, 16) - : requestAnimationFrame(animateResponseText); + requestAnimationFrame(animateResponseText); } // start animation animateResponseText(); + const pushDataToQueue = (data: ResponseQueueItemType) => { + // If the document is hidden, the data is directly sent to the front end + responseQueue.push(data); + + if (document.hidden) { + animateResponseText(); + } + }; + try { // auto complete variables const variables = data?.variables || {}; @@ -171,14 +180,14 @@ export const streamFetch = ({ if (event === SseResponseEventEnum.answer) { const text = parseJson.choices?.[0]?.delta?.content || ''; for (const item of text) { - responseQueue.push({ + pushDataToQueue({ event, text: item }); } } else if (event === SseResponseEventEnum.fastAnswer) { const text = parseJson.choices?.[0]?.delta?.content || ''; - responseQueue.push({ + pushDataToQueue({ event, text }); @@ -187,7 +196,7 @@ export const streamFetch = ({ event === SseResponseEventEnum.toolParams || event === SseResponseEventEnum.toolResponse ) { - responseQueue.push({ + pushDataToQueue({ event, ...parseJson }); @@ -204,7 +213,7 @@ export const streamFetch = ({ variables: parseJson }); } else if (event === SseResponseEventEnum.interactive) { - responseQueue.push({ + pushDataToQueue({ event, ...parseJson }); diff --git a/projects/app/src/web/core/chat/context/chatContext.tsx b/projects/app/src/web/core/chat/context/chatContext.tsx index e695239bbcb..3f871fac7f1 100644 --- a/projects/app/src/web/core/chat/context/chatContext.tsx +++ b/projects/app/src/web/core/chat/context/chatContext.tsx @@ -200,7 +200,7 @@ const ChatContextProvider = ({ ); } else { // Chat history not exists - loadHistories(); + loadHistories(true); } }, [histories, loadHistories, setHistories]