diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index 8f28d128c8..db01a74bed 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -238,6 +238,7 @@ const BasePanel: FC = ({ singleRunParams, nodeInfo, setRunInputData, + handleStop, handleSingleRun, handleRunWithParams, getExistVarValuesInForms, @@ -438,18 +439,10 @@ const BasePanel: FC = ({
{ - if (isSingleRunning) { - handleNodeDataUpdate({ - id, - data: { - _isSingleRun: false, - _singleRunningStatus: undefined, - }, - }) - } - else { + if (isSingleRunning) + handleStop() + else handleSingleRun() - } }} > { diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index f87b2ed42d..6c8266dba5 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -205,6 +205,11 @@ const useOneStepRun = ({ const webhookSingleRunTimeoutRef = useRef(undefined) const webhookSingleRunTokenRef = useRef(0) const webhookSingleRunDelayResolveRef = useRef<(() => void) | null>(null) + const pluginSingleRunActiveRef = useRef(false) + const pluginSingleRunAbortRef = useRef(null) + const pluginSingleRunTimeoutRef = useRef(undefined) + const pluginSingleRunTokenRef = useRef(0) + const pluginSingleRunDelayResolveRef = useRef<(() => void) | null>(null) const isPausedRef = useRef(isPaused) useEffect(() => { isPausedRef.current = isPaused @@ -263,6 +268,22 @@ const useOneStepRun = ({ } }, []) + 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 runWebhookSingleRun = useCallback(async (): Promise => { const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger/run` const urlWithPrefix = `${API_PREFIX}${urlPath.startsWith('/') ? urlPath : `/${urlPath}`}` @@ -335,7 +356,7 @@ const useOneStepRun = ({ data: { ...data, _isSingleRun: false, - _singleRunningStatus: NodeRunningStatus.Running, + _singleRunningStatus: NodeRunningStatus.Listening, }, }) @@ -367,12 +388,12 @@ const useOneStepRun = ({ const urlPath = `/apps/${flowId}/workflows/draft/nodes/${id}/trigger/run` const urlWithPrefix = `${API_PREFIX}${urlPath.startsWith('/') ? urlPath : `/${urlPath}`}` - webhookSingleRunActiveRef.current = true - const token = ++webhookSingleRunTokenRef.current + pluginSingleRunActiveRef.current = true + const token = ++pluginSingleRunTokenRef.current - while (webhookSingleRunActiveRef.current && token === webhookSingleRunTokenRef.current) { + while (pluginSingleRunActiveRef.current && token === pluginSingleRunTokenRef.current) { const controller = new AbortController() - webhookSingleRunAbortRef.current = controller + pluginSingleRunAbortRef.current = controller try { const baseOptions = getBaseOptions() @@ -388,7 +409,7 @@ const useOneStepRun = ({ signal: controller.signal, }) - if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current) + if (!pluginSingleRunActiveRef.current || token !== pluginSingleRunTokenRef.current) return null const contentType = response.headers.get('Content-Type')?.toLowerCase() || '' @@ -397,35 +418,35 @@ const useOneStepRun = ({ if (!response.ok) { const message = responseData?.message || 'Plugin debug failed' Toast.notify({ type: 'error', message }) - cancelWebhookSingleRun() + cancelPluginSingleRun() throw new Error(message) } if (responseData?.status === 'waiting') { const delay = Number(responseData.retry_in) || 2000 - webhookSingleRunAbortRef.current = null - if (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current) + pluginSingleRunAbortRef.current = null + if (!pluginSingleRunActiveRef.current || token !== pluginSingleRunTokenRef.current) return null await new Promise((resolve) => { const timeoutId = window.setTimeout(resolve, delay) - webhookSingleRunTimeoutRef.current = timeoutId - webhookSingleRunDelayResolveRef.current = resolve + pluginSingleRunTimeoutRef.current = timeoutId + pluginSingleRunDelayResolveRef.current = resolve controller.signal.addEventListener('abort', () => { window.clearTimeout(timeoutId) resolve() }, { once: true }) }) - webhookSingleRunTimeoutRef.current = undefined - webhookSingleRunDelayResolveRef.current = null + pluginSingleRunTimeoutRef.current = undefined + pluginSingleRunDelayResolveRef.current = null continue } if (responseData?.status === 'error') { const message = responseData.message || 'Plugin debug failed' Toast.notify({ type: 'error', message }) - cancelWebhookSingleRun() + cancelPluginSingleRun() throw new Error(message) } @@ -434,33 +455,33 @@ const useOneStepRun = ({ data: { ...data, _isSingleRun: false, - _singleRunningStatus: NodeRunningStatus.Running, + _singleRunningStatus: NodeRunningStatus.Listening, }, }) - cancelWebhookSingleRun() + cancelPluginSingleRun() return responseData } catch (error) { - if (controller.signal.aborted && (!webhookSingleRunActiveRef.current || token !== webhookSingleRunTokenRef.current)) + if (controller.signal.aborted && (!pluginSingleRunActiveRef.current || token !== pluginSingleRunTokenRef.current)) return null if (controller.signal.aborted) return null console.error('handleRun: plugin debug polling error', error) Toast.notify({ type: 'error', message: 'Plugin debug request failed' }) - cancelWebhookSingleRun() + cancelPluginSingleRun() if (error instanceof Error) throw error throw new Error(String(error)) } finally { - webhookSingleRunAbortRef.current = null + pluginSingleRunAbortRef.current = null } } return null - }, [flowId, id, data, handleNodeDataUpdate, cancelWebhookSingleRun]) + }, [flowId, id, data, handleNodeDataUpdate, cancelPluginSingleRun]) const checkValidWrap = () => { if (!checkValid) @@ -527,15 +548,17 @@ const useOneStepRun = ({ const isPluginNode = data.type === BlockEnum.TriggerPlugin const isTriggerNode = isWebhookNode || isPluginNode - if (isTriggerNode) + if (isWebhookNode) cancelWebhookSingleRun() + if (isPluginNode) + cancelPluginSingleRun() handleNodeDataUpdate({ id, data: { ...data, _isSingleRun: false, - _singleRunningStatus: isTriggerNode ? NodeRunningStatus.Waiting : NodeRunningStatus.Running, + _singleRunningStatus: isTriggerNode ? NodeRunningStatus.Listening : NodeRunningStatus.Running, }, }) let res: any @@ -545,28 +568,32 @@ const useOneStepRun = ({ if (isWebhookNode) { res = await runWebhookSingleRun() if (!res) { - handleNodeDataUpdate({ - id, - data: { - ...data, - _isSingleRun: false, - _singleRunningStatus: NodeRunningStatus.NotStart, - }, - }) + if (webhookSingleRunActiveRef.current) { + handleNodeDataUpdate({ + id, + data: { + ...data, + _isSingleRun: false, + _singleRunningStatus: NodeRunningStatus.Stopped, + }, + }) + } return false } } else if (isPluginNode) { res = await runPluginSingleRun() if (!res) { - handleNodeDataUpdate({ - id, - data: { - ...data, - _isSingleRun: false, - _singleRunningStatus: NodeRunningStatus.NotStart, - }, - }) + if (pluginSingleRunActiveRef.current) { + handleNodeDataUpdate({ + id, + data: { + ...data, + _isSingleRun: false, + _singleRunningStatus: NodeRunningStatus.Stopped, + }, + }) + } return false } } @@ -817,8 +844,10 @@ const useOneStepRun = ({ } } finally { - if (isTriggerNode) + if (isWebhookNode) cancelWebhookSingleRun() + if (isPluginNode) + cancelPluginSingleRun() if (!isPausedRef.current && !isIteration && !isLoop && res) { setRunResult({ ...res, @@ -846,11 +875,13 @@ const useOneStepRun = ({ const handleStop = () => { cancelWebhookSingleRun() + cancelPluginSingleRun() handleNodeDataUpdate({ id, data: { ...data, - _singleRunningStatus: NodeRunningStatus.NotStart, + _isSingleRun: false, + _singleRunningStatus: NodeRunningStatus.Stopped, }, }) } diff --git a/web/app/components/workflow/run/status.tsx b/web/app/components/workflow/run/status.tsx index a20b9f8d65..97432254b8 100644 --- a/web/app/components/workflow/run/status.tsx +++ b/web/app/components/workflow/run/status.tsx @@ -26,34 +26,6 @@ const StatusPanel: FC = ({ const docLink = useDocLink() const isListening = useStore(s => s.isListening) - if (isListening) { - return ( - -
-
-
{t('runLog.resultPanel.status')}
-
- - LISTENING -
-
-
-
{t('runLog.resultPanel.time')}
-
-
-
-
-
-
{t('runLog.resultPanel.tokens')}
-
-
-
-
-
- - ) - } - return (
@@ -75,7 +47,7 @@ const StatusPanel: FC = ({ {status === 'running' && ( <> - Running + {isListening ? 'Listening' : 'Running'} )} {status === 'succeeded' && ( diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index e29a61cb3a..1cc9a4a6c6 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -360,6 +360,7 @@ export enum WorkflowVersion { export enum NodeRunningStatus { NotStart = 'not-start', Waiting = 'waiting', + Listening = 'listening', Running = 'running', Succeeded = 'succeeded', Failed = 'failed',