diff --git a/web/app/components/base/chat/chat/answer/human-input-filled-form-list.tsx b/web/app/components/base/chat/chat/answer/human-input-filled-form-list.tsx
index 10c6cb6fad..51d7c36913 100644
--- a/web/app/components/base/chat/chat/answer/human-input-filled-form-list.tsx
+++ b/web/app/components/base/chat/chat/answer/human-input-filled-form-list.tsx
@@ -10,14 +10,13 @@ const HumanInputFilledFormList = ({
humanInputFilledFormDataList,
}: HumanInputFilledFormListProps) => {
return (
-
+
{
humanInputFilledFormDataList.map(formData => (
+
{
humanInputFormDataList.map(formData => (
= ({
)
}
- {
- !contentIsEmpty && !hasAgentThoughts && (
-
- )
- }
{
humanInputFormDataList && humanInputFormDataList.length > 0 && (
= ({
/>
)
}
+ {
+ !contentIsEmpty && !hasAgentThoughts && (
+
+ )
+ }
{
(hasAgentThoughts) && (
{
handleWorkflowNodeStarted,
handleWorkflowNodeFinished,
handleWorkflowNodeHumanInputRequired,
+ handleWorkflowNodeHumanInputFormFilled,
handleWorkflowNodeIterationStarted,
handleWorkflowNodeIterationNext,
handleWorkflowNodeIterationFinished,
@@ -795,6 +796,7 @@ export const useWorkflowRun = () => {
onHumanInputRequired(params)
},
onHumanInputFormFilled: (params) => {
+ handleWorkflowNodeHumanInputFormFilled(params)
if (onHumanInputFormFilled)
onHumanInputFormFilled(params)
},
@@ -808,7 +810,7 @@ export const useWorkflowRun = () => {
},
finalCallbacks,
)
- }, [store, doSyncWorkflowDraft, workflowStore, pathname, handleWorkflowFailed, flowId, handleWorkflowResume, handleWorkflowStarted, handleWorkflowFinished, fetchInspectVars, invalidAllLastRun, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeLoopStarted, handleWorkflowNodeLoopNext, handleWorkflowNodeLoopFinished, handleWorkflowNodeRetry, handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace, handleWorkflowPaused, handleWorkflowNodeHumanInputRequired])
+ }, [store, doSyncWorkflowDraft, workflowStore, pathname, handleWorkflowFailed, flowId, handleWorkflowResume, handleWorkflowStarted, handleWorkflowFinished, fetchInspectVars, invalidAllLastRun, handleWorkflowNodeStarted, handleWorkflowNodeFinished, handleWorkflowNodeIterationStarted, handleWorkflowNodeIterationNext, handleWorkflowNodeIterationFinished, handleWorkflowNodeLoopStarted, handleWorkflowNodeLoopNext, handleWorkflowNodeLoopFinished, handleWorkflowNodeRetry, handleWorkflowAgentLog, handleWorkflowTextChunk, handleWorkflowTextReplace, handleWorkflowPaused, handleWorkflowNodeHumanInputRequired, handleWorkflowNodeHumanInputFormFilled])
const handleStopRun = useCallback((taskId: string) => {
const setStoppedState = () => {
diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts
index b11587f6f7..914f4ce0ab 100644
--- a/web/app/components/workflow/hooks/use-workflow-run-event/index.ts
+++ b/web/app/components/workflow/hooks/use-workflow-run-event/index.ts
@@ -2,6 +2,7 @@ export * from './use-workflow-agent-log'
export * from './use-workflow-failed'
export * from './use-workflow-finished'
export * from './use-workflow-node-finished'
+export * from './use-workflow-node-human-input-form-filled'
export * from './use-workflow-node-human-input-required'
export * from './use-workflow-node-iteration-finished'
export * from './use-workflow-node-iteration-next'
diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-form-filled.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-form-filled.ts
new file mode 100644
index 0000000000..f3750b6996
--- /dev/null
+++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-form-filled.ts
@@ -0,0 +1,34 @@
+import type { HumanInputFormFilledResponse } from '@/types/workflow'
+import { produce } from 'immer'
+import { useCallback } from 'react'
+import { useWorkflowStore } from '@/app/components/workflow/store'
+
+export const useWorkflowNodeHumanInputFormFilled = () => {
+ const workflowStore = useWorkflowStore()
+
+ const handleWorkflowNodeHumanInputFormFilled = useCallback((params: HumanInputFormFilledResponse) => {
+ const { data } = params
+ const {
+ workflowRunningData,
+ setWorkflowRunningData,
+ } = workflowStore.getState()
+
+ const newWorkflowRunningData = produce(workflowRunningData!, (draft) => {
+ if (draft.humanInputFormDataList?.length) {
+ const currentFormIndex = draft.humanInputFormDataList.findIndex(item => item.node_id === data.node_id)
+ draft.humanInputFormDataList.splice(currentFormIndex, 1)
+ }
+ if (!draft.humanInputFilledFormDataList) {
+ draft.humanInputFilledFormDataList = [data]
+ }
+ else {
+ draft.humanInputFilledFormDataList.push(data)
+ }
+ })
+ setWorkflowRunningData(newWorkflowRunningData)
+ }, [workflowStore])
+
+ return {
+ handleWorkflowNodeHumanInputFormFilled,
+ }
+}
diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-required.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-required.ts
index 4097675854..4bec00dc31 100644
--- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-required.ts
+++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-node-human-input-required.ts
@@ -4,14 +4,36 @@ import { useCallback } from 'react'
import {
useStoreApi,
} from 'reactflow'
+import { useWorkflowStore } from '@/app/components/workflow/store'
import { NodeRunningStatus } from '@/app/components/workflow/types'
export const useWorkflowNodeHumanInputRequired = () => {
const store = useStoreApi()
+ const workflowStore = useWorkflowStore()
- // ! Human input required !== Workflow Paused
+ // Notice: Human input required !== Workflow Paused
const handleWorkflowNodeHumanInputRequired = useCallback((params: HumanInputRequiredResponse) => {
const { data } = params
+ const {
+ workflowRunningData,
+ setWorkflowRunningData,
+ } = workflowStore.getState()
+
+ const newWorkflowRunningData = produce(workflowRunningData!, (draft) => {
+ if (!draft.humanInputFormDataList) {
+ draft.humanInputFormDataList = [data]
+ }
+ else {
+ const currentFormIndex = draft.humanInputFormDataList.findIndex(item => item.node_id === data.node_id)
+ if (currentFormIndex > -1) {
+ draft.humanInputFormDataList[currentFormIndex] = data
+ }
+ else {
+ draft.humanInputFormDataList.push(data)
+ }
+ }
+ })
+ setWorkflowRunningData(newWorkflowRunningData)
const {
getNodes,
@@ -23,7 +45,7 @@ export const useWorkflowNodeHumanInputRequired = () => {
draft[currentNodeIndex].data._runningStatus = NodeRunningStatus.Paused
})
setNodes(newNodes)
- }, [store])
+ }, [store, workflowStore])
return {
handleWorkflowNodeHumanInputRequired,
diff --git a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts
index fd1d0b04ea..2ae32500ef 100644
--- a/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts
+++ b/web/app/components/workflow/hooks/use-workflow-run-event/use-workflow-run-event.ts
@@ -3,6 +3,7 @@ import {
useWorkflowFailed,
useWorkflowFinished,
useWorkflowNodeFinished,
+ useWorkflowNodeHumanInputFormFilled,
useWorkflowNodeHumanInputRequired,
useWorkflowNodeIterationFinished,
useWorkflowNodeIterationNext,
@@ -37,6 +38,7 @@ export const useWorkflowRunEvent = () => {
const { handleWorkflowAgentLog } = useWorkflowAgentLog()
const { handleWorkflowPaused } = useWorkflowPaused()
const { handleWorkflowNodeHumanInputRequired } = useWorkflowNodeHumanInputRequired()
+ const { handleWorkflowNodeHumanInputFormFilled } = useWorkflowNodeHumanInputFormFilled()
const { handleWorkflowResume } = useWorkflowResume()
return {
@@ -56,6 +58,7 @@ export const useWorkflowRunEvent = () => {
handleWorkflowTextReplace,
handleWorkflowAgentLog,
handleWorkflowPaused,
+ handleWorkflowNodeHumanInputFormFilled,
handleWorkflowNodeHumanInputRequired,
handleWorkflowResume,
}
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 34ff19a4ee..9bb8b1aad4 100644
--- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
@@ -533,7 +533,7 @@ export const useChat = (
responseItem.humanInputFormDataList = [data]
}
else {
- const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === data.node_id)
+ const currentFormIndex = responseItem.humanInputFormDataList.findIndex(item => item.node_id === data.node_id)
if (currentFormIndex > -1) {
responseItem.humanInputFormDataList[currentFormIndex] = data
}
@@ -554,7 +554,7 @@ export const useChat = (
},
onHumanInputFormFilled: ({ data }) => {
if (responseItem.humanInputFormDataList?.length) {
- const currentFormIndex = responseItem.humanInputFormDataList!.findIndex(item => item.node_id === data.node_id)
+ const currentFormIndex = responseItem.humanInputFormDataList.findIndex(item => item.node_id === data.node_id)
responseItem.humanInputFormDataList.splice(currentFormIndex, 1)
}
if (!responseItem.humanInputFilledFormDataList) {
diff --git a/web/app/components/workflow/panel/human-input-filled-form-list.tsx b/web/app/components/workflow/panel/human-input-filled-form-list.tsx
new file mode 100644
index 0000000000..45d8e4c3ee
--- /dev/null
+++ b/web/app/components/workflow/panel/human-input-filled-form-list.tsx
@@ -0,0 +1,33 @@
+import type { HumanInputFilledFormData } from '@/types/workflow'
+import ContentWrapper from '@/app/components/base/chat/chat/answer/human-input-content/content-wrapper'
+import { SubmittedHumanInputContent } from '@/app/components/base/chat/chat/answer/human-input-content/submitted'
+
+type HumanInputFilledFormListProps = {
+ humanInputFilledFormDataList: HumanInputFilledFormData[]
+}
+
+const HumanInputFilledFormList = ({
+ humanInputFilledFormDataList,
+}: HumanInputFilledFormListProps) => {
+ return (
+
+ {
+ humanInputFilledFormDataList.map(formData => (
+
+
+
+ ))
+ }
+
+ )
+}
+
+export default HumanInputFilledFormList
diff --git a/web/app/components/workflow/panel/human-input-form-list.tsx b/web/app/components/workflow/panel/human-input-form-list.tsx
new file mode 100644
index 0000000000..2f78984cd4
--- /dev/null
+++ b/web/app/components/workflow/panel/human-input-form-list.tsx
@@ -0,0 +1,79 @@
+import type { DeliveryMethod } from '@/app/components/workflow/nodes/human-input/types'
+import type { HumanInputFormData } from '@/types/workflow'
+import { useCallback, useMemo } from 'react'
+import { useStoreApi } from 'reactflow'
+import ContentWrapper from '@/app/components/base/chat/chat/answer/human-input-content/content-wrapper'
+import { UnsubmittedHumanInputContent } from '@/app/components/base/chat/chat/answer/human-input-content/unsubmitted'
+import { CUSTOM_NODE } from '@/app/components/workflow/constants'
+import { DeliveryMethodType } from '@/app/components/workflow/nodes/human-input/types'
+
+type HumanInputFormListProps = {
+ humanInputFormDataList: HumanInputFormData[]
+ onHumanInputFormSubmit?: (formID: string, formData: any) => Promise
+}
+
+const HumanInputFormList = ({
+ humanInputFormDataList,
+ onHumanInputFormSubmit,
+}: HumanInputFormListProps) => {
+ const store = useStoreApi()
+
+ const getHumanInputNodeData = useCallback((nodeID: string) => {
+ const {
+ getNodes,
+ } = store.getState()
+ const nodes = getNodes().filter(node => node.type === CUSTOM_NODE)
+ const node = nodes.find(n => n.id === nodeID)
+ return node
+ }, [store])
+
+ const deliveryMethodsConfig = useMemo((): Record => {
+ if (!humanInputFormDataList.length)
+ return {}
+ return humanInputFormDataList.reduce((acc, formData) => {
+ const deliveryMethodsConfig = getHumanInputNodeData(formData.node_id)?.data.delivery_methods || []
+ if (!deliveryMethodsConfig.length) {
+ acc[formData.node_id] = {
+ showEmailTip: false,
+ isEmailDebugMode: false,
+ showDebugModeTip: false,
+ }
+ return acc
+ }
+ const isWebappEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.WebApp && method.enabled)
+ const isEmailEnabled = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.enabled)
+ const isEmailDebugMode = deliveryMethodsConfig.some((method: DeliveryMethod) => method.type === DeliveryMethodType.Email && method.config?.debug_mode)
+ acc[formData.node_id] = {
+ showEmailTip: isEmailEnabled,
+ isEmailDebugMode,
+ showDebugModeTip: !isWebappEnabled,
+ }
+ return acc
+ }, {} as Record)
+ }, [getHumanInputNodeData, humanInputFormDataList])
+
+ return (
+
+ {
+ humanInputFormDataList.map(formData => (
+
+
+
+ ))
+ }
+
+ )
+}
+
+export default HumanInputFormList
diff --git a/web/app/components/workflow/panel/human-input-info.tsx b/web/app/components/workflow/panel/human-input-info.tsx
deleted file mode 100644
index edb86ed3b5..0000000000
--- a/web/app/components/workflow/panel/human-input-info.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import { memo } from 'react'
-// import { useStore } from '../store'
-import BlockIcon from '@/app/components/workflow/block-icon'
-import { BlockEnum } from '../types'
-
-type props = {
- nodeID: string
- nodeTitle: string
- formData: any
-}
-
-const HumanInputInfo = ({ nodeTitle }: props) => {
- // const historyWorkflowData = useStore(s => s.historyWorkflowData)
-
- return (
-
-
- {/* node icon */}
-
- {/* node name */}
-
{nodeTitle}
-
-
- {/* human input form content */}
-
-
- )
-}
-
-export default memo(HumanInputInfo)
diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx
index 1535a63504..4162526d22 100644
--- a/web/app/components/workflow/panel/inputs-panel.tsx
+++ b/web/app/components/workflow/panel/inputs-panel.tsx
@@ -44,15 +44,18 @@ const InputsPanel = ({ onRun }: Props) => {
const startVariables = startNode?.data.variables
const { checkInputsForm } = useCheckInputsForms()
- const initialInputs = { ...inputs }
- if (startVariables) {
- startVariables.forEach((variable) => {
- if (variable.default)
- initialInputs[variable.variable] = variable.default
- if (inputs[variable.variable] !== undefined)
- initialInputs[variable.variable] = inputs[variable.variable]
- })
- }
+ const initialInputs = useMemo(() => {
+ const result = { ...inputs }
+ if (startVariables) {
+ startVariables.forEach((variable) => {
+ if (variable.default)
+ result[variable.variable] = variable.default
+ if (inputs[variable.variable] !== undefined)
+ result[variable.variable] = inputs[variable.variable]
+ })
+ }
+ return result
+ }, [inputs, startVariables])
const variables = useMemo(() => {
const data = startVariables || []
diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx
index 76d819f72d..d01cfdf3e2 100644
--- a/web/app/components/workflow/panel/workflow-preview.tsx
+++ b/web/app/components/workflow/panel/workflow-preview.tsx
@@ -12,6 +12,7 @@ import {
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading'
+import { submitHumanInputForm } from '@/service/workflow'
import { cn } from '@/utils/classnames'
import Toast from '../../base/toast'
import {
@@ -25,7 +26,8 @@ import {
WorkflowRunningStatus,
} from '../types'
import { formatWorkflowRunIdentifier } from '../utils'
-import HumanInputInfo from './human-input-info'
+import HumanInputFilledFormList from './human-input-filled-form-list'
+import HumanInputFormList from './human-input-form-list'
import InputsPanel from './inputs-panel'
const WorkflowPreview = () => {
@@ -38,6 +40,8 @@ const WorkflowPreview = () => {
const panelWidth = useStore(s => s.previewPanelWidth)
const setPreviewPanelWidth = useStore(s => s.setPreviewPanelWidth)
const showDebugAndPreviewPanel = useStore(s => s.showDebugAndPreviewPanel)
+ const humanInputFormDataList = useStore(s => s.workflowRunningData?.humanInputFormDataList)
+ const humanInputFilledFormDataList = useStore(s => s.workflowRunningData?.humanInputFilledFormDataList)
const [currentTab, setCurrentTab] = useState(showInputsPanel ? 'INPUT' : 'TRACING')
const switchTab = async (tab: string) => {
@@ -95,6 +99,10 @@ const WorkflowPreview = () => {
}
}, [resize, stopResizing])
+ const handleSubmitHumanInputForm = useCallback(async (formID: string, formData: any) => {
+ await submitHumanInputForm(formID, formData)
+ }, [])
+
return (
{
switchTab('RESULT')} />
)}
{currentTab === 'RESULT' && (
- <>
- {/* human input form position TODO */}
-
+
+ {humanInputFormDataList && humanInputFormDataList.length > 0 && (
+
+ )}
+ {humanInputFilledFormDataList && humanInputFilledFormDataList.length > 0 && (
+
+ )}
{
{t('operation.copy', { ns: 'common' })}
)}
- >
+
)}
{currentTab === 'DETAIL' && (