diff --git a/web/app/components/base/chat/chat/answer/index.tsx b/web/app/components/base/chat/chat/answer/index.tsx index 8b7d4a7167..bcdd0d9690 100644 --- a/web/app/components/base/chat/chat/answer/index.tsx +++ b/web/app/components/base/chat/chat/answer/index.tsx @@ -6,6 +6,7 @@ import type { ChatConfig, ChatItem, } from '../../types' +import type { HumanInputFieldValue } from './human-input-content/field-renderer' import type { AppData } from '@/models/share' import { cn } from '@langgenius/dify-ui/cn' import { memo, useCallback, useEffect, useRef, useState } from 'react' @@ -40,7 +41,7 @@ type AnswerProps = { noChatInput?: boolean switchSibling?: (siblingMessageId: string) => void hideAvatar?: boolean - onHumanInputFormSubmit?: (formToken: string, formData: any) => Promise + onHumanInputFormSubmit?: (formToken: string, formData: { inputs: Record, action: string }) => Promise } const Answer: FC = ({ item, diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index 6a14b76bd4..ce7a7a7343 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/web/app/components/base/chat/chat/index.tsx @@ -10,6 +10,7 @@ import type { OnRegenerate, OnSend, } from '../types' +import type { HumanInputFieldValue } from './answer/human-input-content/field-renderer' import type { InputForm } from './type' import type { Emoji } from '@/app/components/tools/types' import type { AppData } from '@/models/share' @@ -69,7 +70,7 @@ export type ChatProps = { sidebarCollapseState?: boolean hideAvatar?: boolean sendOnEnter?: boolean - onHumanInputFormSubmit?: (formToken: string, formData: any) => Promise + onHumanInputFormSubmit?: (formToken: string, formData: { inputs: Record, action: string }) => Promise getHumanInputNodeData?: (nodeID: string) => any } diff --git a/web/app/components/base/chat/embedded-chatbot/__tests__/chat-wrapper.spec.tsx b/web/app/components/base/chat/embedded-chatbot/__tests__/chat-wrapper.spec.tsx index 865023722e..1f530f81ff 100644 --- a/web/app/components/base/chat/embedded-chatbot/__tests__/chat-wrapper.spec.tsx +++ b/web/app/components/base/chat/embedded-chatbot/__tests__/chat-wrapper.spec.tsx @@ -1,4 +1,5 @@ import type { RefObject } from 'react' +import type { HumanInputFieldValue } from '../../chat/answer/human-input-content/field-renderer' import type { ChatConfig, ChatItem, ChatItemInTree } from '../../types' import type { EmbeddedChatbotContextValue } from '../context' import type { ConversationItem } from '@/models/share' @@ -59,7 +60,7 @@ vi.mock('../../chat', () => ({ onSend: (message: string) => void onRegenerate: (chatItem: ChatItem, editedQuestion?: { message: string, files?: never[] }) => void switchSibling: (siblingMessageId: string) => void - onHumanInputFormSubmit: (formToken: string, formData: Record) => Promise + onHumanInputFormSubmit: (formToken: string, formData: { inputs: Record, action: string }) => Promise onStopResponding: () => void }) => (
@@ -78,7 +79,7 @@ vi.mock('../../chat', () => ({ - +
), })) @@ -351,7 +352,7 @@ describe('EmbeddedChatbot chat-wrapper', () => { fireEvent.click(screen.getByRole('button', { name: 'submit human input' })) await waitFor(() => { - expect(submitHumanInputFormService).toHaveBeenCalledWith('form-token', { answer: 'ok' }) + expect(submitHumanInputFormService).toHaveBeenCalledWith('form-token', { inputs: { answer: 'ok' }, action: 'approve' }) }) expect(submitHumanInputForm).not.toHaveBeenCalled() }) @@ -391,7 +392,7 @@ describe('EmbeddedChatbot chat-wrapper', () => { fireEvent.click(screen.getByRole('button', { name: 'submit human input' })) await waitFor(() => { - expect(submitHumanInputForm).toHaveBeenCalledWith('form-token', { answer: 'ok' }) + expect(submitHumanInputForm).toHaveBeenCalledWith('form-token', { inputs: { answer: 'ok' }, action: 'approve' }) }) expect(handleSend).toHaveBeenCalledTimes(2) const sendOptions = handleSend.mock.calls[0]?.[2] as { onGetSuggestedQuestions: (responseItemId: string) => void } 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 2f50fe76c6..b1ab648483 100644 --- a/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx +++ b/web/app/components/base/chat/embedded-chatbot/chat-wrapper.tsx @@ -1,4 +1,5 @@ import type { FileEntity } from '../../file-uploader/types' +import type { HumanInputFieldValue } from '../chat/answer/human-input-content/field-renderer' import type { ChatConfig, ChatItem, @@ -232,7 +233,7 @@ const ChatWrapper = () => { } }, [inputsForms.length, isMobile, currentConversationId, collapsed, allInputsHidden]) - const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: any) => { + const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: { inputs: Record, action: string }) => { if (isInstalledApp) await submitHumanInputFormService(formToken, formData) else diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/chat-wrapper.spec.tsx b/web/app/components/workflow/panel/debug-and-preview/__tests__/chat-wrapper.spec.tsx index 20a47d545c..2167e7c411 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/chat-wrapper.spec.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/chat-wrapper.spec.tsx @@ -1,4 +1,5 @@ import type { ChatWrapperRefType } from '../index' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import { act, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { useStore as useAppStore } from '@/app/components/app/store' @@ -37,7 +38,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({ onSend?: (message: string, files: unknown[]) => void onRegenerate?: (chatItem: { id: string, parentMessageId?: string, content?: string, message_files?: unknown[] }) => void switchSibling?: (siblingMessageId: string) => void - onHumanInputFormSubmit?: (formToken: string, formData: Record) => Promise + onHumanInputFormSubmit?: (formToken: string, formData: { inputs: Record, action: string }) => Promise onFeatureBarClick?: (state: boolean) => void }) => (
@@ -55,7 +56,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({ regenerate-chat - + {chatNode}
@@ -296,7 +297,7 @@ describe('ChatWrapper', () => { await user.click(screen.getByRole('button', { name: 'submit-human-input' })) await waitFor(() => { - expect(handleSubmitHumanInputForm).toHaveBeenCalledWith('token-1', { answer: 'ok' }) + expect(handleSubmitHumanInputForm).toHaveBeenCalledWith('token-1', { inputs: { answer: 'ok' }, action: 'approve' }) }) const subscription = mockUseSubscription.mock.calls[0]?.[0] as (payload: { type: string }) => void diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/components.spec.tsx b/web/app/components/workflow/panel/debug-and-preview/__tests__/components.spec.tsx index b7ab773836..13d9108c63 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/components.spec.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/components.spec.tsx @@ -1,4 +1,5 @@ import type { ChatWrapperRefType } from '../index' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { ConversationVariable } from '@/app/components/workflow/types' import { act, fireEvent, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' @@ -78,7 +79,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({ onSend?: (message: string, files: unknown[]) => void onRegenerate?: (chatItem: { id: string, parentMessageId?: string, content?: string, message_files?: unknown[] }) => void switchSibling?: (siblingMessageId: string) => void - onHumanInputFormSubmit?: (formToken: string, formData: Record) => Promise + onHumanInputFormSubmit?: (formToken: string, formData: { inputs: Record, action: string }) => Promise onFeatureBarClick?: (state: boolean) => void }) => { mockChatRender({ @@ -101,7 +102,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({ regenerate-chat - + {chatNode} @@ -593,7 +594,7 @@ describe('debug-and-preview components', () => { await user.click(screen.getByRole('button', { name: 'submit-human-input' })) await waitFor(() => { - expect(handleSubmitHumanInputForm).toHaveBeenCalledWith('token-1', { answer: 'ok' }) + expect(handleSubmitHumanInputForm).toHaveBeenCalledWith('token-1', { inputs: { answer: 'ok' }, action: 'approve' }) }) const stopResponding = mockUseChat.mock.calls[0]?.[3] as (taskId: string) => void diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts index 9a2b8a8df7..223a4d769c 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts @@ -126,10 +126,10 @@ describe('useChat', () => { const { result } = renderHook(() => useChat({})) await act(async () => { - await result.current.handleSubmitHumanInputForm('token-123', { field: 'value' }) + await result.current.handleSubmitHumanInputForm('token-123', { inputs: { field: 'value' }, action: 'approve' }) }) - expect(mockSubmitHumanInputForm).toHaveBeenCalledWith('token-123', { field: 'value' }) + expect(mockSubmitHumanInputForm).toHaveBeenCalledWith('token-123', { inputs: { field: 'value' }, action: 'approve' }) expect(submitHumanInputForm).toBeDefined() }) diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts index cd2eaa0e00..a4bd20ef9d 100644 --- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts +++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts @@ -228,10 +228,10 @@ describe('useChat – handleSubmitHumanInputForm', () => { const { result } = renderHook(() => useChat({})) await act(async () => { - await result.current.handleSubmitHumanInputForm('token-123', { field: 'value' }) + await result.current.handleSubmitHumanInputForm('token-123', { inputs: { field: 'value' }, action: 'approve' }) }) - expect(mockSubmitHumanInputForm).toHaveBeenCalledWith('token-123', { field: 'value' }) + expect(mockSubmitHumanInputForm).toHaveBeenCalledWith('token-123', { inputs: { field: 'value' }, action: 'approve' }) }) }) diff --git a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx index 33ae66eebc..a99c12fd57 100644 --- a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx @@ -1,5 +1,6 @@ import type { StartNodeType } from '../../nodes/start/types' import type { ChatWrapperRefType } from './index' +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { ChatItem, OnSend } from '@/app/components/base/chat/types' import type { FileEntity } from '@/app/components/base/file-uploader/types' import { memo, useCallback, useEffect, useImperativeHandle, useMemo } from 'react' @@ -129,8 +130,7 @@ const ChatWrapper = ( }) }, [handleSwitchSibling, appDetail]) - const doHumanInputFormSubmit = useCallback(async (formToken: string, formData: any) => { - // Handle human input form submission + const doHumanInputFormSubmit = useCallback(async (formToken: string, formData: { inputs: Record, action: string }) => { await handleSubmitHumanInputForm(formToken, formData) }, [handleSubmitHumanInputForm]) diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts index a94e4c1edd..34778b9e66 100644 --- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts +++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts @@ -1,3 +1,4 @@ +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import type { InputForm } from '@/app/components/base/chat/chat/type' import type { ChatItem, @@ -660,7 +661,7 @@ export const useChat = ( ) }, [threadMessages, chatTree.length, updateCurrentQAOnTree, handleResponding, formSettings?.inputsForm, handleRun, t, workflowStore, fetchInspectVars, invalidAllLastRun, config?.suggested_questions_after_answer?.enabled]) - const handleSubmitHumanInputForm = async (formToken: string, formData: any) => { + const handleSubmitHumanInputForm = async (formToken: string, formData: { inputs: Record, action: string }) => { await submitHumanInputForm(formToken, formData) } diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 89ae09c374..3add682fe1 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -1,3 +1,4 @@ +import type { HumanInputFieldValue } from '@/app/components/base/chat/chat/answer/human-input-content/field-renderer' import { Button } from '@langgenius/dify-ui/button' import { cn } from '@langgenius/dify-ui/cn' import { toast } from '@langgenius/dify-ui/toast' @@ -97,7 +98,7 @@ const WorkflowPreview = () => { } }, [resize, stopResizing]) - const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: any) => { + const handleSubmitHumanInputForm = useCallback(async (formToken: string, formData: { inputs: Record, action: string }) => { await submitHumanInputForm(formToken, formData) }, [])