diff --git a/web/app/components/workflow-app/hooks/use-workflow-run.ts b/web/app/components/workflow-app/hooks/use-workflow-run.ts index 8fd3b57ac6..29b2ecd674 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-run.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-run.ts @@ -215,21 +215,24 @@ export const useWorkflowRun = () => { const { setWorkflowRunningData, + setIsListening, } = workflowStore.getState() if (runMode === 'webhook') { + setIsListening(true) setWorkflowRunningData({ result: { - status: WorkflowRunningStatus.Listening, + status: WorkflowRunningStatus.Running, inputs_truncated: false, process_data_truncated: false, outputs_truncated: false, }, tracing: [], - resultText: 'Listening for webhook call...', + resultText: '', }) } else { + setIsListening(false) setWorkflowRunningData({ result: { status: WorkflowRunningStatus.Running, @@ -260,9 +263,15 @@ export const useWorkflowRun = () => { abortControllerRef.current = null } + const clearListeningState = () => { + const state = workflowStore.getState() + state.setIsListening(false) + } + const wrappedOnError = (params: any) => { clearAbortController() handleWorkflowFailed() + clearListeningState() if (onError) onError(params) @@ -270,6 +279,7 @@ export const useWorkflowRun = () => { const wrappedOnCompleted: IOtherOptions['onCompleted'] = async (hasError?: boolean, errorMessage?: string) => { clearAbortController() + clearListeningState() if (onCompleted) onCompleted(hasError, errorMessage) } @@ -289,6 +299,7 @@ export const useWorkflowRun = () => { onWorkflowStarted(params) }, onWorkflowFinished: (params) => { + clearListeningState() handleWorkflowFinished(params) if (onWorkflowFinished) @@ -468,9 +479,11 @@ export const useWorkflowRun = () => { }, tracing: [], }) + setIsListening(false) return } + setIsListening(false) handleStream( response, baseSseOptions.onData ?? noop, @@ -518,6 +531,7 @@ export const useWorkflowRun = () => { }, tracing: [], }) + setIsListening(false) } } @@ -561,7 +575,7 @@ export const useWorkflowRun = () => { abortControllerRef.current.abort() abortControllerRef.current = null - const { setWorkflowRunningData } = workflowStore.getState() + const { setWorkflowRunningData, setIsListening } = workflowStore.getState() setWorkflowRunningData({ result: { status: WorkflowRunningStatus.Stopped, @@ -572,6 +586,7 @@ export const useWorkflowRun = () => { tracing: [], resultText: '', }) + setIsListening(false) }, [workflowStore]) const handleRestoreFromPublishedWorkflow = useCallback((publishedWorkflow: VersionHistory) => { diff --git a/web/app/components/workflow/header/run-mode.tsx b/web/app/components/workflow/header/run-mode.tsx index 682e0ff435..43058f3e1f 100644 --- a/web/app/components/workflow/header/run-mode.tsx +++ b/web/app/components/workflow/header/run-mode.tsx @@ -28,9 +28,9 @@ const RunMode = ({ const { handleStopRun } = useWorkflowRun() const { validateBeforeRun } = useWorkflowRunValidation() const workflowRunningData = useStore(s => s.workflowRunningData) + const isListening = useStore(s => s.isListening) const status = workflowRunningData?.result.status - const isListening = status === WorkflowRunningStatus.Listening const isRunning = status === WorkflowRunningStatus.Running || isListening const dynamicOptions = useDynamicTestRunOptions() diff --git a/web/app/components/workflow/panel/record.tsx b/web/app/components/workflow/panel/record.tsx index bea17e9013..e9c677d235 100644 --- a/web/app/components/workflow/panel/record.tsx +++ b/web/app/components/workflow/panel/record.tsx @@ -5,11 +5,9 @@ import { useStore } from '../store' import { useWorkflowUpdate } from '../hooks' import { useHooksStore } from '../hooks-store' import { formatWorkflowRunIdentifier } from '../utils' -import { WorkflowRunningStatus } from '../types' const Record = () => { const historyWorkflowData = useStore(s => s.historyWorkflowData) - const workflowRunningData = useStore(s => s.workflowRunningData) const { handleUpdateWorkflowCanvas } = useWorkflowUpdate() const getWorkflowRunAndTraceUrl = useHooksStore(s => s.getWorkflowRunAndTraceUrl) @@ -22,9 +20,6 @@ const Record = () => { }) }, [handleUpdateWorkflowCanvas]) - const currentStatus = workflowRunningData?.result.status - const activeTab = currentStatus === WorkflowRunningStatus.Listening ? 'DETAIL' : undefined - return (
@@ -34,8 +29,6 @@ const Record = () => { runDetailUrl={getWorkflowRunAndTraceUrl(historyWorkflowData?.id).runUrl} tracingListUrl={getWorkflowRunAndTraceUrl(historyWorkflowData?.id).traceUrl} getResultCallback={handleResultCallback} - activeTab={activeTab} - statusHint={currentStatus} />
) diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx index 6b6ac4100c..292a964b9e 100644 --- a/web/app/components/workflow/panel/workflow-preview.tsx +++ b/web/app/components/workflow/panel/workflow-preview.tsx @@ -31,6 +31,7 @@ const WorkflowPreview = () => { const { t } = useTranslation() const { handleCancelDebugAndPreviewPanel } = useWorkflowInteractions() const workflowRunningData = useStore(s => s.workflowRunningData) + const isListening = useStore(s => s.isListening) const showInputsPanel = useStore(s => s.showInputsPanel) const workflowCanvasWidth = useStore(s => s.workflowCanvasWidth) const panelWidth = useStore(s => s.previewPanelWidth) @@ -47,16 +48,16 @@ const WorkflowPreview = () => { setCurrentTab('INPUT') }, [showDebugAndPreviewPanel, showInputsPanel]) + useEffect(() => { + if (isListening) + switchTab('DETAIL') + }, [isListening]) + useEffect(() => { const status = workflowRunningData?.result.status if (!workflowRunningData) return - if (status === WorkflowRunningStatus.Listening) { - switchTab('DETAIL') - return - } - if ((status === WorkflowRunningStatus.Succeeded || status === WorkflowRunningStatus.Failed) && !workflowRunningData.resultText && !workflowRunningData.result.files?.length) switchTab('DETAIL') }, [workflowRunningData]) diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx index d9f226c52e..2b32b05796 100644 --- a/web/app/components/workflow/run/index.tsx +++ b/web/app/components/workflow/run/index.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next' import OutputPanel from './output-panel' import ResultPanel from './result-panel' import StatusPanel from './status' +import { WorkflowRunningStatus } from '@/app/components/workflow/types' import TracingPanel from './tracing-panel' import cn from '@/utils/classnames' import { ToastContext } from '@/app/components/base/toast' @@ -13,14 +14,14 @@ import Loading from '@/app/components/base/loading' import { fetchRunDetail, fetchTracingList } from '@/service/log' import type { NodeTracing } from '@/types/workflow' import type { WorkflowRunDetailResponse } from '@/models/log' -import { WorkflowRunningStatus } from '@/app/components/workflow/types' +import { useStore } from '../store' + export type RunProps = { hideResult?: boolean activeTab?: 'RESULT' | 'DETAIL' | 'TRACING' getResultCallback?: (result: WorkflowRunDetailResponse) => void runDetailUrl: string tracingListUrl: string - statusHint?: string } const RunPanel: FC = ({ @@ -29,7 +30,6 @@ const RunPanel: FC = ({ getResultCallback, runDetailUrl, tracingListUrl, - statusHint, }) => { const { t } = useTranslation() const { notify } = useContext(ToastContext) @@ -37,6 +37,7 @@ const RunPanel: FC = ({ const [loading, setLoading] = useState(true) const [runDetail, setRunDetail] = useState() const [list, setList] = useState([]) + const isListening = useStore(s => s.isListening) const executor = useMemo(() => { if (runDetail?.created_by_role === 'account') @@ -90,19 +91,17 @@ const RunPanel: FC = ({ tracingListUrl && await getTracingList() } + useEffect(() => { + if (isListening) + setCurrentTab('DETAIL') + }, [isListening]) + useEffect(() => { // fetch data if (runDetailUrl && tracingListUrl) getData() }, [runDetailUrl, tracingListUrl]) - const derivedStatus = runDetail?.status ?? statusHint - - useEffect(() => { - if (derivedStatus === WorkflowRunningStatus.Listening && currentTab !== 'DETAIL') - setCurrentTab('DETAIL') - }, [currentTab, derivedStatus]) - const [height, setHeight] = useState(0) const ref = useRef(null) @@ -174,9 +173,9 @@ const RunPanel: FC = ({ exceptionCounts={runDetail.exceptions_count} /> )} - {!loading && currentTab === 'DETAIL' && !runDetail && derivedStatus === WorkflowRunningStatus.Listening && ( + {!loading && currentTab === 'DETAIL' && !runDetail && isListening && ( )} {!loading && currentTab === 'TRACING' && ( diff --git a/web/app/components/workflow/run/status-container.tsx b/web/app/components/workflow/run/status-container.tsx index 61340aae88..6837592c4e 100644 --- a/web/app/components/workflow/run/status-container.tsx +++ b/web/app/components/workflow/run/status-container.tsx @@ -14,7 +14,6 @@ const StatusContainer: FC = ({ children, }) => { const { theme } = useTheme() - const isActive = status === 'running' || status === 'listening' return (
= ({ status === 'exception' && 'border-[rgba(247,144,9,0.8)] bg-workflow-display-warning-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-warning.svg)] text-text-destructive', status === 'exception' && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(247,144,9,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]', status === 'exception' && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(247,144,9,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]', - isActive && 'border-[rgba(11,165,236,0.8)] bg-workflow-display-normal-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-running.svg)] text-util-colors-blue-light-blue-light-600', - isActive && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(11,165,236,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]', - isActive && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(11,165,236,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]', + status === 'running' && 'border-[rgba(11,165,236,0.8)] bg-workflow-display-normal-bg bg-[url(~@/app/components/workflow/run/assets/bg-line-running.svg)] text-util-colors-blue-light-blue-light-600', + status === 'running' && theme === Theme.light && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.5),inset_0_1px_3px_0_rgba(0,0,0,0.12),inset_0_2px_24px_0_rgba(11,165,236,0.2),0_1px_2px_0_rgba(9,9,11,0.05),0_0_0_1px_rgba(0,0,0,0.05)]', + status === 'running' && theme === Theme.dark && 'shadow-[inset_2px_2px_0_0_rgba(255,255,255,0.12),inset_0_1px_3px_0_rgba(0,0,0,0.4),inset_0_2px_24px_0_rgba(11,165,236,0.25),0_1px_2px_0_rgba(0,0,0,0.1),0_0_0_1px_rgba(24, 24, 27, 0.95)]', )} >
= ({ }) => { const { t } = useTranslation() const docLink = useDocLink() - const isActive = status === WorkflowRunningStatus.Running || status === WorkflowRunningStatus.Listening + const isListening = useStore(s => s.isListening) + + if (isListening) { + return ( + +
+
+
{t('runLog.resultPanel.status')}
+
+ + LISTENING +
+
+
+
{t('runLog.resultPanel.time')}
+
+
+
+
+
+
{t('runLog.resultPanel.tokens')}
+
+
+
+
+
+ + ) + } return ( @@ -41,21 +69,15 @@ const StatusPanel: FC = ({ status === 'partial-succeeded' && 'text-util-colors-green-green-600', status === 'failed' && 'text-util-colors-red-red-600', status === 'stopped' && 'text-util-colors-warning-warning-600', - isActive && 'text-util-colors-blue-light-blue-light-600', + status === 'running' && 'text-util-colors-blue-light-blue-light-600', )} > - {status === WorkflowRunningStatus.Running && ( + {status === 'running' && ( <> Running )} - {status === WorkflowRunningStatus.Listening && ( - <> - - Listening - - )} {status === 'succeeded' && ( <> @@ -91,10 +113,10 @@ const StatusPanel: FC = ({
{t('runLog.resultPanel.time')}
- {isActive && ( + {status === 'running' && (
)} - {!isActive && ( + {status !== 'running' && ( {time ? `${time?.toFixed(3)}s` : '-'} )}
@@ -102,10 +124,10 @@ const StatusPanel: FC = ({
{t('runLog.resultPanel.tokens')}
- {isActive && ( + {status === 'running' && (
)} - {!isActive && ( + {status !== 'running' && ( {`${tokens || 0} Tokens`} )}
diff --git a/web/app/components/workflow/store/workflow/workflow-slice.ts b/web/app/components/workflow/store/workflow/workflow-slice.ts index 91dac42adb..b25b111671 100644 --- a/web/app/components/workflow/store/workflow/workflow-slice.ts +++ b/web/app/components/workflow/store/workflow/workflow-slice.ts @@ -13,6 +13,8 @@ type PreviewRunningData = WorkflowRunningData & { export type WorkflowSliceShape = { workflowRunningData?: PreviewRunningData setWorkflowRunningData: (workflowData: PreviewRunningData) => void + isListening: boolean + setIsListening: (listening: boolean) => void clipboardElements: Node[] setClipboardElements: (clipboardElements: Node[]) => void selection: null | { x1: number; y1: number; x2: number; y2: number } @@ -36,6 +38,8 @@ export type WorkflowSliceShape = { export const createWorkflowSlice: StateCreator = set => ({ workflowRunningData: undefined, setWorkflowRunningData: workflowRunningData => set(() => ({ workflowRunningData })), + isListening: false, + setIsListening: listening => set(() => ({ isListening: listening })), clipboardElements: [], setClipboardElements: clipboardElements => set(() => ({ clipboardElements })), selection: null, diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 1be4fcbac3..5e44565c9e 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -344,7 +344,6 @@ export type NodeDefault = { export type OnSelectBlock = (type: BlockEnum, pluginDefaultValue?: PluginDefaultValue) => void export enum WorkflowRunningStatus { - Listening = 'listening', Waiting = 'waiting', Running = 'running', Succeeded = 'succeeded',