feat: introduce trigger functionality (#27644)
Signed-off-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: Stream <Stream_2@qq.com> Co-authored-by: lyzno1 <92089059+lyzno1@users.noreply.github.com> Co-authored-by: zhsama <torvalds@linux.do> Co-authored-by: Harry <xh001x@hotmail.com> Co-authored-by: lyzno1 <yuanyouhuilyz@gmail.com> Co-authored-by: yessenia <yessenia.contact@gmail.com> Co-authored-by: hjlarry <hjlarry@163.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: WTW0313 <twwu@dify.ai> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -10,7 +10,15 @@ import {
|
||||
import { getNodeInfoById, isConversationVar, isENV, isSystemVar, toNodeOutputVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
|
||||
|
||||
import type { CommonNodeType, InputVar, ValueSelector, Var, Variable } from '@/app/components/workflow/types'
|
||||
import { BlockEnum, InputVarType, NodeRunningStatus, VarType } from '@/app/components/workflow/types'
|
||||
import {
|
||||
BlockEnum,
|
||||
InputVarType,
|
||||
NodeRunningStatus,
|
||||
VarType,
|
||||
WorkflowRunningStatus,
|
||||
} from '@/app/components/workflow/types'
|
||||
import type { TriggerNodeType } from '@/app/components/workflow/types'
|
||||
import { EVENT_WORKFLOW_STOP } from '@/app/components/workflow/variable-inspect/types'
|
||||
import { useStore, useWorkflowStore } from '@/app/components/workflow/store'
|
||||
import { fetchNodeInspectVars, getIterationSingleNodeRunUrl, getLoopSingleNodeRunUrl, singleNodeRun } from '@/service/workflow'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
@@ -28,7 +36,7 @@ import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter
|
||||
import IterationDefault from '@/app/components/workflow/nodes/iteration/default'
|
||||
import DocumentExtractorDefault from '@/app/components/workflow/nodes/document-extractor/default'
|
||||
import LoopDefault from '@/app/components/workflow/nodes/loop/default'
|
||||
import { ssePost } from '@/service/base'
|
||||
import { post, ssePost } from '@/service/base'
|
||||
import { noop } from 'lodash-es'
|
||||
import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants'
|
||||
import type { NodeRunResult, NodeTracing } from '@/types/workflow'
|
||||
@@ -50,9 +58,10 @@ import {
|
||||
useStoreApi,
|
||||
} from 'reactflow'
|
||||
import { useInvalidLastRun } from '@/service/use-workflow'
|
||||
import useInspectVarsCrud from '../../../hooks/use-inspect-vars-crud'
|
||||
import useInspectVarsCrud from '@/app/components/workflow/hooks/use-inspect-vars-crud'
|
||||
import type { FlowType } from '@/types/common'
|
||||
import useMatchSchemaType from '../components/variable/use-match-schema-type'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import {
|
||||
useAllBuiltInTools,
|
||||
useAllCustomTools,
|
||||
@@ -61,7 +70,7 @@ import {
|
||||
} from '@/service/use-tools'
|
||||
|
||||
// eslint-disable-next-line ts/no-unsafe-function-type
|
||||
const checkValidFns: Record<BlockEnum, Function> = {
|
||||
const checkValidFns: Partial<Record<BlockEnum, Function>> = {
|
||||
[BlockEnum.LLM]: checkLLMValid,
|
||||
[BlockEnum.KnowledgeRetrieval]: checkKnowledgeRetrievalValid,
|
||||
[BlockEnum.IfElse]: checkIfElseValid,
|
||||
@@ -76,7 +85,12 @@ const checkValidFns: Record<BlockEnum, Function> = {
|
||||
[BlockEnum.Iteration]: checkIterationValid,
|
||||
[BlockEnum.DocExtractor]: checkDocumentExtractorValid,
|
||||
[BlockEnum.Loop]: checkLoopValid,
|
||||
} as any
|
||||
}
|
||||
|
||||
type RequestError = {
|
||||
message: string
|
||||
status: string
|
||||
}
|
||||
|
||||
export type Params<T> = {
|
||||
id: string
|
||||
@@ -198,7 +212,52 @@ const useOneStepRun = <T>({
|
||||
const store = useStoreApi()
|
||||
const {
|
||||
setShowSingleRunPanel,
|
||||
setIsListening,
|
||||
setListeningTriggerType,
|
||||
setListeningTriggerNodeId,
|
||||
setListeningTriggerNodeIds,
|
||||
setListeningTriggerIsAll,
|
||||
setShowVariableInspectPanel,
|
||||
} = workflowStore.getState()
|
||||
const updateNodeInspectRunningState = useCallback((nodeId: string, isRunning: boolean) => {
|
||||
const {
|
||||
nodesWithInspectVars,
|
||||
setNodesWithInspectVars,
|
||||
} = workflowStore.getState()
|
||||
|
||||
let hasChanges = false
|
||||
const nodes = produce(nodesWithInspectVars, (draft) => {
|
||||
const index = draft.findIndex(node => node.nodeId === nodeId)
|
||||
if (index !== -1) {
|
||||
const targetNode = draft[index]
|
||||
if (targetNode.isSingRunRunning !== isRunning) {
|
||||
targetNode.isSingRunRunning = isRunning
|
||||
if (isRunning)
|
||||
targetNode.isValueFetched = false
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
else if (isRunning) {
|
||||
const { getNodes } = store.getState()
|
||||
const target = getNodes().find(node => node.id === nodeId)
|
||||
if (target) {
|
||||
draft.unshift({
|
||||
nodeId,
|
||||
nodeType: target.data.type,
|
||||
title: target.data.title,
|
||||
vars: [],
|
||||
nodePayload: target.data,
|
||||
isSingRunRunning: true,
|
||||
isValueFetched: false,
|
||||
})
|
||||
hasChanges = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (hasChanges)
|
||||
setNodesWithInspectVars(nodes)
|
||||
}, [workflowStore, store])
|
||||
const invalidLastRun = useInvalidLastRun(flowType, flowId!, id)
|
||||
const [runResult, doSetRunResult] = useState<NodeRunResult | null>(null)
|
||||
const {
|
||||
@@ -207,10 +266,26 @@ const useOneStepRun = <T>({
|
||||
invalidateConversationVarValues,
|
||||
} = useInspectVarsCrud()
|
||||
const runningStatus = data._singleRunningStatus || NodeRunningStatus.NotStart
|
||||
const webhookSingleRunActiveRef = useRef(false)
|
||||
const webhookSingleRunAbortRef = useRef<AbortController | null>(null)
|
||||
const webhookSingleRunTimeoutRef = useRef<number | undefined>(undefined)
|
||||
const webhookSingleRunTokenRef = useRef(0)
|
||||
const webhookSingleRunDelayResolveRef = useRef<(() => void) | null>(null)
|
||||
const pluginSingleRunActiveRef = useRef(false)
|
||||
const pluginSingleRunAbortRef = useRef<AbortController | null>(null)
|
||||
const pluginSingleRunTimeoutRef = useRef<number | undefined>(undefined)
|
||||
const pluginSingleRunTokenRef = useRef(0)
|
||||
const pluginSingleRunDelayResolveRef = useRef<(() => void) | null>(null)
|
||||
const isPausedRef = useRef(isPaused)
|
||||
useEffect(() => {
|
||||
isPausedRef.current = isPaused
|
||||
}, [isPaused])
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
|
||||
const isScheduleTriggerNode = data.type === BlockEnum.TriggerSchedule
|
||||
const isWebhookTriggerNode = data.type === BlockEnum.TriggerWebhook
|
||||
const isPluginTriggerNode = data.type === BlockEnum.TriggerPlugin
|
||||
const isTriggerNode = isWebhookTriggerNode || isPluginTriggerNode || isScheduleTriggerNode
|
||||
|
||||
const setRunResult = useCallback(async (data: NodeRunResult | null) => {
|
||||
const isPaused = isPausedRef.current
|
||||
@@ -230,13 +305,27 @@ const useOneStepRun = <T>({
|
||||
const { getNodes } = store.getState()
|
||||
const nodes = getNodes()
|
||||
appendNodeInspectVars(id, vars, nodes)
|
||||
updateNodeInspectRunningState(id, false)
|
||||
if (data?.status === NodeRunningStatus.Succeeded) {
|
||||
invalidLastRun()
|
||||
if (isStartNode)
|
||||
if (isStartNode || isTriggerNode)
|
||||
invalidateSysVarValues()
|
||||
invalidateConversationVarValues() // loop, iteration, variable assigner node can update the conversation variables, but to simple the logic(some nodes may also can update in the future), all nodes refresh.
|
||||
}
|
||||
}, [isRunAfterSingleRun, runningStatus, flowId, id, store, appendNodeInspectVars, invalidLastRun, isStartNode, invalidateSysVarValues, invalidateConversationVarValues])
|
||||
}, [
|
||||
isRunAfterSingleRun,
|
||||
runningStatus,
|
||||
flowId,
|
||||
id,
|
||||
store,
|
||||
appendNodeInspectVars,
|
||||
updateNodeInspectRunningState,
|
||||
invalidLastRun,
|
||||
isStartNode,
|
||||
isTriggerNode,
|
||||
invalidateSysVarValues,
|
||||
invalidateConversationVarValues,
|
||||
])
|
||||
|
||||
const { handleNodeDataUpdate }: { handleNodeDataUpdate: (data: any) => void } = useNodeDataUpdate()
|
||||
const setNodeRunning = () => {
|
||||
@@ -248,6 +337,299 @@ const useOneStepRun = <T>({
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const cancelWebhookSingleRun = useCallback(() => {
|
||||
webhookSingleRunActiveRef.current = false
|
||||
webhookSingleRunTokenRef.current += 1
|
||||
if (webhookSingleRunAbortRef.current)
|
||||
webhookSingleRunAbortRef.current.abort()
|
||||
webhookSingleRunAbortRef.current = null
|
||||
if (webhookSingleRunTimeoutRef.current !== undefined) {
|
||||
window.clearTimeout(webhookSingleRunTimeoutRef.current)
|
||||
webhookSingleRunTimeoutRef.current = undefined
|
||||
}
|
||||
if (webhookSingleRunDelayResolveRef.current) {
|
||||
webhookSingleRunDelayResolveRef.current()
|
||||
webhookSingleRunDelayResolveRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
const cancelPluginSingleRun = useCallback(() => {
|
||||
pluginSingleRunActiveRef.current = false
|
||||
pluginSingleRunTokenRef.current += 1
|
||||
if (pluginSingleRunAbortRef.current)
|
||||
pluginSingleRunAbortRef.current.abort()
|
||||
pluginSingleRunAbortRef.current = null
|
||||
if (pluginSingleRunTimeoutRef.current !== undefined) {
|
||||
window.clearTimeout(pluginSingleRunTimeoutRef.current)
|
||||
pluginSingleRunTimeoutRef.current = undefined
|
||||
}
|
||||
if (pluginSingleRunDelayResolveRef.current) {
|
||||
pluginSingleRunDelayResolveRef.current()
|
||||
pluginSingleRunDelayResolveRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
const startTriggerListening = useCallback(() => {
|
||||
if (!isTriggerNode)
|
||||
return
|
||||
|
||||
setIsListening(true)
|
||||
setShowVariableInspectPanel(true)
|
||||
setListeningTriggerType(data.type as TriggerNodeType)
|
||||
setListeningTriggerNodeId(id)
|
||||
setListeningTriggerNodeIds([id])
|
||||
setListeningTriggerIsAll(false)
|
||||
}, [
|
||||
isTriggerNode,
|
||||
setIsListening,
|
||||
setShowVariableInspectPanel,
|
||||
setListeningTriggerType,
|
||||
data.type,
|
||||
setListeningTriggerNodeId,
|
||||
id,
|
||||
setListeningTriggerNodeIds,
|
||||
setListeningTriggerIsAll,
|
||||
])
|
||||
|
||||
const stopTriggerListening = useCallback(() => {
|
||||
if (!isTriggerNode)
|
||||
return
|
||||
|
||||
setIsListening(false)
|
||||
setListeningTriggerType(null)
|
||||
setListeningTriggerNodeId(null)
|
||||
setListeningTriggerNodeIds([])
|
||||
setListeningTriggerIsAll(false)
|
||||
}, [
|
||||
isTriggerNode,
|
||||
setIsListening,
|
||||
setListeningTriggerType,
|
||||
setListeningTriggerNodeId,
|
||||
setListeningTriggerNodeIds,
|
||||
setListeningTriggerIsAll,
|
||||
])
|
||||
|
||||
const runScheduleSingleRun = useCallback(async (): Promise<NodeRunResult | null> => {
|
||||
const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger/run`
|
||||
|
||||
try {
|
||||
const response: any = await post(urlPath, {
|
||||
body: JSON.stringify({}),
|
||||
})
|
||||
|
||||
if (!response) {
|
||||
const message = 'Schedule trigger run failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (response?.status === 'error') {
|
||||
const message = response?.message || 'Schedule trigger run failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Succeeded,
|
||||
},
|
||||
})
|
||||
|
||||
return response as NodeRunResult
|
||||
}
|
||||
catch (error) {
|
||||
console.error('handleRun: schedule trigger single run error', error)
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Failed,
|
||||
},
|
||||
})
|
||||
Toast.notify({ type: 'error', message: 'Schedule trigger run failed' })
|
||||
throw error
|
||||
}
|
||||
}, [flowId, id, handleNodeDataUpdate, data])
|
||||
|
||||
const runWebhookSingleRun = useCallback(async (): Promise<any | null> => {
|
||||
const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger/run`
|
||||
|
||||
webhookSingleRunActiveRef.current = true
|
||||
const token = ++webhookSingleRunTokenRef.current
|
||||
|
||||
while (webhookSingleRunActiveRef.current && token === webhookSingleRunTokenRef.current) {
|
||||
const controller = new AbortController()
|
||||
webhookSingleRunAbortRef.current = controller
|
||||
|
||||
try {
|
||||
const response: any = await post(urlPath, {
|
||||
body: JSON.stringify({}),
|
||||
signal: controller.signal,
|
||||
})
|
||||
|
||||
if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current)
|
||||
return null
|
||||
|
||||
if (!response) {
|
||||
const message = response?.message || 'Webhook debug failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
cancelWebhookSingleRun()
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (response?.status === 'waiting') {
|
||||
const delay = Number(response.retry_in) || 2000
|
||||
webhookSingleRunAbortRef.current = null
|
||||
if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current)
|
||||
return null
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
const timeoutId = window.setTimeout(resolve, delay)
|
||||
webhookSingleRunTimeoutRef.current = timeoutId
|
||||
webhookSingleRunDelayResolveRef.current = resolve
|
||||
controller.signal.addEventListener('abort', () => {
|
||||
window.clearTimeout(timeoutId)
|
||||
resolve()
|
||||
}, { once: true })
|
||||
})
|
||||
|
||||
webhookSingleRunTimeoutRef.current = undefined
|
||||
webhookSingleRunDelayResolveRef.current = null
|
||||
continue
|
||||
}
|
||||
|
||||
if (response?.status === 'error') {
|
||||
const message = response.message || 'Webhook debug failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
cancelWebhookSingleRun()
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Listening,
|
||||
},
|
||||
})
|
||||
|
||||
cancelWebhookSingleRun()
|
||||
return response
|
||||
}
|
||||
catch (error) {
|
||||
if (controller.signal.aborted && (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current))
|
||||
return null
|
||||
if (controller.signal.aborted)
|
||||
return null
|
||||
|
||||
Toast.notify({ type: 'error', message: 'Webhook debug request failed' })
|
||||
cancelWebhookSingleRun()
|
||||
if (error instanceof Error)
|
||||
throw error
|
||||
throw new Error(String(error))
|
||||
}
|
||||
finally {
|
||||
webhookSingleRunAbortRef.current = null
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}, [flowId, id, data, handleNodeDataUpdate, cancelWebhookSingleRun])
|
||||
|
||||
const runPluginSingleRun = useCallback(async (): Promise<any | null> => {
|
||||
const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger/run`
|
||||
|
||||
pluginSingleRunActiveRef.current = true
|
||||
const token = ++pluginSingleRunTokenRef.current
|
||||
|
||||
while (pluginSingleRunActiveRef.current && token === pluginSingleRunTokenRef.current) {
|
||||
const controller = new AbortController()
|
||||
pluginSingleRunAbortRef.current = controller
|
||||
|
||||
let requestError: RequestError | undefined
|
||||
const response: any = await post(urlPath, {
|
||||
body: JSON.stringify({}),
|
||||
signal: controller.signal,
|
||||
}).catch(async (error: Response) => {
|
||||
const data = await error.clone().json() as Record<string, any>
|
||||
const { error: respError, status } = data || {}
|
||||
requestError = {
|
||||
message: respError,
|
||||
status,
|
||||
}
|
||||
return null
|
||||
}).finally(() => {
|
||||
pluginSingleRunAbortRef.current = null
|
||||
})
|
||||
|
||||
if (!pluginSingleRunActiveRef.current || token !== pluginSingleRunTokenRef.current)
|
||||
return null
|
||||
|
||||
if (requestError) {
|
||||
if (controller.signal.aborted)
|
||||
return null
|
||||
|
||||
Toast.notify({ type: 'error', message: requestError.message })
|
||||
cancelPluginSingleRun()
|
||||
throw requestError
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
const message = 'Plugin debug failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
cancelPluginSingleRun()
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
if (response?.status === 'waiting') {
|
||||
const delay = Number(response.retry_in) || 2000
|
||||
if (!pluginSingleRunActiveRef.current || token !== pluginSingleRunTokenRef.current)
|
||||
return null
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
const timeoutId = window.setTimeout(resolve, delay)
|
||||
pluginSingleRunTimeoutRef.current = timeoutId
|
||||
pluginSingleRunDelayResolveRef.current = resolve
|
||||
controller.signal.addEventListener('abort', () => {
|
||||
window.clearTimeout(timeoutId)
|
||||
resolve()
|
||||
}, { once: true })
|
||||
})
|
||||
|
||||
pluginSingleRunTimeoutRef.current = undefined
|
||||
pluginSingleRunDelayResolveRef.current = null
|
||||
continue
|
||||
}
|
||||
|
||||
if (response?.status === 'error') {
|
||||
const message = response.message || 'Plugin debug failed'
|
||||
Toast.notify({ type: 'error', message })
|
||||
cancelPluginSingleRun()
|
||||
throw new Error(message)
|
||||
}
|
||||
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Listening,
|
||||
},
|
||||
})
|
||||
|
||||
cancelPluginSingleRun()
|
||||
return response
|
||||
}
|
||||
|
||||
return null
|
||||
}, [flowId, id, data, handleNodeDataUpdate, cancelPluginSingleRun])
|
||||
|
||||
const checkValidWrap = () => {
|
||||
if (!checkValid)
|
||||
return { isValid: true, errorMessage: '' }
|
||||
@@ -262,7 +644,7 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: res.errorMessage,
|
||||
message: res.errorMessage || '',
|
||||
})
|
||||
}
|
||||
return res
|
||||
@@ -309,33 +691,84 @@ const useOneStepRun = <T>({
|
||||
const isCompleted = runningStatus === NodeRunningStatus.Succeeded || runningStatus === NodeRunningStatus.Failed
|
||||
|
||||
const handleRun = async (submitData: Record<string, any>) => {
|
||||
if (isWebhookTriggerNode)
|
||||
cancelWebhookSingleRun()
|
||||
if (isPluginTriggerNode)
|
||||
cancelPluginSingleRun()
|
||||
|
||||
updateNodeInspectRunningState(id, true)
|
||||
|
||||
if (isTriggerNode)
|
||||
startTriggerListening()
|
||||
else
|
||||
stopTriggerListening()
|
||||
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Running,
|
||||
_singleRunningStatus: isTriggerNode
|
||||
? NodeRunningStatus.Listening
|
||||
: NodeRunningStatus.Running,
|
||||
},
|
||||
})
|
||||
let res: any
|
||||
let hasError = false
|
||||
try {
|
||||
if (!isIteration && !isLoop) {
|
||||
const isStartNode = data.type === BlockEnum.Start
|
||||
const postData: Record<string, any> = {}
|
||||
if (isStartNode) {
|
||||
const { '#sys.query#': query, '#sys.files#': files, ...inputs } = submitData
|
||||
if (isChatMode)
|
||||
postData.conversation_id = ''
|
||||
|
||||
postData.inputs = inputs
|
||||
postData.query = query
|
||||
postData.files = files || []
|
||||
if (isScheduleTriggerNode) {
|
||||
res = await runScheduleSingleRun()
|
||||
}
|
||||
else if (isWebhookTriggerNode) {
|
||||
res = await runWebhookSingleRun()
|
||||
if (!res) {
|
||||
if (webhookSingleRunActiveRef.current) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Stopped,
|
||||
},
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
else if (isPluginTriggerNode) {
|
||||
res = await runPluginSingleRun()
|
||||
if (!res) {
|
||||
if (pluginSingleRunActiveRef.current) {
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Stopped,
|
||||
},
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
else {
|
||||
postData.inputs = submitData
|
||||
const isStartNode = data.type === BlockEnum.Start
|
||||
const postData: Record<string, any> = {}
|
||||
if (isStartNode) {
|
||||
const { '#sys.query#': query, '#sys.files#': files, ...inputs } = submitData
|
||||
if (isChatMode)
|
||||
postData.conversation_id = ''
|
||||
|
||||
postData.inputs = inputs
|
||||
postData.query = query
|
||||
postData.files = files || []
|
||||
}
|
||||
else {
|
||||
postData.inputs = submitData
|
||||
}
|
||||
res = await singleNodeRun(flowType, flowId!, id, postData) as any
|
||||
}
|
||||
res = await singleNodeRun(flowType, flowId!, id, postData) as any
|
||||
}
|
||||
else if (isIteration) {
|
||||
setIterationRunResult([])
|
||||
@@ -566,6 +999,14 @@ const useOneStepRun = <T>({
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (isWebhookTriggerNode)
|
||||
cancelWebhookSingleRun()
|
||||
if (isPluginTriggerNode)
|
||||
cancelPluginSingleRun()
|
||||
if (isTriggerNode)
|
||||
stopTriggerListening()
|
||||
if (!isIteration && !isLoop)
|
||||
updateNodeInspectRunningState(id, false)
|
||||
if (!isPausedRef.current && !isIteration && !isLoop && res) {
|
||||
setRunResult({
|
||||
...res,
|
||||
@@ -591,15 +1032,55 @@ const useOneStepRun = <T>({
|
||||
}
|
||||
}
|
||||
|
||||
const handleStop = () => {
|
||||
const handleStop = useCallback(() => {
|
||||
if (isTriggerNode) {
|
||||
const isTriggerActive = runningStatus === NodeRunningStatus.Listening
|
||||
|| webhookSingleRunActiveRef.current
|
||||
|| pluginSingleRunActiveRef.current
|
||||
if (!isTriggerActive)
|
||||
return
|
||||
}
|
||||
else if (runningStatus !== NodeRunningStatus.Running) {
|
||||
return
|
||||
}
|
||||
|
||||
cancelWebhookSingleRun()
|
||||
cancelPluginSingleRun()
|
||||
handleNodeDataUpdate({
|
||||
id,
|
||||
data: {
|
||||
...data,
|
||||
_singleRunningStatus: NodeRunningStatus.NotStart,
|
||||
_isSingleRun: false,
|
||||
_singleRunningStatus: NodeRunningStatus.Stopped,
|
||||
},
|
||||
})
|
||||
}
|
||||
stopTriggerListening()
|
||||
updateNodeInspectRunningState(id, false)
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
nodesWithInspectVars,
|
||||
deleteNodeInspectVars,
|
||||
} = workflowStore.getState()
|
||||
if (workflowRunningData) {
|
||||
setWorkflowRunningData(produce(workflowRunningData, (draft) => {
|
||||
draft.result.status = WorkflowRunningStatus.Stopped
|
||||
}))
|
||||
}
|
||||
|
||||
const inspectNode = nodesWithInspectVars.find(node => node.nodeId === id)
|
||||
if (inspectNode && !inspectNode.isValueFetched && (!inspectNode.vars || inspectNode.vars.length === 0))
|
||||
deleteNodeInspectVars(id)
|
||||
}, [
|
||||
isTriggerNode,
|
||||
runningStatus,
|
||||
cancelWebhookSingleRun,
|
||||
cancelPluginSingleRun,
|
||||
handleNodeDataUpdate,
|
||||
id,
|
||||
stopTriggerListening,
|
||||
updateNodeInspectRunningState,
|
||||
workflowStore,
|
||||
])
|
||||
|
||||
const toVarInputs = (variables: Variable[]): InputVar[] => {
|
||||
if (!variables)
|
||||
@@ -662,6 +1143,11 @@ const useOneStepRun = <T>({
|
||||
})
|
||||
}
|
||||
|
||||
eventEmitter?.useSubscription((v: any) => {
|
||||
if (v.type === EVENT_WORKFLOW_STOP)
|
||||
handleStop()
|
||||
})
|
||||
|
||||
return {
|
||||
isShowSingleRun,
|
||||
hideSingleRun,
|
||||
|
||||
Reference in New Issue
Block a user