From d0a713e117f2a099494c264ad8d1eaa0cc051b66 Mon Sep 17 00:00:00 2001 From: twwu Date: Sun, 4 Jan 2026 15:27:40 +0800 Subject: [PATCH] feat: integrate human input form submission handling in chat components --- .../chat/chat-with-history/chat-wrapper.tsx | 14 +- web/app/components/base/chat/chat/hooks.ts | 634 ++++++++++-------- .../chat/embedded-chatbot/chat-wrapper.tsx | 12 +- 3 files changed, 371 insertions(+), 289 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 25ff39370f..cb2e4a8ba6 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 @@ -15,7 +15,9 @@ import { fetchSuggestedQuestions, getUrl, stopChatMessageResponding, + submitHumanInputForm, } from '@/service/share' +import { submitHumanInputForm as submitHumanInputFormService } from '@/service/workflow' import { TransferMethod } from '@/types/app' import { cn } from '@/utils/classnames' import { formatBooleanInputs } from '@/utils/model-config' @@ -146,7 +148,7 @@ const ChatWrapper = () => { isPublicAPI: !isInstalledApp, }, ) - }, [chatList, handleNewConversationCompleted, handleSend, currentConversationId, currentConversationInputs, newConversationInputs, isInstalledApp, appId]) + }, [inputsForms, currentConversationId, currentConversationInputs, newConversationInputs, chatList, handleSend, isInstalledApp, appId, handleNewConversationCompleted]) const doRegenerate = useCallback((chatItem: ChatItem, editedQuestion?: { message: string, files?: FileEntity[] }) => { const question = editedQuestion ? chatItem : chatList.find(item => item.id === chatItem.parentMessageId)! @@ -159,7 +161,14 @@ const ChatWrapper = () => { return chatList // Without messages we are in the welcome screen, so hide the opening statement from chatlist return chatList.filter(item => !item.isOpeningStatement) - }, [chatList]) + }, [chatList, currentConversationId]) + + const handleSubmitHumanInputForm = useCallback(async (formID: string, formData: any) => { + if (isInstalledApp) + await submitHumanInputFormService(formID, formData) + else + await submitHumanInputForm(formID, formData) + }, [isInstalledApp]) const [collapsed, setCollapsed] = useState(!!currentConversationId) @@ -268,6 +277,7 @@ const ChatWrapper = () => { inputsForm={inputsForms} onRegenerate={doRegenerate} onStopResponding={handleStop} + onHumanInputFormSubmit={handleSubmitHumanInputForm} chatNode={( <> {chatNode} diff --git a/web/app/components/base/chat/chat/hooks.ts b/web/app/components/base/chat/chat/hooks.ts index 9b8a9b11dc..2545c63fa8 100644 --- a/web/app/components/base/chat/chat/hooks.ts +++ b/web/app/components/base/chat/chat/hooks.ts @@ -8,6 +8,9 @@ import type { InputForm } from './type' import type AudioPlayer from '@/app/components/base/audio-btn/audio' import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { Annotation } from '@/models/log' +import type { + IOtherOptions, +} from '@/service/base' import { uniqBy } from 'es-toolkit/compat' import { noop } from 'es-toolkit/function' import { produce, setAutoFreeze } from 'immer' @@ -27,9 +30,12 @@ import { getProcessedFilesFromResponse, } from '@/app/components/base/file-uploader/utils' import { useToastContext } from '@/app/components/base/toast' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' +import { NodeRunningStatus, WorkflowRunningStatus } from '@/app/components/workflow/types' import useTimestamp from '@/hooks/use-timestamp' -import { ssePost } from '@/service/base' +import { + sseGet, + ssePost, +} from '@/service/base' import { TransferMethod } from '@/types/app' import { getThreadMessages } from '../utils' import { @@ -102,7 +108,7 @@ export const useChat = ( } } return ret - }, [threadMessages, config?.opening_statement, getIntroduction, config?.suggested_questions]) + }, [threadMessages, config, getIntroduction]) useEffect(() => { setAutoFreeze(false) @@ -319,238 +325,249 @@ export const useChat = ( return player } - ssePost( - url, - { - body: bodyParams, - }, - { - isPublicAPI, - onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => { - if (!isAgentMode) { - responseItem.content = responseItem.content + message - } - else { - const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1] - if (lastThought) - lastThought.thought = lastThought.thought + message // need immer setAutoFreeze - } - - if (messageId && !hasSetResponseId) { - questionItem.id = `question-${messageId}` - responseItem.id = messageId - responseItem.parentMessageId = questionItem.id - hasSetResponseId = true - } - - if (isFirstMessage && newConversationId) - conversationId.current = newConversationId - - taskIdRef.current = taskId - if (messageId) - responseItem.id = messageId - - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - async onCompleted(hasError?: boolean) { - handleResponding(false) - - if (hasError) - return - - if (onConversationComplete) - onConversationComplete(conversationId.current) - - if (conversationId.current && !hasStopResponded.current && onGetConversationMessages) { - const { data }: any = await onGetConversationMessages( - conversationId.current, - newAbortController => conversationMessagesAbortControllerRef.current = newAbortController, - ) - const newResponseItem = data.find((item: any) => item.id === responseItem.id) - if (!newResponseItem) - return - - const isUseAgentThought = newResponseItem.agent_thoughts?.length > 0 && newResponseItem.agent_thoughts[newResponseItem.agent_thoughts?.length - 1].thought === newResponseItem.answer - updateChatTreeNode(responseItem.id, { - content: isUseAgentThought ? '' : newResponseItem.answer, - log: [ - ...newResponseItem.message, - ...(newResponseItem.message[newResponseItem.message.length - 1].role !== 'assistant' - ? [ - { - role: 'assistant', - text: newResponseItem.answer, - files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [], - }, - ] - : []), - ], - more: { - time: formatTime(newResponseItem.created_at, 'hh:mm A'), - tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens, - latency: newResponseItem.provider_response_latency.toFixed(2), - tokens_per_second: newResponseItem.provider_response_latency > 0 ? (newResponseItem.answer_tokens / newResponseItem.provider_response_latency).toFixed(2) : undefined, - }, - // for agent log - conversationId: conversationId.current, - input: { - inputs: newResponseItem.inputs, - query: newResponseItem.query, - }, - }) - } - if (config?.suggested_questions_after_answer?.enabled && !hasStopResponded.current && onGetSuggestedQuestions) { - try { - const { data }: any = await onGetSuggestedQuestions( - responseItem.id, - newAbortController => suggestedQuestionsAbortControllerRef.current = newAbortController, - ) - setSuggestQuestions(data) - } - // eslint-disable-next-line unused-imports/no-unused-vars - catch (e) { - setSuggestQuestions([]) - } - } - }, - onFile(file) { + const otherOptions: IOtherOptions = { + isPublicAPI, + onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => { + if (!isAgentMode) { + responseItem.content = responseItem.content + message + } + else { const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1] if (lastThought) - responseItem.agent_thoughts![responseItem.agent_thoughts!.length - 1].message_files = [...(lastThought as any).message_files, file] + lastThought.thought = lastThought.thought + message // need immer setAutoFreeze + } - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, + if (messageId && !hasSetResponseId) { + questionItem.id = `question-${messageId}` + responseItem.id = messageId + responseItem.parentMessageId = questionItem.id + hasSetResponseId = true + } + + if (isFirstMessage && newConversationId) + conversationId.current = newConversationId + + taskIdRef.current = taskId + if (messageId) + responseItem.id = messageId + + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + async onCompleted(hasError?: boolean) { + handleResponding(false) + + if (hasError) + return + + if (onConversationComplete) + onConversationComplete(conversationId.current) + + if (conversationId.current && !hasStopResponded.current && onGetConversationMessages) { + const { data }: any = await onGetConversationMessages( + conversationId.current, + newAbortController => conversationMessagesAbortControllerRef.current = newAbortController, + ) + const newResponseItem = data.find((item: any) => item.id === responseItem.id) + if (!newResponseItem) + return + + const isUseAgentThought = newResponseItem.agent_thoughts?.length > 0 && newResponseItem.agent_thoughts[newResponseItem.agent_thoughts?.length - 1].thought === newResponseItem.answer + updateChatTreeNode(responseItem.id, { + content: isUseAgentThought ? '' : newResponseItem.answer, + log: [ + ...newResponseItem.message, + ...(newResponseItem.message[newResponseItem.message.length - 1].role !== 'assistant' + ? [ + { + role: 'assistant', + text: newResponseItem.answer, + files: newResponseItem.message_files?.filter((file: any) => file.belongs_to === 'assistant') || [], + }, + ] + : []), + ], + more: { + time: formatTime(newResponseItem.created_at, 'hh:mm A'), + tokens: newResponseItem.answer_tokens + newResponseItem.message_tokens, + latency: newResponseItem.provider_response_latency.toFixed(2), + tokens_per_second: newResponseItem.provider_response_latency > 0 ? (newResponseItem.answer_tokens / newResponseItem.provider_response_latency).toFixed(2) : undefined, + }, + // for agent log + conversationId: conversationId.current, + input: { + inputs: newResponseItem.inputs, + query: newResponseItem.query, + }, }) - }, - onThought(thought) { - isAgentMode = true - const response = responseItem as any - if (thought.message_id && !hasSetResponseId) - response.id = thought.message_id - if (thought.conversation_id) - response.conversationId = thought.conversation_id + } + if (config?.suggested_questions_after_answer?.enabled && !hasStopResponded.current && onGetSuggestedQuestions) { + try { + const { data }: any = await onGetSuggestedQuestions( + responseItem.id, + newAbortController => suggestedQuestionsAbortControllerRef.current = newAbortController, + ) + setSuggestQuestions(data) + } + // eslint-disable-next-line unused-imports/no-unused-vars + catch (e) { + setSuggestQuestions([]) + } + } + }, + onFile(file) { + const lastThought = responseItem.agent_thoughts?.[responseItem.agent_thoughts?.length - 1] + if (lastThought) + responseItem.agent_thoughts![responseItem.agent_thoughts!.length - 1].message_files = [...(lastThought as any).message_files, file] - if (response.agent_thoughts.length === 0) { - response.agent_thoughts.push(thought) + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onThought(thought) { + isAgentMode = true + const response = responseItem as any + if (thought.message_id && !hasSetResponseId) + response.id = thought.message_id + if (thought.conversation_id) + response.conversationId = thought.conversation_id + + if (response.agent_thoughts.length === 0) { + response.agent_thoughts.push(thought) + } + else { + const lastThought = response.agent_thoughts[response.agent_thoughts.length - 1] + // thought changed but still the same thought, so update. + if (lastThought.id === thought.id) { + thought.thought = lastThought.thought + thought.message_files = lastThought.message_files + responseItem.agent_thoughts![response.agent_thoughts.length - 1] = thought } else { - const lastThought = response.agent_thoughts[response.agent_thoughts.length - 1] - // thought changed but still the same thought, so update. - if (lastThought.id === thought.id) { - thought.thought = lastThought.thought - thought.message_files = lastThought.message_files - responseItem.agent_thoughts![response.agent_thoughts.length - 1] = thought - } - else { - responseItem.agent_thoughts!.push(thought) - } + responseItem.agent_thoughts!.push(thought) } + } + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onMessageEnd: (messageEnd) => { + if (messageEnd.metadata?.annotation_reply) { + responseItem.id = messageEnd.id + responseItem.annotation = ({ + id: messageEnd.metadata.annotation_reply.id, + authorName: messageEnd.metadata.annotation_reply.account.name, + }) updateCurrentQAOnTree({ placeholderQuestionId, questionItem, responseItem, parentId: data.parent_message_id, }) - }, - onMessageEnd: (messageEnd) => { - if (messageEnd.metadata?.annotation_reply) { - responseItem.id = messageEnd.id - responseItem.annotation = ({ - id: messageEnd.metadata.annotation_reply.id, - authorName: messageEnd.metadata.annotation_reply.account.name, - }) - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - return - } - responseItem.citation = messageEnd.metadata?.retriever_resources || [] - const processedFilesFromResponse = getProcessedFilesFromResponse(messageEnd.files || []) - responseItem.allFiles = uniqBy([...(responseItem.allFiles || []), ...(processedFilesFromResponse || [])], 'id') + return + } + responseItem.citation = messageEnd.metadata?.retriever_resources || [] + const processedFilesFromResponse = getProcessedFilesFromResponse(messageEnd.files || []) + responseItem.allFiles = uniqBy([...(responseItem.allFiles || []), ...(processedFilesFromResponse || [])], 'id') - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onMessageReplace: (messageReplace) => { - responseItem.content = messageReplace.answer - }, - onError() { - handleResponding(false) - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onWorkflowStarted: ({ workflow_run_id, task_id }) => { + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onMessageReplace: (messageReplace) => { + responseItem.content = messageReplace.answer + }, + onError() { + handleResponding(false) + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onWorkflowStarted: ({ workflow_run_id, task_id, data: { is_resumption } }) => { + if (is_resumption) { + responseItem.workflowProcess!.status = WorkflowRunningStatus.Running + } + else { taskIdRef.current = task_id responseItem.workflow_run_id = workflow_run_id responseItem.workflowProcess = { status: WorkflowRunningStatus.Running, tracing: [], } - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onWorkflowFinished: ({ data: workflowFinishedData }) => { - responseItem.workflowProcess!.status = workflowFinishedData.status as WorkflowRunningStatus - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onIterationStart: ({ data: iterationStartedData }) => { - responseItem.workflowProcess!.tracing!.push({ - ...iterationStartedData, - status: WorkflowRunningStatus.Running, - }) - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onIterationFinish: ({ data: iterationFinishedData }) => { - const tracing = responseItem.workflowProcess!.tracing! - const iterationIndex = tracing.findIndex(item => item.node_id === iterationFinishedData.node_id - && (item.execution_metadata?.parallel_id === iterationFinishedData.execution_metadata?.parallel_id || item.parallel_id === iterationFinishedData.execution_metadata?.parallel_id))! - tracing[iterationIndex] = { - ...tracing[iterationIndex], - ...iterationFinishedData, - status: WorkflowRunningStatus.Succeeded, - } + } + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onWorkflowFinished: ({ data: workflowFinishedData }) => { + responseItem.workflowProcess!.status = workflowFinishedData.status as WorkflowRunningStatus + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onIterationStart: ({ data: iterationStartedData }) => { + responseItem.workflowProcess!.tracing!.push({ + ...iterationStartedData, + status: WorkflowRunningStatus.Running, + }) + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onIterationFinish: ({ data: iterationFinishedData }) => { + const tracing = responseItem.workflowProcess!.tracing! + const iterationIndex = tracing.findIndex(item => item.node_id === iterationFinishedData.node_id + && (item.execution_metadata?.parallel_id === iterationFinishedData.execution_metadata?.parallel_id || item.parallel_id === iterationFinishedData.execution_metadata?.parallel_id))! + tracing[iterationIndex] = { + ...tracing[iterationIndex], + ...iterationFinishedData, + status: WorkflowRunningStatus.Succeeded, + } - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onNodeStarted: ({ data: nodeStartedData }) => { + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onNodeStarted: ({ data: nodeStartedData }) => { + const { is_resumption } = nodeStartedData + if (is_resumption) { + const currentIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === data.node_id) + if (currentIndex > -1) { + responseItem.workflowProcess!.tracing![currentIndex] = { + ...nodeStartedData, + status: NodeRunningStatus.Running, + } + } + } + else { if (nodeStartedData.iteration_id) return @@ -561,79 +578,125 @@ export const useChat = ( ...nodeStartedData, status: WorkflowRunningStatus.Running, }) - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onNodeFinished: ({ data: nodeFinishedData }) => { - if (nodeFinishedData.iteration_id) - return - - if (data.loop_id) - return - - const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => { - if (!item.execution_metadata?.parallel_id) - return item.node_id === nodeFinishedData.node_id - - return item.node_id === nodeFinishedData.node_id && (item.execution_metadata?.parallel_id === nodeFinishedData.execution_metadata?.parallel_id) - }) - responseItem.workflowProcess!.tracing[currentIndex] = nodeFinishedData as any - - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onTTSChunk: (messageId: string, audio: string) => { - if (!audio || audio === '') - return - const audioPlayer = getOrCreatePlayer() - if (audioPlayer) { - audioPlayer.playAudioWithAudio(audio, true) - AudioPlayerManager.getInstance().resetMsgId(messageId) - } - }, - onTTSEnd: (messageId: string, audio: string) => { - const audioPlayer = getOrCreatePlayer() - if (audioPlayer) - audioPlayer.playAudioWithAudio(audio, false) - }, - onLoopStart: ({ data: loopStartedData }) => { - responseItem.workflowProcess!.tracing!.push({ - ...loopStartedData, - status: WorkflowRunningStatus.Running, - }) - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, - onLoopFinish: ({ data: loopFinishedData }) => { - const tracing = responseItem.workflowProcess!.tracing! - const loopIndex = tracing.findIndex(item => item.node_id === loopFinishedData.node_id - && (item.execution_metadata?.parallel_id === loopFinishedData.execution_metadata?.parallel_id || item.parallel_id === loopFinishedData.execution_metadata?.parallel_id))! - tracing[loopIndex] = { - ...tracing[loopIndex], - ...loopFinishedData, - status: WorkflowRunningStatus.Succeeded, - } - - updateCurrentQAOnTree({ - placeholderQuestionId, - questionItem, - responseItem, - parentId: data.parent_message_id, - }) - }, + } + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) }, + onNodeFinished: ({ data: nodeFinishedData }) => { + if (nodeFinishedData.iteration_id) + return + + if (data.loop_id) + return + + const currentIndex = responseItem.workflowProcess!.tracing!.findIndex((item) => { + if (!item.execution_metadata?.parallel_id) + return item.id === nodeFinishedData.id + + return item.id === nodeFinishedData.id && (item.execution_metadata?.parallel_id === nodeFinishedData.execution_metadata?.parallel_id) + }) + responseItem.workflowProcess!.tracing[currentIndex] = nodeFinishedData as any + + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onTTSChunk: (messageId: string, audio: string) => { + if (!audio || audio === '') + return + const audioPlayer = getOrCreatePlayer() + if (audioPlayer) { + audioPlayer.playAudioWithAudio(audio, true) + AudioPlayerManager.getInstance().resetMsgId(messageId) + } + }, + onTTSEnd: (messageId: string, audio: string) => { + const audioPlayer = getOrCreatePlayer() + if (audioPlayer) + audioPlayer.playAudioWithAudio(audio, false) + }, + onLoopStart: ({ data: loopStartedData }) => { + responseItem.workflowProcess!.tracing!.push({ + ...loopStartedData, + status: WorkflowRunningStatus.Running, + }) + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onLoopFinish: ({ data: loopFinishedData }) => { + const tracing = responseItem.workflowProcess!.tracing! + const loopIndex = tracing.findIndex(item => item.node_id === loopFinishedData.node_id + && (item.execution_metadata?.parallel_id === loopFinishedData.execution_metadata?.parallel_id || item.parallel_id === loopFinishedData.execution_metadata?.parallel_id))! + tracing[loopIndex] = { + ...tracing[loopIndex], + ...loopFinishedData, + status: WorkflowRunningStatus.Succeeded, + } + + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onHumanInputRequired: ({ data: humanInputRequiredData }) => { + responseItem.humanInputFormData = humanInputRequiredData + const currentTracingIndex = responseItem.workflowProcess!.tracing!.findIndex(item => item.node_id === humanInputRequiredData.node_id) + if (currentTracingIndex > -1) { + responseItem.workflowProcess!.tracing[currentTracingIndex].status = NodeRunningStatus.Paused + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + } + }, + onHumanInputFormFilled: ({ data: humanInputFormFilledData }) => { + delete responseItem.humanInputFormData + responseItem.humanInputFormFilledData = humanInputFormFilledData + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + onWorkflowPaused: ({ data: workflowPausedData }) => { + const url = `/workflow/${workflowPausedData.workflow_run_id}/events` + sseGet( + url, + {}, + otherOptions, + ) + responseItem.workflowProcess!.status = WorkflowRunningStatus.Paused + updateCurrentQAOnTree({ + placeholderQuestionId, + questionItem, + responseItem, + parentId: data.parent_message_id, + }) + }, + } + + ssePost( + url, + { + body: bodyParams, + }, + otherOptions, ) return true }, [ @@ -713,7 +776,6 @@ export const useChat = ( return { chatList, setTargetMessageId, - conversationId: conversationId.current, isResponding, setIsResponding, handleSend, 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 4c97b7095d..62c960410f 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -16,7 +16,9 @@ import { fetchSuggestedQuestions, getUrl, stopChatMessageResponding, + submitHumanInputForm, } from '@/service/share' +import { submitHumanInputForm as submitHumanInputFormService } from '@/service/workflow' import { TransferMethod } from '@/types/app' import { cn } from '@/utils/classnames' import Avatar from '../../avatar' @@ -174,6 +176,13 @@ const ChatWrapper = () => { } }, [inputsForms.length, isMobile, currentConversationId, collapsed, allInputsHidden]) + const handleSubmitHumanInputForm = useCallback(async (formID: string, formData: any) => { + if (isInstalledApp) + await submitHumanInputFormService(formID, formData) + else + await submitHumanInputForm(formID, formData) + }, [isInstalledApp]) + const welcome = useMemo(() => { const welcomeMessage = chatList.find(item => item.isOpeningStatement) if (respondingState) @@ -217,7 +226,7 @@ const ChatWrapper = () => { ) - }, [appData?.site.icon, appData?.site.icon_background, appData?.site.icon_type, appData?.site.icon_url, chatList, collapsed, currentConversationId, inputsForms.length, respondingState, allInputsHidden]) + }, [chatList, respondingState, currentConversationId, collapsed, inputsForms.length, allInputsHidden, isMobile, appData?.site.icon_type, appData?.site.icon, appData?.site.icon_background, appData?.site.icon_url]) const answerIcon = isDify() ? @@ -246,6 +255,7 @@ const ChatWrapper = () => { inputsForm={inputsForms} onRegenerate={doRegenerate} onStopResponding={handleStop} + onHumanInputFormSubmit={handleSubmitHumanInputForm} chatNode={( <> {chatNode}