fix: workflow restore (#3711)

This commit is contained in:
zxhlyh 2024-04-23 17:02:23 +08:00 committed by GitHub
parent 96160837d2
commit 83caffe000
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 654 additions and 318 deletions

View File

@ -402,3 +402,5 @@ export const TOOL_OUTPUT_STRUCT: Var[] = [
type: VarType.arrayFile,
},
]
export const WORKFLOW_DATA_UPDATE = 'WORKFLOW_DATA_UPDATE'

View File

@ -73,6 +73,7 @@ const Header: FC = () => {
const handleRestore = useCallback(() => {
workflowStore.setState({ isRestoring: false })
workflowStore.setState({ backupDraft: undefined })
handleSyncWorkflowDraft(true)
}, [handleSyncWorkflowDraft, workflowStore])

View File

@ -19,6 +19,7 @@ import type {
Viewport,
} from 'reactflow'
import {
changeNodesAndEdgesId,
getLayoutByDagre,
initialEdges,
initialNodes,
@ -39,6 +40,7 @@ import {
import {
AUTO_LAYOUT_OFFSET,
SUPPORT_OUTPUT_VARS_NODE,
WORKFLOW_DATA_UPDATE,
} from '../constants'
import { findUsedVarNodes, getNodeOutputVars, updateNodeVars } from '../nodes/_base/components/variable/utils'
import { useNodesExtraData } from './use-nodes-data'
@ -56,6 +58,8 @@ import {
fetchAllCustomTools,
} from '@/service/tools'
import I18n from '@/context/i18n'
import { useEventEmitterContextContext } from '@/context/event-emitter'
export const useIsChatMode = () => {
const appDetail = useAppStore(s => s.appDetail)
@ -69,6 +73,7 @@ export const useWorkflow = () => {
const workflowStore = useWorkflowStore()
const nodesExtraData = useNodesExtraData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { eventEmitter } = useEventEmitterContextContext()
const handleLayout = useCallback(async () => {
workflowStore.setState({ nodeAnimation: true })
@ -314,15 +319,21 @@ export const useWorkflow = () => {
}, [locale])
const renderTreeFromRecord = useCallback((nodes: Node[], edges: Edge[], viewport?: Viewport) => {
const { setNodes } = store.getState()
const { setViewport, setEdges } = reactflow
const { setViewport } = reactflow
setNodes(initialNodes(nodes, edges))
setEdges(initialEdges(edges, nodes))
const [newNodes, newEdges] = changeNodesAndEdgesId(nodes, edges)
eventEmitter?.emit({
type: WORKFLOW_DATA_UPDATE,
payload: {
nodes: initialNodes(newNodes, newEdges),
edges: initialEdges(newEdges, newNodes),
},
} as any)
if (viewport)
setViewport(viewport)
}, [store, reactflow])
}, [reactflow, eventEmitter])
const getNode = useCallback((nodeId?: string) => {
const { getNodes } = store.getState()

View File

@ -14,6 +14,8 @@ import {
import ReactFlow, {
Background,
ReactFlowProvider,
useEdgesState,
useNodesState,
useOnViewportChange,
} from 'reactflow'
import type { Viewport } from 'reactflow'
@ -46,9 +48,11 @@ import {
initialEdges,
initialNodes,
} from './utils'
import { WORKFLOW_DATA_UPDATE } from './constants'
import Loading from '@/app/components/base/loading'
import { FeaturesProvider } from '@/app/components/base/features'
import type { Features as FeaturesData } from '@/app/components/base/features/types'
import { useEventEmitterContextContext } from '@/context/event-emitter'
const nodeTypes = {
custom: CustomNode,
@ -63,10 +67,12 @@ type WorkflowProps = {
viewport?: Viewport
}
const Workflow: FC<WorkflowProps> = memo(({
nodes,
edges,
nodes: originalNodes,
edges: originalEdges,
viewport,
}) => {
const [nodes, setNodes] = useNodesState(originalNodes)
const [edges, setEdges] = useEdgesState(originalEdges)
const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
const nodeAnimation = useStore(s => s.nodeAnimation)
const {
@ -76,6 +82,15 @@ const Workflow: FC<WorkflowProps> = memo(({
const { workflowReadOnly } = useWorkflowReadOnly()
const { nodesReadOnly } = useNodesReadOnly()
const { eventEmitter } = useEventEmitterContextContext()
eventEmitter?.useSubscription((v: any) => {
if (v.type === WORKFLOW_DATA_UPDATE) {
setNodes(v.payload.nodes)
setEdges(v.payload.edges)
}
})
useEffect(() => {
setAutoFreeze(false)

View File

@ -16,6 +16,7 @@ import type { ToolDefaultValue } from '../../../block-selector/types'
import {
useNodesExtraData,
useNodesInteractions,
useNodesReadOnly,
} from '../../../hooks'
import { useStore } from '../../../store'
@ -35,6 +36,7 @@ export const NodeTargetHandle = memo(({
const [open, setOpen] = useState(false)
const { handleNodeAdd } = useNodesInteractions()
const nodesExtraData = useNodesExtraData()
const { getNodesReadOnly } = useNodesReadOnly()
const connected = data._connectedTargetHandleIds?.includes(handleId)
const availablePrevNodes = nodesExtraData[data.type].availablePrevNodes
const isConnectable = !!availablePrevNodes.length
@ -78,7 +80,7 @@ export const NodeTargetHandle = memo(({
onClick={handleHandleClick}
>
{
!connected && isConnectable && !data._isInvalidConnection && (
!connected && isConnectable && !data._isInvalidConnection && !getNodesReadOnly() && (
<BlockSelector
open={open}
onOpenChange={handleOpenChange}
@ -113,6 +115,7 @@ export const NodeSourceHandle = memo(({
const [open, setOpen] = useState(false)
const { handleNodeAdd } = useNodesInteractions()
const nodesExtraData = useNodesExtraData()
const { getNodesReadOnly } = useNodesReadOnly()
const availableNextNodes = nodesExtraData[data.type].availableNextNodes
const isConnectable = !!availableNextNodes.length
const connected = data._connectedSourceHandleIds?.includes(handleId)
@ -159,7 +162,7 @@ export const NodeSourceHandle = memo(({
onClick={handleHandleClick}
>
{
!connected && isConnectable && !data._isInvalidConnection && (
!connected && isConnectable && !data._isInvalidConnection && !getNodesReadOnly() && (
<BlockSelector
open={open}
onOpenChange={handleOpenChange}

View File

@ -58,7 +58,7 @@ const BaseNode: FC<BaseNodeProps> = ({
`}
>
{
data.type !== BlockEnum.VariableAssigner && !data._runningStatus && !nodesReadOnly && (
data.type !== BlockEnum.VariableAssigner && !data._runningStatus && (
<NodeTargetHandle
id={id}
data={data}
@ -68,7 +68,7 @@ const BaseNode: FC<BaseNodeProps> = ({
)
}
{
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._runningStatus && !nodesReadOnly && (
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && !data._runningStatus && (
<NodeSourceHandle
id={id}
data={data}

View File

@ -4,6 +4,7 @@ import {
getOutgoers,
} from 'reactflow'
import dagre from 'dagre'
import { v4 as uuid4 } from 'uuid'
import {
cloneDeep,
uniqBy,
@ -331,3 +332,28 @@ export const getToolCheckParams = (
language,
}
}
export const changeNodesAndEdgesId = (nodes: Node[], edges: Edge[]) => {
const idMap = nodes.reduce((acc, node) => {
acc[node.id] = uuid4()
return acc
}, {} as Record<string, string>)
const newNodes = nodes.map((node) => {
return {
...node,
id: idMap[node.id],
}
})
const newEdges = edges.map((edge) => {
return {
...edge,
source: idMap[edge.source],
target: idMap[edge.target],
}
})
return [newNodes, newEdges] as [Node[], Edge[]]
}

View File

@ -83,6 +83,7 @@
"sortablejs": "^1.15.0",
"swr": "^2.1.0",
"use-context-selector": "^1.4.1",
"uuid": "^9.0.1",
"zustand": "^4.5.1"
},
"devDependencies": {
@ -104,6 +105,7 @@
"@types/react-window-infinite-loader": "^1.0.6",
"@types/recordrtc": "^5.6.11",
"@types/sortablejs": "^1.15.1",
"@types/uuid": "^9.0.8",
"autoprefixer": "^10.4.14",
"cross-env": "^7.0.3",
"eslint": "^8.36.0",

File diff suppressed because it is too large Load Diff