mirror of https://github.com/langgenius/dify.git
feat: implement human input form handling and display components
This commit is contained in:
parent
cf9b72d574
commit
f654a7f704
|
|
@ -10,14 +10,13 @@ const HumanInputFilledFormList = ({
|
|||
humanInputFilledFormDataList,
|
||||
}: HumanInputFilledFormListProps) => {
|
||||
return (
|
||||
<div className="mt-2">
|
||||
<div className="mt-2 flex flex-col gap-y-2">
|
||||
{
|
||||
humanInputFilledFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle="todo: replace with node title"
|
||||
showExpandIcon
|
||||
className="mb-2 last:mb-0"
|
||||
>
|
||||
<SubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
|
|
|
|||
|
|
@ -42,13 +42,12 @@ const HumanInputFormList = ({
|
|||
}, [getHumanInputNodeData, humanInputFormDataList])
|
||||
|
||||
return (
|
||||
<div className="mt-2">
|
||||
<div className="mt-2 flex flex-col gap-y-2">
|
||||
{
|
||||
humanInputFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle={formData.node_title}
|
||||
className="mb-2 last:mb-0"
|
||||
>
|
||||
<UnsubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
|
|
|
|||
|
|
@ -176,11 +176,6 @@ const Answer: FC<AnswerProps> = ({
|
|||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!contentIsEmpty && !hasAgentThoughts && (
|
||||
<BasicContent item={item} />
|
||||
)
|
||||
}
|
||||
{
|
||||
humanInputFormDataList && humanInputFormDataList.length > 0 && (
|
||||
<HumanInputFormList
|
||||
|
|
@ -197,6 +192,11 @@ const Answer: FC<AnswerProps> = ({
|
|||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
!contentIsEmpty && !hasAgentThoughts && (
|
||||
<BasicContent item={item} />
|
||||
)
|
||||
}
|
||||
{
|
||||
(hasAgentThoughts) && (
|
||||
<AgentContent
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ export const useWorkflowRun = () => {
|
|||
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 = () => {
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="mt-3 flex flex-col gap-y-3">
|
||||
{
|
||||
humanInputFilledFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle="todo: replace with node title"
|
||||
showExpandIcon
|
||||
className="bg-components-panel-bg"
|
||||
>
|
||||
<SubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
formData={formData}
|
||||
/>
|
||||
</ContentWrapper>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInputFilledFormList
|
||||
|
|
@ -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<void>
|
||||
}
|
||||
|
||||
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<string, { showEmailTip: boolean, isEmailDebugMode: boolean, showDebugModeTip: boolean }> => {
|
||||
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<string, { showEmailTip: boolean, isEmailDebugMode: boolean, showDebugModeTip: boolean }>)
|
||||
}, [getHumanInputNodeData, humanInputFormDataList])
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-y-3">
|
||||
{
|
||||
humanInputFormDataList.map(formData => (
|
||||
<ContentWrapper
|
||||
key={formData.node_id}
|
||||
nodeTitle={formData.node_title}
|
||||
className="bg-components-panel-bg"
|
||||
>
|
||||
<UnsubmittedHumanInputContent
|
||||
key={formData.node_id}
|
||||
formData={formData}
|
||||
showEmailTip={!!deliveryMethodsConfig[formData.node_id]?.showEmailTip}
|
||||
isEmailDebugMode={!!deliveryMethodsConfig[formData.node_id]?.isEmailDebugMode}
|
||||
showDebugModeTip={!!deliveryMethodsConfig[formData.node_id]?.showDebugModeTip}
|
||||
onSubmit={onHumanInputFormSubmit}
|
||||
/>
|
||||
</ContentWrapper>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HumanInputFormList
|
||||
|
|
@ -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 (
|
||||
<div className="rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg p-2 shadow-xs">
|
||||
<div className="p-2">
|
||||
{/* node icon */}
|
||||
<BlockIcon
|
||||
type={BlockEnum.HumanInput}
|
||||
// toolIcon={triggerIcon}
|
||||
/>
|
||||
{/* node name */}
|
||||
<div className="system-sm-semibold-uppercase text-text-primary">{nodeTitle}</div>
|
||||
</div>
|
||||
<div>
|
||||
{/* human input form content */}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(HumanInputInfo)
|
||||
|
|
@ -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 || []
|
||||
|
|
|
|||
|
|
@ -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<string>(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 (
|
||||
<div
|
||||
className="relative flex h-full flex-col rounded-l-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl"
|
||||
|
|
@ -175,13 +183,18 @@ const WorkflowPreview = () => {
|
|||
<InputsPanel onRun={() => switchTab('RESULT')} />
|
||||
)}
|
||||
{currentTab === 'RESULT' && (
|
||||
<>
|
||||
{/* human input form position TODO */}
|
||||
<HumanInputInfo
|
||||
nodeTitle="Human Input Required"
|
||||
nodeID="human-input-node-id"
|
||||
formData={{}}
|
||||
/>
|
||||
<div className="p-2">
|
||||
{humanInputFormDataList && humanInputFormDataList.length > 0 && (
|
||||
<HumanInputFormList
|
||||
humanInputFormDataList={humanInputFormDataList}
|
||||
onHumanInputFormSubmit={handleSubmitHumanInputForm}
|
||||
/>
|
||||
)}
|
||||
{humanInputFilledFormDataList && humanInputFilledFormDataList.length > 0 && (
|
||||
<HumanInputFilledFormList
|
||||
humanInputFilledFormDataList={humanInputFilledFormDataList}
|
||||
/>
|
||||
)}
|
||||
<ResultText
|
||||
isRunning={workflowRunningData?.result?.status === WorkflowRunningStatus.Running || !workflowRunningData?.result}
|
||||
outputs={workflowRunningData?.resultText}
|
||||
|
|
@ -205,7 +218,7 @@ const WorkflowPreview = () => {
|
|||
<div>{t('operation.copy', { ns: 'common' })}</div>
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
{currentTab === 'DETAIL' && (
|
||||
<ResultPanel
|
||||
|
|
|
|||
|
|
@ -17,7 +17,13 @@ import type { VarType as VarKindType } from '@/app/components/workflow/nodes/too
|
|||
import type { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
|
||||
import type { SchemaTypeDefinition } from '@/service/use-common'
|
||||
import type { Resolution, TransferMethod } from '@/types/app'
|
||||
import type { FileResponse, NodeTracing, PanelProps } from '@/types/workflow'
|
||||
import type {
|
||||
FileResponse,
|
||||
HumanInputFilledFormData,
|
||||
HumanInputFormData,
|
||||
NodeTracing,
|
||||
PanelProps,
|
||||
} from '@/types/workflow'
|
||||
|
||||
export enum BlockEnum {
|
||||
Start = 'start',
|
||||
|
|
@ -429,6 +435,8 @@ export type WorkflowRunningData = {
|
|||
exceptions_count?: number
|
||||
}
|
||||
tracing?: NodeTracing[]
|
||||
humanInputFormDataList?: HumanInputFormData[]
|
||||
humanInputFilledFormDataList?: HumanInputFilledFormData[]
|
||||
}
|
||||
|
||||
export type HistoryWorkflowData = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue