mirror of
https://github.com/langgenius/dify.git
synced 2026-01-08 07:14:14 +00:00
fix
This commit is contained in:
@@ -8,7 +8,10 @@ import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from '../store'
|
||||
import { useWorkflowRun } from '../hooks'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
useWorkflowRun,
|
||||
} from '../hooks'
|
||||
import RunAndHistory from './run-and-history'
|
||||
import EditingTitle from './editing-title'
|
||||
import RunningTitle from './running-title'
|
||||
@@ -24,7 +27,10 @@ const Header: FC = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
const appSidebarExpand = useAppStore(s => s.appSidebarExpand)
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const {
|
||||
nodesReadOnly,
|
||||
getNodesReadOnly,
|
||||
} = useNodesReadOnly()
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
const {
|
||||
handleRunSetting,
|
||||
@@ -32,11 +38,11 @@ const Header: FC = () => {
|
||||
} = useWorkflowRun()
|
||||
|
||||
const handleShowFeatures = useCallback(() => {
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
workflowStore.setState({ showFeaturesPanel: true })
|
||||
}, [runningStatus, workflowStore])
|
||||
}, [getNodesReadOnly, workflowStore])
|
||||
|
||||
const handleGoBackToEdit = useCallback(() => {
|
||||
handleRunSetting(true)
|
||||
@@ -65,10 +71,10 @@ const Header: FC = () => {
|
||||
)
|
||||
}
|
||||
{
|
||||
!runningStatus && !isRestoring && <EditingTitle />
|
||||
!nodesReadOnly && !isRestoring && <EditingTitle />
|
||||
}
|
||||
{
|
||||
runningStatus && !isRestoring && <RunningTitle />
|
||||
nodesReadOnly && !isRestoring && <RunningTitle />
|
||||
}
|
||||
{
|
||||
isRestoring && <RestoringTitle />
|
||||
@@ -78,7 +84,7 @@ const Header: FC = () => {
|
||||
!isRestoring && (
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
runningStatus && (
|
||||
nodesReadOnly && (
|
||||
<Button
|
||||
className={`
|
||||
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
|
||||
@@ -97,7 +103,7 @@ const Header: FC = () => {
|
||||
className={`
|
||||
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
|
||||
border-[0.5px] border-gray-200 shadow-xs
|
||||
${runningStatus && '!cursor-not-allowed opacity-50'}
|
||||
${nodesReadOnly && '!cursor-not-allowed opacity-50'}
|
||||
`}
|
||||
onClick={handleShowFeatures}
|
||||
>
|
||||
@@ -115,7 +121,6 @@ const Header: FC = () => {
|
||||
className={`
|
||||
px-3 py-0 h-8 bg-white text-[13px] font-medium text-gray-700
|
||||
border-[0.5px] border-gray-200 shadow-xs
|
||||
${runningStatus && '!cursor-not-allowed opacity-50'}
|
||||
`}
|
||||
onClick={handleShowFeatures}
|
||||
>
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useWorkflowStore,
|
||||
} from '../store'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
useWorkflow,
|
||||
useWorkflowRun,
|
||||
@@ -31,7 +32,10 @@ const Publish = () => {
|
||||
const { formatTimeFromNow } = useWorkflow()
|
||||
const { handleCheckBeforePublish } = useWorkflowRun()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const {
|
||||
nodesReadOnly,
|
||||
getNodesReadOnly,
|
||||
} = useNodesReadOnly()
|
||||
const draftUpdatedAt = useStore(s => s.draftUpdatedAt)
|
||||
const publishedAt = useStore(s => s.publishedAt)
|
||||
const [open, setOpen] = useState(false)
|
||||
@@ -61,7 +65,7 @@ const Publish = () => {
|
||||
}, [workflowStore])
|
||||
|
||||
const handleTrigger = useCallback(() => {
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
if (open)
|
||||
@@ -72,7 +76,7 @@ const Publish = () => {
|
||||
setOpen(true)
|
||||
setPublished(false)
|
||||
}
|
||||
}, [runningStatus, open, handleSyncWorkflowDraft])
|
||||
}, [getNodesReadOnly, open, handleSyncWorkflowDraft])
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
@@ -89,7 +93,7 @@ const Publish = () => {
|
||||
type='primary'
|
||||
className={`
|
||||
px-3 py-0 h-8 text-[13px] font-medium
|
||||
${runningStatus && 'cursor-not-allowed opacity-50'}
|
||||
${nodesReadOnly && 'cursor-not-allowed opacity-50'}
|
||||
`}
|
||||
>
|
||||
{t('workflow.common.publish')}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '../store'
|
||||
import {
|
||||
useIsChatMode,
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
useWorkflowRun,
|
||||
} from '../hooks'
|
||||
@@ -25,9 +26,9 @@ const RunMode = memo(() => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleStopRun } = useWorkflowRun()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const showInputsPanel = useStore(s => s.showInputsPanel)
|
||||
const isRunning = runningStatus === WorkflowRunningStatus.Running
|
||||
const isRunning = workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
||||
|
||||
const handleClick = () => {
|
||||
workflowStore.setState({ showInputsPanel: true })
|
||||
@@ -65,7 +66,7 @@ const RunMode = memo(() => {
|
||||
isRunning && (
|
||||
<div
|
||||
className='flex items-center justify-center ml-0.5 w-7 h-7 cursor-pointer hover:bg-black/5 rounded-md'
|
||||
onClick={handleStopRun}
|
||||
onClick={() => handleStopRun(workflowRunningData?.task_id || '')}
|
||||
>
|
||||
<StopCircle className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
@@ -80,7 +81,7 @@ const PreviewMode = memo(() => {
|
||||
const { t } = useTranslation()
|
||||
const { handleRunSetting } = useWorkflowRun()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
const handleClick = () => {
|
||||
handleSyncWorkflowDraft(true)
|
||||
@@ -92,12 +93,12 @@ const PreviewMode = memo(() => {
|
||||
className={`
|
||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||
hover:bg-primary-50 cursor-pointer
|
||||
${runningStatus && 'bg-primary-50 opacity-50 !cursor-not-allowed'}
|
||||
${nodesReadOnly && 'bg-primary-50 opacity-50 !cursor-not-allowed'}
|
||||
`}
|
||||
onClick={() => !runningStatus && handleClick()}
|
||||
onClick={() => !nodesReadOnly && handleClick()}
|
||||
>
|
||||
{
|
||||
runningStatus
|
||||
nodesReadOnly
|
||||
? (
|
||||
<>
|
||||
{t('workflow.common.inPreview')}
|
||||
@@ -140,7 +141,7 @@ const RunAndHistory: FC = () => {
|
||||
${showRunHistory && 'bg-primary-50'}
|
||||
`}
|
||||
onClick={() => {
|
||||
workflowStore.setState({ showRunHistory: !showRunHistory, workflowRunId: '' })
|
||||
workflowStore.setState({ showRunHistory: !showRunHistory })
|
||||
setCurrentLogItem()
|
||||
setShowMessageLogModal(false)
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
getConnectedEdges,
|
||||
useStoreApi,
|
||||
} from 'reactflow'
|
||||
import { useWorkflowStore } from '../store'
|
||||
import type {
|
||||
Edge,
|
||||
Node,
|
||||
@@ -16,16 +15,15 @@ import type {
|
||||
import { BlockEnum } from '../types'
|
||||
import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../utils'
|
||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
||||
import { useNodesReadOnly } from './use-workflow'
|
||||
|
||||
export const useEdgesInteractions = () => {
|
||||
const store = useStoreApi()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
const handleEdgeEnter = useCallback<EdgeMouseHandler>((_, edge) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -38,12 +36,10 @@ export const useEdgesInteractions = () => {
|
||||
currentEdge.data._hovering = true
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store, workflowStore])
|
||||
}, [store, getNodesReadOnly])
|
||||
|
||||
const handleEdgeLeave = useCallback<EdgeMouseHandler>((_, edge) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -56,12 +52,10 @@ export const useEdgesInteractions = () => {
|
||||
currentEdge.data._hovering = false
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store, workflowStore])
|
||||
}, [store, getNodesReadOnly])
|
||||
|
||||
const handleEdgeDeleteByDeleteBranch = useCallback((nodeId: string, branchId: string) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -92,12 +86,10 @@ export const useEdgesInteractions = () => {
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, workflowStore])
|
||||
}, [store, handleSyncWorkflowDraft, getNodesReadOnly])
|
||||
|
||||
const handleEdgeDelete = useCallback(() => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -127,12 +119,10 @@ export const useEdgesInteractions = () => {
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, workflowStore, handleSyncWorkflowDraft])
|
||||
}, [store, getNodesReadOnly, handleSyncWorkflowDraft])
|
||||
|
||||
const handleEdgesChange = useCallback<OnEdgesChange>((changes) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -147,7 +137,7 @@ export const useEdgesInteractions = () => {
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store, workflowStore])
|
||||
}, [store, getNodesReadOnly])
|
||||
|
||||
const handleVariableAssignerEdgesChange = useCallback((nodeId: string, variables: any) => {
|
||||
const {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { useWorkflowStore } from '../store'
|
||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
||||
import { useNodesReadOnly } from './use-workflow'
|
||||
|
||||
type NodeDataUpdatePayload = {
|
||||
id: string
|
||||
@@ -11,8 +11,8 @@ type NodeDataUpdatePayload = {
|
||||
|
||||
export const useNodeDataUpdate = () => {
|
||||
const store = useStoreApi()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
const handleNodeDataUpdate = useCallback(({ id, data }: NodeDataUpdatePayload) => {
|
||||
const {
|
||||
@@ -28,14 +28,12 @@ export const useNodeDataUpdate = () => {
|
||||
}, [store])
|
||||
|
||||
const handleNodeDataUpdateWithSyncDraft = useCallback((payload: NodeDataUpdatePayload) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
handleNodeDataUpdate(payload)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [handleSyncWorkflowDraft, handleNodeDataUpdate, workflowStore])
|
||||
}, [handleSyncWorkflowDraft, handleNodeDataUpdate, getNodesReadOnly])
|
||||
|
||||
return {
|
||||
handleNodeDataUpdate,
|
||||
|
||||
@@ -32,7 +32,10 @@ import {
|
||||
} from '../utils'
|
||||
import { useNodesExtraData } from './use-nodes-data'
|
||||
import { useNodesSyncDraft } from './use-nodes-sync-draft'
|
||||
import { useWorkflow } from './use-workflow'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
useWorkflow,
|
||||
} from './use-workflow'
|
||||
|
||||
export const useNodesInteractions = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -41,25 +44,21 @@ export const useNodesInteractions = () => {
|
||||
const nodesExtraData = useNodesExtraData()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { getAfterNodesInSameBranch } = useWorkflow()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const dragNodeStartPosition = useRef({ x: 0, y: 0 } as { x: number; y: number })
|
||||
const connectingNodeRef = useRef<{ nodeId: string; handleType: HandleType } | null>(null)
|
||||
|
||||
const handleNodeDragStart = useCallback<NodeDragHandler>((_, node) => {
|
||||
workflowStore.setState({ nodeAnimation: false })
|
||||
const {
|
||||
runningStatus,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
dragNodeStartPosition.current = { x: node.position.x, y: node.position.y }
|
||||
}, [workflowStore])
|
||||
}, [workflowStore, getNodesReadOnly])
|
||||
|
||||
const handleNodeDrag = useCallback<NodeDragHandler>((e, node: Node) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -159,16 +158,15 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
|
||||
setNodes(newNodes)
|
||||
}, [store, workflowStore])
|
||||
}, [store, workflowStore, getNodesReadOnly])
|
||||
|
||||
const handleNodeDragStop = useCallback<NodeDragHandler>((_, node) => {
|
||||
const {
|
||||
runningStatus,
|
||||
setHelpLineHorizontal,
|
||||
setHelpLineVertical,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const { x, y } = dragNodeStartPosition.current
|
||||
@@ -177,12 +175,10 @@ export const useNodesInteractions = () => {
|
||||
setHelpLineVertical()
|
||||
handleSyncWorkflowDraft()
|
||||
}
|
||||
}, [handleSyncWorkflowDraft, workflowStore])
|
||||
}, [handleSyncWorkflowDraft, workflowStore, getNodesReadOnly])
|
||||
|
||||
const handleNodeEnter = useCallback<NodeMouseHandler>((_, node) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -217,12 +213,10 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store, nodesExtraData, workflowStore])
|
||||
}, [store, nodesExtraData, getNodesReadOnly])
|
||||
|
||||
const handleNodeLeave = useCallback<NodeMouseHandler>(() => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -243,12 +237,10 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
}, [store, workflowStore])
|
||||
}, [store, getNodesReadOnly])
|
||||
|
||||
const handleNodeSelect = useCallback((nodeId: string, cancelSelection?: boolean) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -272,18 +264,14 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
setNodes(newNodes)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, workflowStore])
|
||||
}, [store, handleSyncWorkflowDraft, getNodesReadOnly])
|
||||
|
||||
const handleNodeClick = useCallback<NodeMouseHandler>((_, node) => {
|
||||
const {
|
||||
runningStatus,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
handleNodeSelect(node.id)
|
||||
}, [handleNodeSelect, workflowStore])
|
||||
}, [handleNodeSelect, getNodesReadOnly])
|
||||
|
||||
const handleNodeConnect = useCallback<OnConnect>(({
|
||||
source,
|
||||
@@ -291,9 +279,7 @@ export const useNodesInteractions = () => {
|
||||
target,
|
||||
targetHandle,
|
||||
}) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -344,7 +330,7 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, workflowStore])
|
||||
}, [store, handleSyncWorkflowDraft, getNodesReadOnly])
|
||||
|
||||
const handleNodeConnectStart = useCallback<OnConnectStart>((_, { nodeId, handleType }) => {
|
||||
if (nodeId && handleType) {
|
||||
@@ -360,9 +346,7 @@ export const useNodesInteractions = () => {
|
||||
}, [])
|
||||
|
||||
const handleNodeDelete = useCallback((nodeId: string) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -393,7 +377,7 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, workflowStore])
|
||||
}, [store, handleSyncWorkflowDraft, getNodesReadOnly])
|
||||
|
||||
const handleNodeAdd = useCallback<OnNodeAdd>((
|
||||
{
|
||||
@@ -409,9 +393,7 @@ export const useNodesInteractions = () => {
|
||||
nextNodeTargetHandle,
|
||||
},
|
||||
) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
if (nodeType === BlockEnum.VariableAssigner)
|
||||
@@ -593,7 +575,7 @@ export const useNodesInteractions = () => {
|
||||
setEdges(newEdges)
|
||||
}
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, getAfterNodesInSameBranch, workflowStore, t])
|
||||
}, [store, handleSyncWorkflowDraft, getAfterNodesInSameBranch, getNodesReadOnly, t])
|
||||
|
||||
const handleNodeChange = useCallback((
|
||||
currentNodeId: string,
|
||||
@@ -601,9 +583,7 @@ export const useNodesInteractions = () => {
|
||||
sourceHandle: string,
|
||||
toolDefaultValue?: ToolDefaultValue,
|
||||
) => {
|
||||
const { runningStatus } = workflowStore.getState()
|
||||
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const {
|
||||
@@ -659,7 +639,7 @@ export const useNodesInteractions = () => {
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
}, [store, handleSyncWorkflowDraft, workflowStore, t])
|
||||
}, [store, handleSyncWorkflowDraft, getNodesReadOnly, t])
|
||||
|
||||
return {
|
||||
handleNodeDragStart,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useWorkflowStore,
|
||||
} from '../store'
|
||||
import { BlockEnum } from '../types'
|
||||
import { useNodesReadOnly } from './use-workflow'
|
||||
import { syncWorkflowDraft } from '@/service/workflow'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
@@ -18,6 +19,7 @@ export const useNodesSyncDraft = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const reactFlow = useReactFlow()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const { getNodesReadOnly } = useNodesReadOnly()
|
||||
const debouncedSyncWorkflowDraft = useStore(s => s.debouncedSyncWorkflowDraft)
|
||||
|
||||
const doSyncWorkflowDraft = useCallback(() => {
|
||||
@@ -78,19 +80,14 @@ export const useNodesSyncDraft = () => {
|
||||
}, [store, reactFlow, featuresStore, workflowStore])
|
||||
|
||||
const handleSyncWorkflowDraft = useCallback((sync?: boolean) => {
|
||||
const {
|
||||
runningStatus,
|
||||
isRestoring,
|
||||
} = workflowStore.getState()
|
||||
|
||||
if (runningStatus || isRestoring)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
if (sync)
|
||||
doSyncWorkflowDraft()
|
||||
else
|
||||
debouncedSyncWorkflowDraft(doSyncWorkflowDraft)
|
||||
}, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, workflowStore])
|
||||
}, [debouncedSyncWorkflowDraft, doSyncWorkflowDraft, getNodesReadOnly])
|
||||
|
||||
return {
|
||||
handleSyncWorkflowDraft,
|
||||
|
||||
@@ -71,10 +71,23 @@ export const useWorkflowRun = () => {
|
||||
}, [store, reactflow, workflowStore])
|
||||
|
||||
const handleRunSetting = useCallback((shouldClear?: boolean) => {
|
||||
workflowStore.setState({ runningStatus: shouldClear ? undefined : WorkflowRunningStatus.Waiting })
|
||||
workflowStore.setState({ taskId: '' })
|
||||
workflowStore.setState({ currentSequenceNumber: 0 })
|
||||
workflowStore.setState({ workflowRunId: '' })
|
||||
if (shouldClear) {
|
||||
workflowStore.setState({
|
||||
workflowRunningData: undefined,
|
||||
historyWorkflowData: undefined,
|
||||
})
|
||||
}
|
||||
else {
|
||||
workflowStore.setState({
|
||||
workflowRunningData: {
|
||||
result: {
|
||||
status: shouldClear ? '' : WorkflowRunningStatus.Waiting,
|
||||
},
|
||||
tracing: [],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const {
|
||||
setNodes,
|
||||
getNodes,
|
||||
@@ -141,11 +154,19 @@ export const useWorkflowRun = () => {
|
||||
},
|
||||
{
|
||||
onWorkflowStarted: (params) => {
|
||||
const { task_id, workflow_run_id, data } = params
|
||||
workflowStore.setState({ runningStatus: WorkflowRunningStatus.Running })
|
||||
workflowStore.setState({ taskId: task_id })
|
||||
workflowStore.setState({ currentSequenceNumber: data.sequence_number })
|
||||
workflowStore.setState({ workflowRunId: workflow_run_id })
|
||||
const { task_id, data } = params
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.task_id = task_id
|
||||
draft.result = {
|
||||
...draft?.result,
|
||||
...data,
|
||||
}
|
||||
}))
|
||||
|
||||
const newNodes = produce(getNodes(), (draft) => {
|
||||
draft.forEach((node) => {
|
||||
node.data._runningStatus = NodeRunningStatus.Waiting
|
||||
@@ -158,13 +179,31 @@ export const useWorkflowRun = () => {
|
||||
},
|
||||
onWorkflowFinished: (params) => {
|
||||
const { data } = params
|
||||
workflowStore.setState({ runningStatus: data.status as WorkflowRunningStatus })
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.result = {
|
||||
...draft.result,
|
||||
...data,
|
||||
}
|
||||
}))
|
||||
|
||||
if (onWorkflowFinished)
|
||||
onWorkflowFinished(params)
|
||||
},
|
||||
onNodeStarted: (params) => {
|
||||
const { data } = params
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
draft.tracing!.push(data as any)
|
||||
}))
|
||||
|
||||
const nodes = getNodes()
|
||||
const {
|
||||
setViewport,
|
||||
@@ -196,6 +235,17 @@ export const useWorkflowRun = () => {
|
||||
},
|
||||
onNodeFinished: (params) => {
|
||||
const { data } = params
|
||||
const {
|
||||
workflowRunningData,
|
||||
setWorkflowRunningData,
|
||||
} = workflowStore.getState()
|
||||
setWorkflowRunningData(produce(workflowRunningData!, (draft) => {
|
||||
const currentIndex = draft.tracing!.findIndex(trace => trace.node_id === data.node_id)
|
||||
|
||||
if (currentIndex > -1 && draft.tracing)
|
||||
draft.tracing[currentIndex] = data as any
|
||||
}))
|
||||
|
||||
const newNodes = produce(getNodes(), (draft) => {
|
||||
const currentNode = draft.find(node => node.id === data.node_id)!
|
||||
|
||||
@@ -211,12 +261,11 @@ export const useWorkflowRun = () => {
|
||||
)
|
||||
}, [store, reactflow, workflowStore])
|
||||
|
||||
const handleStopRun = useCallback(() => {
|
||||
const handleStopRun = useCallback((taskId: string) => {
|
||||
const appId = useAppStore.getState().appDetail?.id
|
||||
const taskId = workflowStore.getState().taskId
|
||||
|
||||
stopWorkflowRun(`/apps/${appId}/workflow-runs/tasks/${taskId}/stop`)
|
||||
}, [workflowStore])
|
||||
}, [])
|
||||
|
||||
const handleRestoreFromPublishedWorkflow = useCallback(async () => {
|
||||
const appDetail = useAppStore.getState().appDetail
|
||||
|
||||
@@ -20,8 +20,14 @@ import {
|
||||
getLayoutByDagre,
|
||||
} from '../utils'
|
||||
import type { Node } from '../types'
|
||||
import { BlockEnum } from '../types'
|
||||
import { useWorkflowStore } from '../store'
|
||||
import {
|
||||
BlockEnum,
|
||||
WorkflowRunningStatus,
|
||||
} from '../types'
|
||||
import {
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from '../store'
|
||||
import {
|
||||
AUTO_LAYOUT_OFFSET,
|
||||
START_INITIAL_POSITION,
|
||||
@@ -346,3 +352,38 @@ export const useWorkflowInit = () => {
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
export const useWorkflowReadOnly = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
|
||||
const getWorkflowReadOnly = useCallback(() => {
|
||||
return workflowStore.getState().workflowRunningData?.result.status === WorkflowRunningStatus.Running
|
||||
}, [workflowStore])
|
||||
|
||||
return {
|
||||
workflowReadOnly: workflowRunningData?.result.status === WorkflowRunningStatus.Running,
|
||||
getWorkflowReadOnly,
|
||||
}
|
||||
}
|
||||
export const useNodesReadOnly = () => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const isRestoring = useStore(s => s.isRestoring)
|
||||
|
||||
const getNodesReadOnly = useCallback(() => {
|
||||
const {
|
||||
workflowRunningData,
|
||||
historyWorkflowData,
|
||||
isRestoring,
|
||||
} = workflowStore.getState()
|
||||
|
||||
return workflowRunningData || historyWorkflowData || isRestoring
|
||||
}, [workflowStore])
|
||||
|
||||
return {
|
||||
nodesReadOnly: workflowRunningData || historyWorkflowData || isRestoring,
|
||||
getNodesReadOnly,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,15 @@ import type {
|
||||
Edge,
|
||||
Node,
|
||||
} from './types'
|
||||
import { WorkflowRunningStatus } from './types'
|
||||
import { WorkflowContextProvider } from './context'
|
||||
import {
|
||||
useEdgesInteractions,
|
||||
useNodesInteractions,
|
||||
useNodesReadOnly,
|
||||
useNodesSyncDraft,
|
||||
useWorkflow,
|
||||
useWorkflowInit,
|
||||
useWorkflowReadOnly,
|
||||
} from './hooks'
|
||||
import Header from './header'
|
||||
import CustomNode from './nodes'
|
||||
@@ -62,9 +63,10 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
viewport,
|
||||
}) => {
|
||||
const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const nodeAnimation = useStore(s => s.nodeAnimation)
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const { workflowReadOnly } = useWorkflowReadOnly()
|
||||
const { nodesReadOnly } = useNodesReadOnly()
|
||||
|
||||
useEffect(() => {
|
||||
setAutoFreeze(false)
|
||||
@@ -106,7 +108,7 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
id='workflow-container'
|
||||
className={`
|
||||
relative w-full min-w-[960px] h-full bg-[#F0F2F7]
|
||||
${runningStatus === WorkflowRunningStatus.Running && 'workflow-panel-animation'}
|
||||
${workflowReadOnly && 'workflow-panel-animation'}
|
||||
${nodeAnimation && 'workflow-node-animation'}
|
||||
`}
|
||||
>
|
||||
@@ -138,14 +140,14 @@ const Workflow: FC<WorkflowProps> = memo(({
|
||||
defaultViewport={viewport}
|
||||
multiSelectionKeyCode={null}
|
||||
deleteKeyCode={null}
|
||||
nodesDraggable={!runningStatus}
|
||||
nodesConnectable={!runningStatus}
|
||||
nodesFocusable={!runningStatus}
|
||||
edgesFocusable={!runningStatus}
|
||||
panOnDrag={runningStatus !== WorkflowRunningStatus.Running}
|
||||
zoomOnPinch={runningStatus !== WorkflowRunningStatus.Running}
|
||||
zoomOnScroll={runningStatus !== WorkflowRunningStatus.Running}
|
||||
zoomOnDoubleClick={runningStatus !== WorkflowRunningStatus.Running}
|
||||
nodesDraggable={!nodesReadOnly}
|
||||
nodesConnectable={!nodesReadOnly}
|
||||
nodesFocusable={!nodesReadOnly}
|
||||
edgesFocusable={!nodesReadOnly}
|
||||
panOnDrag={!workflowReadOnly}
|
||||
zoomOnPinch={!workflowReadOnly}
|
||||
zoomOnScroll={!workflowReadOnly}
|
||||
zoomOnDoubleClick={!workflowReadOnly}
|
||||
isValidConnection={isValidConnection}
|
||||
>
|
||||
<Background
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { MiniMap } from 'reactflow'
|
||||
import { useWorkflow } from '../hooks'
|
||||
import { useStore } from '../store'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
useWorkflow,
|
||||
} from '../hooks'
|
||||
import ZoomInOut from './zoom-in-out'
|
||||
import { OrganizeGrid } from '@/app/components/base/icons/src/vender/line/layout'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
|
||||
const Operator = () => {
|
||||
const { t } = useTranslation()
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const { handleLayout } = useWorkflow()
|
||||
const {
|
||||
nodesReadOnly,
|
||||
getNodesReadOnly,
|
||||
} = useNodesReadOnly()
|
||||
|
||||
const goLayout = () => {
|
||||
if (runningStatus)
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
handleLayout()
|
||||
}
|
||||
@@ -35,7 +40,7 @@ const Operator = () => {
|
||||
<div
|
||||
className={`
|
||||
ml-[1px] flex items-center justify-center w-8 h-8 cursor-pointer hover:bg-black/5 rounded-lg
|
||||
${runningStatus && '!cursor-not-allowed opacity-50'}
|
||||
${nodesReadOnly && '!cursor-not-allowed opacity-50'}
|
||||
`}
|
||||
onClick={goLayout}
|
||||
>
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
useReactFlow,
|
||||
useViewport,
|
||||
} from 'reactflow'
|
||||
import { useNodesSyncDraft } from '../hooks'
|
||||
import { useStore } from '../store'
|
||||
import { WorkflowRunningStatus } from '../types'
|
||||
import {
|
||||
useNodesSyncDraft,
|
||||
useWorkflowReadOnly,
|
||||
} from '../hooks'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
@@ -32,7 +33,10 @@ const ZoomInOut: FC = () => {
|
||||
const { zoom } = useViewport()
|
||||
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
|
||||
const [open, setOpen] = useState(false)
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const {
|
||||
workflowReadOnly,
|
||||
getWorkflowReadOnly,
|
||||
} = useWorkflowReadOnly()
|
||||
|
||||
const ZOOM_IN_OUT_OPTIONS = [
|
||||
[
|
||||
@@ -64,8 +68,9 @@ const ZoomInOut: FC = () => {
|
||||
]
|
||||
|
||||
const handleZoom = (type: string) => {
|
||||
if (runningStatus === WorkflowRunningStatus.Running)
|
||||
if (workflowReadOnly)
|
||||
return
|
||||
|
||||
if (type === 'in')
|
||||
zoomIn()
|
||||
|
||||
@@ -85,10 +90,11 @@ const ZoomInOut: FC = () => {
|
||||
}
|
||||
|
||||
const handleTrigger = useCallback(() => {
|
||||
if (runningStatus === WorkflowRunningStatus.Running)
|
||||
if (getWorkflowReadOnly())
|
||||
return
|
||||
|
||||
setOpen(v => !v)
|
||||
}, [runningStatus])
|
||||
}, [getWorkflowReadOnly])
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
@@ -104,7 +110,7 @@ const ZoomInOut: FC = () => {
|
||||
<div className={`
|
||||
flex items-center px-2 h-8 cursor-pointer text-[13px] hover:bg-gray-50 rounded-lg
|
||||
${open && 'bg-gray-50'}
|
||||
${runningStatus === WorkflowRunningStatus.Running && '!cursor-not-allowed opacity-50'}
|
||||
${workflowReadOnly && '!cursor-not-allowed opacity-50'}
|
||||
`}>
|
||||
<SearchLg className='mr-1 w-4 h-4' />
|
||||
<div className='w-[34px]'>{parseFloat(`${zoom * 100}`).toFixed(0)}%</div>
|
||||
|
||||
@@ -17,7 +17,8 @@ const ChatRecord = () => {
|
||||
const [fetched, setFetched] = useState(false)
|
||||
const [chatList, setChatList] = useState([])
|
||||
const appDetail = useAppStore(s => s.appDetail)
|
||||
const currentConversationID = useStore(s => s.currentConversationID)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const currentConversationID = historyWorkflowData?.conversation_id
|
||||
|
||||
const chatMessageList = useMemo(() => {
|
||||
const res: ChatItem[] = []
|
||||
|
||||
@@ -33,7 +33,6 @@ const ChatWrapper = forwardRef<ChatWrapperRefType>((_, ref) => {
|
||||
const workflowStore = useWorkflowStore()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const inputs = useStore(s => s.inputs)
|
||||
const workflowRunId = useStore(s => s.workflowRunId)
|
||||
const { handleStopRun } = useWorkflowRun()
|
||||
const features = featuresStore!.getState().features
|
||||
|
||||
@@ -84,8 +83,8 @@ const ChatWrapper = forwardRef<ChatWrapperRefType>((_, ref) => {
|
||||
|
||||
const doStop = useCallback(() => {
|
||||
handleStop()
|
||||
handleStopRun()
|
||||
}, [handleStop, handleStopRun])
|
||||
handleStopRun(workflowStore.getState().workflowRunningData?.task_id || '')
|
||||
}, [handleStop, handleStopRun, workflowStore])
|
||||
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
@@ -96,7 +95,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType>((_, ref) => {
|
||||
return (
|
||||
<Chat
|
||||
config={config as any}
|
||||
chatList={chatList.map(item => ({ ...item, workflow_run_id: workflowRunId }))}
|
||||
chatList={chatList}
|
||||
isResponding={isResponding}
|
||||
chatContainerclassName='px-4'
|
||||
chatContainerInnerClassName='pt-6'
|
||||
|
||||
@@ -13,30 +13,37 @@ import DebugAndPreview from './debug-and-preview'
|
||||
import RunHistory from './run-history'
|
||||
import Record from './record'
|
||||
import InputsPanel from './inputs-panel'
|
||||
import WorkflowPreview from './workflow-preview'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import MessageLogModal from '@/app/components/base/message-log-modal'
|
||||
|
||||
const Panel: FC = () => {
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const isChatMode = useIsChatMode()
|
||||
const runningStatus = useStore(s => s.runningStatus)
|
||||
const workflowRunId = useStore(s => s.workflowRunId)
|
||||
const selectedNode = nodes.find(node => node.data.selected)
|
||||
const showRunHistory = useStore(state => state.showRunHistory)
|
||||
const showInputsPanel = useStore(s => s.showInputsPanel)
|
||||
const currentConversationID = useStore(s => s.currentConversationID)
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal } = useAppStore()
|
||||
const {
|
||||
showWorkflowInfoPanel,
|
||||
showNodePanel,
|
||||
showDebugAndPreviewPanel,
|
||||
showWorkflowPreview,
|
||||
} = useMemo(() => {
|
||||
return {
|
||||
showWorkflowInfoPanel: !selectedNode && !runningStatus,
|
||||
showNodePanel: !!selectedNode && !runningStatus,
|
||||
showDebugAndPreviewPanel: isChatMode && runningStatus && !currentConversationID,
|
||||
showWorkflowInfoPanel: !selectedNode && !workflowRunningData && !historyWorkflowData,
|
||||
showNodePanel: !!selectedNode && !workflowRunningData && !historyWorkflowData,
|
||||
showDebugAndPreviewPanel: isChatMode && workflowRunningData && !historyWorkflowData,
|
||||
showWorkflowPreview: !isChatMode && workflowRunningData && !historyWorkflowData,
|
||||
}
|
||||
}, [selectedNode, isChatMode, runningStatus, currentConversationID])
|
||||
}, [
|
||||
selectedNode,
|
||||
isChatMode,
|
||||
workflowRunningData,
|
||||
historyWorkflowData,
|
||||
])
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -45,11 +52,6 @@ const Panel: FC = () => {
|
||||
${(showRunHistory || showDebugAndPreviewPanel) && '!pr-0'}
|
||||
`}
|
||||
>
|
||||
{
|
||||
showInputsPanel && (
|
||||
<InputsPanel />
|
||||
)
|
||||
}
|
||||
{
|
||||
showMessageLogModal && (
|
||||
<MessageLogModal
|
||||
@@ -64,13 +66,23 @@ const Panel: FC = () => {
|
||||
)
|
||||
}
|
||||
{
|
||||
runningStatus && !isChatMode && workflowRunId && (
|
||||
historyWorkflowData && (
|
||||
<Record />
|
||||
)
|
||||
}
|
||||
{
|
||||
runningStatus && isChatMode && showRunHistory && currentConversationID && (
|
||||
<Record />
|
||||
showDebugAndPreviewPanel && (
|
||||
<DebugAndPreview />
|
||||
)
|
||||
}
|
||||
{
|
||||
showInputsPanel && (
|
||||
<InputsPanel />
|
||||
)
|
||||
}
|
||||
{
|
||||
showWorkflowPreview && (
|
||||
<WorkflowPreview />
|
||||
)
|
||||
}
|
||||
{
|
||||
@@ -83,11 +95,6 @@ const Panel: FC = () => {
|
||||
<WorkflowInfo />
|
||||
)
|
||||
}
|
||||
{
|
||||
showDebugAndPreviewPanel && (
|
||||
<DebugAndPreview />
|
||||
)
|
||||
}
|
||||
{
|
||||
showRunHistory && (
|
||||
<RunHistory />
|
||||
|
||||
@@ -6,8 +6,7 @@ import ChatRecord from './chat-record'
|
||||
|
||||
const Record = () => {
|
||||
const isChatMode = useIsChatMode()
|
||||
const currentSequenceNumber = useStore(s => s.currentSequenceNumber)
|
||||
const workflowRunId = useStore(s => s.workflowRunId)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
@@ -15,12 +14,12 @@ const Record = () => {
|
||||
${isChatMode ? 'w-[320px]' : 'w-[400px]'}
|
||||
`}>
|
||||
<div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
|
||||
{`Test ${isChatMode ? 'Chat' : 'Run'}#${currentSequenceNumber}`}
|
||||
{`Test ${isChatMode ? 'Chat' : 'Run'}#${historyWorkflowData?.sequence_number}`}
|
||||
</div>
|
||||
{
|
||||
isChatMode
|
||||
? <ChatRecord />
|
||||
: <Run runID={workflowRunId} />
|
||||
: <Run runID={historyWorkflowData?.id || ''} />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import { CheckCircle, XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
import {
|
||||
useStore as useRunHistoryStore,
|
||||
useStore,
|
||||
useWorkflowStore,
|
||||
} from '@/app/components/workflow/store'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
@@ -25,7 +25,7 @@ const RunHistory = () => {
|
||||
const { formatTimeFromNow } = useWorkflow()
|
||||
const { handleBackupDraft } = useWorkflowRun()
|
||||
const workflowStore = useWorkflowStore()
|
||||
const workflowRunId = useRunHistoryStore(state => state.workflowRunId)
|
||||
const historyWorkflowData = useStore(s => s.historyWorkflowData)
|
||||
const { data: runList, isLoading: runListLoading } = useSWR((appDetail && !isChatMode) ? `/apps/${appDetail.id}/workflow-runs` : null, fetchWorkflowRunHistory)
|
||||
|
||||
const { data: chatList, isLoading: chatListLoading } = useSWR((appDetail && isChatMode) ? `/apps/${appDetail.id}/advanced-chat/workflow-runs` : null, fetcChatRunHistory)
|
||||
@@ -45,8 +45,7 @@ const RunHistory = () => {
|
||||
onClick={() => {
|
||||
workflowStore.setState({
|
||||
showRunHistory: false,
|
||||
workflowRunId: '',
|
||||
currentConversationID: '',
|
||||
historyWorkflowData: undefined,
|
||||
})
|
||||
setCurrentLogItem()
|
||||
setShowMessageLogModal(false)
|
||||
@@ -69,14 +68,11 @@ const RunHistory = () => {
|
||||
key={item.id}
|
||||
className={cn(
|
||||
'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer',
|
||||
item.id === workflowRunId && 'bg-primary-50',
|
||||
item.id === historyWorkflowData?.id && 'bg-primary-50',
|
||||
)}
|
||||
onClick={() => {
|
||||
workflowStore.setState({
|
||||
currentSequenceNumber: item.sequence_number,
|
||||
workflowRunId: item.id,
|
||||
currentConversationID: item.conversation_id,
|
||||
runningStatus: item.status as WorkflowRunningStatus,
|
||||
historyWorkflowData: item,
|
||||
})
|
||||
handleBackupDraft()
|
||||
}}
|
||||
@@ -95,7 +91,7 @@ const RunHistory = () => {
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center text-[13px] font-medium leading-[18px]',
|
||||
item.id === workflowRunId && 'text-primary-600',
|
||||
item.id === historyWorkflowData?.id && 'text-primary-600',
|
||||
)}
|
||||
>
|
||||
{`Test ${isChatMode ? 'Chat' : 'Run'}#${item.sequence_number}`}
|
||||
|
||||
67
web/app/components/workflow/panel/workflow-preview.tsx
Normal file
67
web/app/components/workflow/panel/workflow-preview.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import {
|
||||
memo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ResultPanel from '../run/result-panel'
|
||||
import TracingPanel from '../run/tracing-panel'
|
||||
import { useStore } from '../store'
|
||||
|
||||
const WorkflowPreview = () => {
|
||||
const { t } = useTranslation()
|
||||
const [currentTab, setCurrentTab] = useState<string>('TRACING')
|
||||
const workflowRunningData = useStore(s => s.workflowRunningData)
|
||||
|
||||
const switchTab = async (tab: string) => {
|
||||
setCurrentTab(tab)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
flex flex-col w-[420px] h-full rounded-2xl border-[0.5px] border-gray-200 shadow-xl bg-white
|
||||
`}>
|
||||
<div className='flex items-center justify-between p-4 pb-1 text-base font-semibold text-gray-900'>
|
||||
Test Run#${workflowRunningData?.result.sequence_number}
|
||||
</div>
|
||||
<div>
|
||||
<div className='shrink-0 flex items-center px-4 border-b-[0.5px] border-[rgba(0,0,0,0.05)]'>
|
||||
<div
|
||||
className={cn(
|
||||
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
|
||||
currentTab === 'RESULT' && '!border-[rgb(21,94,239)] text-gray-700',
|
||||
)}
|
||||
onClick={() => switchTab('RESULT')}
|
||||
>{t('runLog.result')}</div>
|
||||
<div
|
||||
className={cn(
|
||||
'mr-6 py-3 border-b-2 border-transparent text-[13px] font-semibold leading-[18px] text-gray-400 cursor-pointer',
|
||||
currentTab === 'TRACING' && '!border-[rgb(21,94,239)] text-gray-700',
|
||||
)}
|
||||
onClick={() => switchTab('TRACING')}
|
||||
>{t('runLog.tracing')}</div>
|
||||
</div>
|
||||
{currentTab === 'RESULT' && (
|
||||
<ResultPanel
|
||||
inputs={workflowRunningData?.result?.inputs}
|
||||
outputs={workflowRunningData?.result?.outputs}
|
||||
status={workflowRunningData?.result?.status || ''}
|
||||
error={workflowRunningData?.result?.error}
|
||||
elapsed_time={workflowRunningData?.result?.elapsed_time}
|
||||
total_tokens={workflowRunningData?.result?.total_tokens}
|
||||
created_at={workflowRunningData?.result?.created_at}
|
||||
created_by={''}
|
||||
steps={workflowRunningData?.result?.total_steps}
|
||||
/>
|
||||
)}
|
||||
{currentTab === 'TRACING' && (
|
||||
<TracingPanel
|
||||
list={workflowRunningData?.tracing || []}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(WorkflowPreview)
|
||||
@@ -13,7 +13,7 @@ import type { NodeTracing } from '@/types/workflow'
|
||||
import type { WorkflowRunDetailResponse } from '@/models/log'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
|
||||
type RunProps = {
|
||||
export type RunProps = {
|
||||
activeTab?: 'RESULT' | 'TRACING'
|
||||
runID: string
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ const NodePanel: FC<Props> = ({ nodeInfo, collapsed = true }) => {
|
||||
<BlockIcon className='shrink-0 mr-2' type={nodeInfo.node_type} />
|
||||
<div className='grow text-gray-700 text-[13px] leading-[16px] font-semibold truncate' title={nodeInfo.title}>{nodeInfo.title}</div>
|
||||
{nodeInfo.status !== 'running' && (
|
||||
<div className='shrink-0 text-gray-500 text-xs leading-[18px]'>{`${getTime(nodeInfo.elapsed_time)} · ${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens`}</div>
|
||||
<div className='shrink-0 text-gray-500 text-xs leading-[18px]'>{`${getTime(nodeInfo.elapsed_time || 0)} · ${getTokenCount(nodeInfo.execution_metadata?.total_tokens || 0)} tokens`}</div>
|
||||
)}
|
||||
{nodeInfo.status === 'succeeded' && (
|
||||
<CheckCircle className='shrink-0 ml-2 w-3.5 h-3.5 text-[#12B76A]' />
|
||||
|
||||
@@ -16,75 +16,63 @@ import type {
|
||||
} from './block-selector/types'
|
||||
import type {
|
||||
Edge,
|
||||
HistoryWorkflowData,
|
||||
Node,
|
||||
RunFile,
|
||||
WorkflowRunningStatus,
|
||||
WorkflowRunningData,
|
||||
} from './types'
|
||||
import { WorkflowContext } from './context'
|
||||
|
||||
type State = {
|
||||
taskId: string
|
||||
currentSequenceNumber: number
|
||||
workflowRunId: string
|
||||
currentConversationID: string
|
||||
type Shape = {
|
||||
workflowRunningData?: WorkflowRunningData
|
||||
setWorkflowRunningData: (workflowData: WorkflowRunningData) => void
|
||||
historyWorkflowData?: HistoryWorkflowData
|
||||
setHistoryWorkflowData: (historyWorkflowData: HistoryWorkflowData) => void
|
||||
showRunHistory: boolean
|
||||
setShowRunHistory: (showRunHistory: boolean) => void
|
||||
showFeaturesPanel: boolean
|
||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||
helpLineHorizontal?: HelpLineHorizontalPosition
|
||||
setHelpLineHorizontal: (helpLineHorizontal?: HelpLineHorizontalPosition) => void
|
||||
helpLineVertical?: HelpLineVerticalPosition
|
||||
setHelpLineVertical: (helpLineVertical?: HelpLineVerticalPosition) => void
|
||||
toolsets: CollectionWithExpanded[]
|
||||
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
||||
toolsMap: ToolsMap
|
||||
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
||||
draftUpdatedAt: number
|
||||
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
||||
publishedAt: number
|
||||
runningStatus?: WorkflowRunningStatus
|
||||
setPublishedAt: (publishedAt: number) => void
|
||||
showInputsPanel: boolean
|
||||
setShowInputsPanel: (showInputsPanel: boolean) => void
|
||||
inputs: Record<string, string>
|
||||
setInputs: (inputs: Record<string, string>) => void
|
||||
files: RunFile[]
|
||||
setFiles: (files: RunFile[]) => void
|
||||
backupDraft?: {
|
||||
nodes: Node[]
|
||||
edges: Edge[]
|
||||
viewport: Viewport
|
||||
}
|
||||
setBackupDraft: (backupDraft?: Shape['backupDraft']) => void
|
||||
notInitialWorkflow: boolean
|
||||
nodesDefaultConfigs: Record<string, any>
|
||||
nodeAnimation: boolean
|
||||
isRestoring: boolean
|
||||
}
|
||||
|
||||
type Action = {
|
||||
setTaskId: (taskId: string) => void
|
||||
setCurrentSequenceNumber: (currentSequenceNumber: number) => void
|
||||
setWorkflowRunId: (workflowRunId: string) => void
|
||||
setCurrentConversationID: (currentConversationID: string) => void
|
||||
setShowRunHistory: (showRunHistory: boolean) => void
|
||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||
setHelpLineHorizontal: (helpLineHorizontal?: HelpLineHorizontalPosition) => void
|
||||
setHelpLineVertical: (helpLineVertical?: HelpLineVerticalPosition) => void
|
||||
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
||||
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
||||
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
||||
setPublishedAt: (publishedAt: number) => void
|
||||
setRunningStatus: (runningStatus?: WorkflowRunningStatus) => void
|
||||
setShowInputsPanel: (showInputsPanel: boolean) => void
|
||||
setInputs: (inputs: Record<string, string>) => void
|
||||
setFiles: (files: RunFile[]) => void
|
||||
setBackupDraft: (backupDraft?: State['backupDraft']) => void
|
||||
setNotInitialWorkflow: (notInitialWorkflow: boolean) => void
|
||||
nodesDefaultConfigs: Record<string, any>
|
||||
setNodesDefaultConfigs: (nodesDefaultConfigs: Record<string, any>) => void
|
||||
nodeAnimation: boolean
|
||||
setNodeAnimation: (nodeAnimation: boolean) => void
|
||||
isRestoring: boolean
|
||||
setIsRestoring: (isRestoring: boolean) => void
|
||||
debouncedSyncWorkflowDraft: (fn: () => void) => void
|
||||
}
|
||||
|
||||
export const createWorkflowStore = () => {
|
||||
return create<State & Action>(set => ({
|
||||
taskId: '',
|
||||
setTaskId: taskId => set(() => ({ taskId })),
|
||||
currentSequenceNumber: 0,
|
||||
setCurrentSequenceNumber: currentSequenceNumber => set(() => ({ currentSequenceNumber })),
|
||||
workflowRunId: '',
|
||||
setWorkflowRunId: workflowRunId => set(() => ({ workflowRunId })),
|
||||
currentConversationID: '',
|
||||
setCurrentConversationID: currentConversationID => set(() => ({ currentConversationID })),
|
||||
return create<Shape>(set => ({
|
||||
workflowData: undefined,
|
||||
setWorkflowRunningData: workflowRunningData => set(() => ({ workflowRunningData })),
|
||||
historyWorkflowData: undefined,
|
||||
setHistoryWorkflowData: historyWorkflowData => set(() => ({ historyWorkflowData })),
|
||||
showRunHistory: false,
|
||||
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
|
||||
showFeaturesPanel: false,
|
||||
@@ -101,8 +89,6 @@ export const createWorkflowStore = () => {
|
||||
setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt: draftUpdatedAt ? draftUpdatedAt * 1000 : 0 })),
|
||||
publishedAt: 0,
|
||||
setPublishedAt: publishedAt => set(() => ({ publishedAt: publishedAt ? publishedAt * 1000 : 0 })),
|
||||
runningStatus: undefined,
|
||||
setRunningStatus: runningStatus => set(() => ({ runningStatus })),
|
||||
showInputsPanel: false,
|
||||
setShowInputsPanel: showInputsPanel => set(() => ({ showInputsPanel })),
|
||||
inputs: {},
|
||||
@@ -125,7 +111,7 @@ export const createWorkflowStore = () => {
|
||||
}))
|
||||
}
|
||||
|
||||
export function useStore<T>(selector: (state: State & Action) => T): T {
|
||||
export function useStore<T>(selector: (state: Shape) => T): T {
|
||||
const store = useContext(WorkflowContext)
|
||||
if (!store)
|
||||
throw new Error('Missing WorkflowContext.Provider in the tree')
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
import type { TransferMethod } from '@/types/app'
|
||||
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
|
||||
import type { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||
import type { NodeTracing } from '@/types/workflow'
|
||||
|
||||
export enum BlockEnum {
|
||||
Start = 'start',
|
||||
@@ -219,3 +220,34 @@ export type RunFile = {
|
||||
url?: string
|
||||
upload_file_id?: string
|
||||
}
|
||||
|
||||
export type WorkflowRunningData = {
|
||||
task_id?: string
|
||||
message_id?: string
|
||||
conversation_id?: string
|
||||
result: {
|
||||
sequence_number?: number
|
||||
workflow_id?: string
|
||||
inputs?: string
|
||||
process_data?: string
|
||||
outputs?: string
|
||||
status: string
|
||||
error?: string
|
||||
elapsed_time?: number
|
||||
total_tokens?: number
|
||||
created_at?: number
|
||||
created_by?: string
|
||||
finished_at?: number
|
||||
steps?: number
|
||||
showSteps?: boolean
|
||||
total_steps?: number
|
||||
}
|
||||
tracing?: NodeTracing[]
|
||||
}
|
||||
|
||||
export type HistoryWorkflowData = {
|
||||
id: string
|
||||
sequence_number: number
|
||||
status: string
|
||||
conversation_id?: string
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user