diff --git a/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx b/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx
index a05b29a018..95595c5b6d 100644
--- a/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx
+++ b/web/app/components/base/chat/chat/answer/human-input-content/human-input-form.tsx
@@ -4,14 +4,11 @@ import * as React from 'react'
import { useCallback, useState } from 'react'
import Button from '@/app/components/base/button'
import ContentItem from './content-item'
-import ExpirationTime from './expiration-time'
import { getButtonStyle, initializeInputs, splitByOutputVar } from './utils'
const HumanInputForm = ({
formData,
- showTimeout,
onSubmit,
- expirationTime,
}: HumanInputFormProps) => {
const formID = formData.form_id
const defaultInputs = initializeInputs(formData.inputs)
@@ -56,9 +53,6 @@ const HumanInputForm = ({
))}
- {showTimeout && typeof expirationTime === 'number' && (
-
- )}
>
)
}
diff --git a/web/app/components/base/chat/chat/answer/human-input-content/index.tsx b/web/app/components/base/chat/chat/answer/human-input-content/index.tsx
index 87f69db468..052ee5f3e9 100644
--- a/web/app/components/base/chat/chat/answer/human-input-content/index.tsx
+++ b/web/app/components/base/chat/chat/answer/human-input-content/index.tsx
@@ -1,7 +1,9 @@
import type { HumanInputContentProps } from './type'
import { Trans, useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
+import { TriggerAll } from '@/app/components/base/icons/src/vender/workflow'
import { useSelector as useAppSelector } from '@/context/app-context'
+import ExpirationTime from './expiration-time'
import HumanInputForm from './human-input-form'
const HumanInputContent = ({
@@ -10,6 +12,8 @@ const HumanInputContent = ({
isEmailDebugMode = false,
showDebugModeTip = false,
showTimeout = false,
+ executedAction,
+ expirationTime,
onSubmit,
}: HumanInputContentProps) => {
const { t } = useTranslation()
@@ -19,9 +23,9 @@ const HumanInputContent = ({
<>
+ {/* Tips */}
{(showEmailTip || showDebugModeTip) && (
<>
@@ -43,6 +47,25 @@ const HumanInputContent = ({
>
)}
+ {/* Timeout */}
+ {showTimeout && typeof expirationTime === 'number' && (
+
+ )}
+ {/* Executed Action */}
+ {executedAction && (
+
+
+
+
+ }}
+ values={{ actionName: executedAction.title }}
+ />
+
+
+ )}
>
)
}
diff --git a/web/app/components/base/chat/chat/answer/human-input-content/type.ts b/web/app/components/base/chat/chat/answer/human-input-content/type.ts
index 14528005be..63d5ef5d06 100644
--- a/web/app/components/base/chat/chat/answer/human-input-content/type.ts
+++ b/web/app/components/base/chat/chat/answer/human-input-content/type.ts
@@ -19,8 +19,6 @@ export type HumanInputContentProps = {
export type HumanInputFormProps = {
formData: HumanInputFormData
- showTimeout?: boolean
- expirationTime?: number
onSubmit?: (formID: string, data: any) => Promise
}
diff --git a/web/app/components/base/chat/chat/answer/index.tsx b/web/app/components/base/chat/chat/answer/index.tsx
index 39330c05bf..b9d84d8ecd 100644
--- a/web/app/components/base/chat/chat/answer/index.tsx
+++ b/web/app/components/base/chat/chat/answer/index.tsx
@@ -6,8 +6,10 @@ import type {
ChatConfig,
ChatItem,
} from '../../types'
+import type { ExecutedAction } from './human-input-content/type'
import type { DeliveryMethod } from '@/app/components/workflow/nodes/human-input/types'
import type { AppData } from '@/models/share'
+import type { HumanInputFormData } from '@/types/workflow'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { EditTitle } from '@/app/components/app/annotation/edit-annotation-modal/edit-item'
@@ -70,6 +72,7 @@ const Answer: FC = ({
allFiles,
message_files,
humanInputFormData,
+ humanInputFormFilledData,
} = item
const hasAgentThoughts = !!agent_thoughts?.length
@@ -101,6 +104,30 @@ const Answer: FC = ({
}
}, [getHumanInputNodeData, humanInputFormData?.node_id])
+ const filledFormData = useMemo((): HumanInputFormData | undefined => {
+ if (!humanInputFormFilledData)
+ return
+ return {
+ form_id: '',
+ node_id: humanInputFormFilledData.node_id,
+ node_title: '',
+ form_content: humanInputFormFilledData.rendered_content,
+ inputs: [],
+ actions: [],
+ web_app_form_token: '',
+ resolved_placeholder_values: {},
+ }
+ }, [humanInputFormFilledData])
+
+ const executedAction = useMemo((): ExecutedAction | undefined => {
+ if (!humanInputFormFilledData)
+ return
+ return {
+ id: humanInputFormFilledData.action_id,
+ title: humanInputFormFilledData.action_text,
+ }
+ }, [humanInputFormFilledData])
+
const getContainerWidth = () => {
if (containerRef.current)
setContainerWidth(containerRef.current?.clientWidth + 16)
@@ -200,15 +227,25 @@ const Answer: FC = ({
)
}
- {humanInputFormData && (
-
- )}
+ {
+ humanInputFormData && (
+
+ )
+ }
+ {
+ filledFormData && (
+
+ )
+ }
{
(hasAgentThoughts) && (
{
onError,
onWorkflowPaused,
onHumanInputRequired,
+ onHumanInputFormFilled,
onCompleted,
...restCallback
} = callback || {}
@@ -610,6 +611,7 @@ export const useWorkflowRun = () => {
baseSseOptions.onTextReplace,
baseSseOptions.onAgentLog,
baseSseOptions.onHumanInputRequired,
+ baseSseOptions.onHumanInputFormFilled,
baseSseOptions.onWorkflowPaused,
baseSseOptions.onDataSourceNodeProcessing,
baseSseOptions.onDataSourceNodeCompleted,
@@ -792,6 +794,10 @@ export const useWorkflowRun = () => {
if (onHumanInputRequired)
onHumanInputRequired(params)
},
+ onHumanInputFormFilled: (params) => {
+ if (onHumanInputFormFilled)
+ onHumanInputFormFilled(params)
+ },
...restCallback,
}
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 5d8117089a..7906e91421 100644
--- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
@@ -539,6 +539,16 @@ export const useChat = (
})
}
},
+ onHumanInputFormFilled: ({ data }) => {
+ delete responseItem.humanInputFormData
+ responseItem.humanInputFormFilledData = data
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
+ },
onWorkflowPaused: ({ data: _data }) => {
responseItem.workflowProcess!.status = WorkflowRunningStatus.Paused
updateCurrentQAOnTree({
diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json
index 37eeef06d3..0e84fe3e4b 100644
--- a/web/i18n/en-US/workflow.json
+++ b/web/i18n/en-US/workflow.json
@@ -588,6 +588,7 @@
"nodes.humanInput.userActions.emptyTip": "Click the '+' button to add user actions",
"nodes.humanInput.userActions.title": "User Actions",
"nodes.humanInput.userActions.tooltip": "Define buttons that users can click to respond to this form. Each button can trigger different workflow paths.",
+ "nodes.humanInput.userActions.triggered": "{{actionName}} has been triggered",
"nodes.ifElse.addCondition": "Add Condition",
"nodes.ifElse.addSubVariable": "Sub Variable",
"nodes.ifElse.and": "and",
diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json
index 99fc9ca68b..c9e2b797ec 100644
--- a/web/i18n/zh-Hans/workflow.json
+++ b/web/i18n/zh-Hans/workflow.json
@@ -588,6 +588,7 @@
"nodes.humanInput.userActions.emptyTip": "点击 '+' 按钮添加用户操作",
"nodes.humanInput.userActions.title": "用户操作",
"nodes.humanInput.userActions.tooltip": "定义用户可以点击以响应此表单的按钮。每个按钮都可以触发不同的工作流路径。",
+ "nodes.humanInput.userActions.triggered": "已触发{{actionName}}",
"nodes.ifElse.addCondition": "添加条件",
"nodes.ifElse.addSubVariable": "添加子变量",
"nodes.ifElse.and": "and",
diff --git a/web/service/base.ts b/web/service/base.ts
index 4b60a791e3..d332e01ac9 100644
--- a/web/service/base.ts
+++ b/web/service/base.ts
@@ -8,6 +8,7 @@ import type {
} from '@/types/pipeline'
import type {
AgentLogResponse,
+ HumanInputFormFilledResponse,
HumanInputRequiredResponse,
IterationFinishedResponse,
IterationNextResponse,
@@ -73,6 +74,7 @@ export type IOnLoopFinished = (workflowFinished: LoopFinishedResponse) => void
export type IOnAgentLog = (agentLog: AgentLogResponse) => void
export type IOHumanInputRequired = (humanInputRequired: HumanInputRequiredResponse) => void
+export type IOnHumanInputFormFilled = (humanInputFormFilled: HumanInputFormFilledResponse) => void
export type IOWorkflowPaused = (workflowPaused: WorkflowPausedResponse) => void
export type IOnDataSourceNodeProcessing = (dataSourceNodeProcessing: DataSourceNodeProcessingResponse) => void
export type IOnDataSourceNodeCompleted = (dataSourceNodeCompleted: DataSourceNodeCompletedResponse) => void
@@ -113,6 +115,7 @@ export type IOtherOptions = {
onLoopFinish?: IOnLoopFinished
onAgentLog?: IOnAgentLog
onHumanInputRequired?: IOHumanInputRequired
+ onHumanInputFormFilled?: IOnHumanInputFormFilled
onWorkflowPaused?: IOWorkflowPaused
// Pipeline data source node run
@@ -197,6 +200,7 @@ export const handleStream = (
onTextReplace?: IOnTextReplace,
onAgentLog?: IOnAgentLog,
onHumanInputRequired?: IOHumanInputRequired,
+ onHumanInputFormFilled?: IOnHumanInputFormFilled,
onWorkflowPaused?: IOWorkflowPaused,
onDataSourceNodeProcessing?: IOnDataSourceNodeProcessing,
onDataSourceNodeCompleted?: IOnDataSourceNodeCompleted,
@@ -322,6 +326,9 @@ export const handleStream = (
else if (bufferObj.event === 'human_input_required') {
onHumanInputRequired?.(bufferObj as HumanInputRequiredResponse)
}
+ else if (bufferObj.event === 'human_input_form_filled') {
+ onHumanInputFormFilled?.(bufferObj as HumanInputFormFilledResponse)
+ }
else if (bufferObj.event === 'workflow_paused') {
onWorkflowPaused?.(bufferObj as WorkflowPausedResponse)
}
@@ -448,6 +455,7 @@ export const ssePost = async (
onLoopNext,
onLoopFinish,
onHumanInputRequired,
+ onHumanInputFormFilled,
onWorkflowPaused,
onDataSourceNodeProcessing,
onDataSourceNodeCompleted,
@@ -551,6 +559,7 @@ export const ssePost = async (
onTextReplace,
onAgentLog,
onHumanInputRequired,
+ onHumanInputFormFilled,
onWorkflowPaused,
onDataSourceNodeProcessing,
onDataSourceNodeCompleted,
@@ -598,6 +607,7 @@ export const sseGet = async (
onLoopNext,
onLoopFinish,
onHumanInputRequired,
+ onHumanInputFormFilled,
onWorkflowPaused,
onDataSourceNodeProcessing,
onDataSourceNodeCompleted,
@@ -694,6 +704,7 @@ export const sseGet = async (
onTextReplace,
onAgentLog,
onHumanInputRequired,
+ onHumanInputFormFilled,
onWorkflowPaused,
onDataSourceNodeProcessing,
onDataSourceNodeCompleted,
diff --git a/web/types/workflow.ts b/web/types/workflow.ts
index b74cb1ffb2..3b164ef095 100644
--- a/web/types/workflow.ts
+++ b/web/types/workflow.ts
@@ -331,6 +331,20 @@ export type HumanInputRequiredResponse = {
data: HumanInputFormData
}
+export type HumanInputFormFilledData = {
+ node_id: string
+ rendered_content: string
+ action_id: string
+ action_text: string
+}
+
+export type HumanInputFormFilledResponse = {
+ task_id: string
+ workflow_run_id: string
+ event: string
+ data: HumanInputFormFilledData
+}
+
export type WorkflowRunHistory = {
id: string
version: string