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 32bd08a7a8..03521c847d 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 @@ -8,6 +8,7 @@ import { useCallback, useEffect, useMemo, + useState, } from 'react' import { RiCloseLine, @@ -74,7 +75,7 @@ const BasePanel: FC = ({ const { showMessageLogModal } = useAppStore(useShallow(state => ({ showMessageLogModal: state.showMessageLogModal, }))) - const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running + const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running const showSingleRunPanel = useStore(s => s.showSingleRunPanel) const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth) @@ -146,6 +147,19 @@ const BasePanel: FC = ({ const isSupportSingleRun = canRunBySingle(data.type, isChildNode) const appDetail = useAppStore(state => state.appDetail) + const [hasClickRunning, setHasClickRunning] = useState(false) + const [isPaused, setIsPaused] = useState(false) + useEffect(() => { + if(data._singleRunningStatus === NodeRunningStatus.Running) { + setHasClickRunning(true) + setIsPaused(false) + } + else if(data._singleRunningStatus === undefined) { + setIsPaused(true) + setHasClickRunning(false) + } + }, [data._singleRunningStatus]) + const { isShowSingleRun, hideSingleRun, @@ -170,8 +184,13 @@ const BasePanel: FC = ({ id, data, defaultRunInputData: NODES_EXTRA_DATA[data.type]?.defaultRunInputData || {}, + isPaused, }) + useEffect(() => { + setIsPaused(false) + }, [tabType]) + const logParams = useLogs() const passedLogParams = (() => { if ([BlockEnum.Tool, BlockEnum.Agent, BlockEnum.Iteration, BlockEnum.Loop].includes(data.type)) @@ -380,6 +399,7 @@ const BasePanel: FC = ({ onSingleRunClicked={handleSingleRun} nodeInfo={nodeInfo} singleRunResult={runResult!} + isPaused={isPaused} {...passedLogParams} /> )} diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx index dfbea26ce2..111d6c8393 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx @@ -18,6 +18,7 @@ type Props = { runningStatus?: NodeRunningStatus onSingleRunClicked: () => void singleRunResult?: NodeTracing + isPaused?: boolean } & Partial const LastRun: FC = ({ @@ -29,6 +30,7 @@ const LastRun: FC = ({ runningStatus: oneStepRunRunningStatus, onSingleRunClicked, singleRunResult, + isPaused, ...otherResultPanelProps }) => { const isOneStepRunSucceed = oneStepRunRunningStatus === NodeRunningStatus.Succeeded @@ -36,10 +38,13 @@ const LastRun: FC = ({ const canRunLastRun = !isRunAfterSingleRun || isOneStepRunSucceed || isOneStepRunFailed const { data: lastRunResult, isFetching, error } = useLastRun(appId, nodeId, canRunLastRun) const isRunning = useMemo(() => { + if(isPaused) + return false + if(!isRunAfterSingleRun) return isFetching return [NodeRunningStatus.Running, NodeRunningStatus.NotStart].includes(oneStepRunRunningStatus!) - }, [isFetching, isRunAfterSingleRun, oneStepRunRunningStatus]) + }, [isFetching, isPaused, isRunAfterSingleRun, oneStepRunRunningStatus]) const noLastRun = (error as any)?.status === 404 const runResult = (canRunLastRun ? lastRunResult : singleRunResult) || {} @@ -64,6 +69,7 @@ const LastRun: FC = ({ = { iteratorInputKey?: string loopInputKey?: string isRunAfterSingleRun: boolean + isPaused: boolean } const varTypeToInputVarType = (type: VarType, { @@ -111,6 +112,7 @@ const useOneStepRun = ({ iteratorInputKey, loopInputKey, isRunAfterSingleRun, + isPaused, }: Params) => { const { t } = useTranslation() const { getBeforeNodesInSameBranch, getBeforeNodesInSameBranchIncludeParent } = useWorkflow() as any @@ -173,8 +175,14 @@ const useOneStepRun = ({ invalidateConversationVarValues, } = useInspectVarsCrud() const runningStatus = data._singleRunningStatus || NodeRunningStatus.NotStart - const isPaused = !data._isSingleRun + const isPausedRef = useRef(isPaused) + useEffect(() => { + isPausedRef.current = isPaused + }, [isPaused]) + const setRunResult = useCallback(async (data: NodeRunResult | null) => { + const isPaused = isPausedRef.current + // The backend don't support pause the single run, so the frontend handle the pause state. if(isPaused) return @@ -196,7 +204,7 @@ const useOneStepRun = ({ 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. } - }, [isPaused, isRunAfterSingleRun, runningStatus, appId, id, store, appendNodeInspectVars, invalidLastRun, isStartNode, invalidateSysVarValues, invalidateConversationVarValues]) + }, [isRunAfterSingleRun, runningStatus, appId, id, store, appendNodeInspectVars, invalidLastRun, isStartNode, invalidateSysVarValues, invalidateConversationVarValues]) const { handleNodeDataUpdate }: { handleNodeDataUpdate: (data: any) => void } = useNodeDataUpdate() const setNodeRunning = () => { @@ -303,6 +311,8 @@ const useOneStepRun = ({ { onWorkflowStarted: noop, onWorkflowFinished: (params) => { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { @@ -380,6 +390,8 @@ const useOneStepRun = ({ setIterationRunResult(newIterationRunResult) }, onError: () => { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { @@ -402,6 +414,8 @@ const useOneStepRun = ({ { onWorkflowStarted: noop, onWorkflowFinished: (params) => { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { @@ -480,6 +494,8 @@ const useOneStepRun = ({ setLoopRunResult(newLoopRunResult) }, onError: () => { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { @@ -500,6 +516,8 @@ const useOneStepRun = ({ hasError = true invalidLastRun() if (!isIteration && !isLoop) { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { @@ -512,7 +530,7 @@ const useOneStepRun = ({ } } finally { - if (!isIteration && !isLoop && res) { + if (!isPausedRef.current && !isIteration && !isLoop && res) { setRunResult({ ...res, total_tokens: res.execution_metadata?.total_tokens || 0, @@ -520,7 +538,12 @@ const useOneStepRun = ({ }) } } + if(isPausedRef.current) + return + if (!isIteration && !isLoop && !hasError) { + if(isPausedRef.current) + return handleNodeDataUpdate({ id, data: { diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 3a3dca4763..fd710f7772 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -329,6 +329,7 @@ export enum NodeRunningStatus { Failed = 'failed', Exception = 'exception', Retry = 'retry', + Stopped = 'stopped', } export type OnNodeAdd = (