From c5721184e9669bcb5b4546a6634ddfd849da1825 Mon Sep 17 00:00:00 2001 From: twwu Date: Tue, 20 Jan 2026 17:03:06 +0800 Subject: [PATCH] refactor: implement workflow resumption logic in embedded chat and update chat wrapper functionality --- .../chat/chat-with-history/chat-wrapper.tsx | 4 +- .../chat/embedded-chatbot/chat-wrapper.tsx | 39 ++++++++++++++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx index 344dbdecb7..ae5b9e35b0 100644 --- a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx +++ b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx @@ -72,7 +72,6 @@ const ChatWrapper = () => { chatList, handleSend, handleStop, - handleResume, handleSwitchSibling, isResponding: respondingState, suggestedQuestions, @@ -158,9 +157,8 @@ const ChatWrapper = () => { // Only resume the last paused workflow if (lastPausedNode) { - handleResume( + handleSwitchSibling( lastPausedNode.id, - lastPausedNode.workflow_run_id!, { onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId), onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted, diff --git a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx index 9256339662..f5fcf73d98 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -2,6 +2,7 @@ import type { FileEntity } from '../../file-uploader/types' import type { ChatConfig, ChatItem, + ChatItemInTree, OnSend, } from '../types' import { useCallback, useEffect, useMemo, useState } from 'react' @@ -128,6 +129,40 @@ const ChatWrapper = () => { setIsResponding(respondingState) }, [respondingState, setIsResponding]) + // Resume paused workflows when chat history is loaded + useEffect(() => { + if (!appPrevChatList || appPrevChatList.length === 0) + return + + // Find the last answer item with workflow_run_id that needs resumption (DFS - find deepest first) + let lastPausedNode: ChatItemInTree | undefined + const findLastPausedWorkflow = (nodes: ChatItemInTree[]) => { + nodes.forEach((node) => { + // DFS: recurse to children first + if (node.children && node.children.length > 0) + findLastPausedWorkflow(node.children) + + // Track the last node with humanInputFormDataList + if (node.isAnswer && node.workflow_run_id && node.humanInputFormDataList && node.humanInputFormDataList.length > 0) + lastPausedNode = node + }) + } + + findLastPausedWorkflow(appPrevChatList) + + // Only resume the last paused workflow + if (lastPausedNode) { + handleSwitchSibling( + lastPausedNode.id, + { + onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId), + onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted, + isPublicAPI: !isInstalledApp, + }, + ) + } + }, []) + const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => { const data: any = { query: message, @@ -157,8 +192,10 @@ const ChatWrapper = () => { const doSwitchSibling = useCallback((siblingMessageId: string) => { handleSwitchSibling(siblingMessageId, { onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId), + onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted, + isPublicAPI: !isInstalledApp, }) - }, [handleSwitchSibling, isInstalledApp, appId]) + }, [handleSwitchSibling, isInstalledApp, appId, currentConversationId, handleNewConversationCompleted]) const messageList = useMemo(() => { if (currentConversationId || chatList.length > 1)