mirror of
https://github.com/farcasclaudiu/Flowise.git
synced 2026-06-28 21:00:58 +03:00
@@ -185,6 +185,72 @@ export const initNode = (nodeData, newNodeId) => {
|
|||||||
return nodeData
|
return nodeData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateOutdatedNodeData = (newComponentNodeData, existingComponentNodeData) => {
|
||||||
|
const initNewComponentNodeData = initNode(newComponentNodeData, existingComponentNodeData.id)
|
||||||
|
|
||||||
|
// Update credentials with existing credentials
|
||||||
|
if (existingComponentNodeData.credential) {
|
||||||
|
initNewComponentNodeData.credential = existingComponentNodeData.credential
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update inputs with existing inputs
|
||||||
|
if (existingComponentNodeData.inputs) {
|
||||||
|
for (const key in existingComponentNodeData.inputs) {
|
||||||
|
if (key in initNewComponentNodeData.inputs) {
|
||||||
|
initNewComponentNodeData.inputs[key] = existingComponentNodeData.inputs[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update outputs with existing outputs
|
||||||
|
if (existingComponentNodeData.outputs) {
|
||||||
|
for (const key in existingComponentNodeData.outputs) {
|
||||||
|
if (key in initNewComponentNodeData.outputs) {
|
||||||
|
initNewComponentNodeData.outputs[key] = existingComponentNodeData.outputs[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return initNewComponentNodeData
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateOutdatedNodeEdge = (newComponentNodeData, edges) => {
|
||||||
|
const removedEdges = []
|
||||||
|
for (const edge of edges) {
|
||||||
|
const targetNodeId = edge.targetHandle.split('-')[0]
|
||||||
|
const sourceNodeId = edge.sourceHandle.split('-')[0]
|
||||||
|
|
||||||
|
if (targetNodeId === newComponentNodeData.id) {
|
||||||
|
// Check if targetHandle is in inputParams or inputAnchors
|
||||||
|
const inputParam = newComponentNodeData.inputParams.find((param) => param.id === edge.targetHandle)
|
||||||
|
const inputAnchor = newComponentNodeData.inputAnchors.find((param) => param.id === edge.targetHandle)
|
||||||
|
|
||||||
|
if (!inputParam && !inputAnchor) {
|
||||||
|
removedEdges.push(edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sourceNodeId === newComponentNodeData.id) {
|
||||||
|
if (newComponentNodeData.outputAnchors?.length) {
|
||||||
|
for (const outputAnchor of newComponentNodeData.outputAnchors) {
|
||||||
|
const outputAnchorType = outputAnchor.type
|
||||||
|
if (outputAnchorType === 'options') {
|
||||||
|
if (!outputAnchor.options.find((outputOption) => outputOption.id === edge.sourceHandle)) {
|
||||||
|
removedEdges.push(edge)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (outputAnchor.id !== edge.sourceHandle) {
|
||||||
|
removedEdges.push(edge)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return removedEdges
|
||||||
|
}
|
||||||
|
|
||||||
export const isValidConnection = (connection, reactFlowInstance) => {
|
export const isValidConnection = (connection, reactFlowInstance) => {
|
||||||
const sourceHandle = connection.sourceHandle
|
const sourceHandle = connection.sourceHandle
|
||||||
const targetHandle = connection.targetHandle
|
const targetHandle = connection.targetHandle
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ const CanvasNode = ({ data }) => {
|
|||||||
componentNode?.deprecateMessage ??
|
componentNode?.deprecateMessage ??
|
||||||
'This node will be deprecated in the next release. Change to a new node tagged with NEW'
|
'This node will be deprecated in the next release. Change to a new node tagged with NEW'
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
setWarningMessage('')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [canvas.componentNodes, data.name, data.version])
|
}, [canvas.componentNodes, data.name, data.version])
|
||||||
@@ -241,8 +243,8 @@ const CanvasNode = ({ data }) => {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Divider />
|
<Divider />
|
||||||
{data.outputAnchors.map((outputAnchor, index) => (
|
{data.outputAnchors.map((outputAnchor) => (
|
||||||
<NodeOutputHandler key={index} outputAnchor={outputAnchor} data={data} />
|
<NodeOutputHandler key={JSON.stringify(data)} outputAnchor={outputAnchor} data={data} />
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</NodeTooltip>
|
</NodeTooltip>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
import { omit, cloneDeep } from 'lodash'
|
import { omit, cloneDeep } from 'lodash'
|
||||||
|
|
||||||
// material-ui
|
// material-ui
|
||||||
import { Toolbar, Box, AppBar, Button } from '@mui/material'
|
import { Toolbar, Box, AppBar, Button, Fab } from '@mui/material'
|
||||||
import { useTheme } from '@mui/material/styles'
|
import { useTheme } from '@mui/material/styles'
|
||||||
|
|
||||||
// project imports
|
// project imports
|
||||||
@@ -38,10 +38,17 @@ import useApi from '@/hooks/useApi'
|
|||||||
import useConfirm from '@/hooks/useConfirm'
|
import useConfirm from '@/hooks/useConfirm'
|
||||||
|
|
||||||
// icons
|
// icons
|
||||||
import { IconX } from '@tabler/icons'
|
import { IconX, IconRefreshAlert } from '@tabler/icons'
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
import { getUniqueNodeId, initNode, rearrangeToolsOrdering, getUpsertDetails } from '@/utils/genericHelper'
|
import {
|
||||||
|
getUniqueNodeId,
|
||||||
|
initNode,
|
||||||
|
rearrangeToolsOrdering,
|
||||||
|
getUpsertDetails,
|
||||||
|
updateOutdatedNodeData,
|
||||||
|
updateOutdatedNodeEdge
|
||||||
|
} from '@/utils/genericHelper'
|
||||||
import useNotifier from '@/utils/useNotifier'
|
import useNotifier from '@/utils/useNotifier'
|
||||||
|
|
||||||
// const
|
// const
|
||||||
@@ -84,6 +91,7 @@ const Canvas = () => {
|
|||||||
|
|
||||||
const [selectedNode, setSelectedNode] = useState(null)
|
const [selectedNode, setSelectedNode] = useState(null)
|
||||||
const [isUpsertButtonEnabled, setIsUpsertButtonEnabled] = useState(false)
|
const [isUpsertButtonEnabled, setIsUpsertButtonEnabled] = useState(false)
|
||||||
|
const [isSyncNodesButtonEnabled, setIsSyncNodesButtonEnabled] = useState(false)
|
||||||
|
|
||||||
const reactFlowWrapper = useRef(null)
|
const reactFlowWrapper = useRef(null)
|
||||||
|
|
||||||
@@ -305,6 +313,28 @@ const Canvas = () => {
|
|||||||
[reactFlowInstance]
|
[reactFlowInstance]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const syncNodes = () => {
|
||||||
|
const componentNodes = canvas.componentNodes
|
||||||
|
|
||||||
|
const cloneNodes = cloneDeep(nodes)
|
||||||
|
const cloneEdges = cloneDeep(edges)
|
||||||
|
let toBeRemovedEdges = []
|
||||||
|
|
||||||
|
for (let i = 0; i < cloneNodes.length; i++) {
|
||||||
|
const node = cloneNodes[i]
|
||||||
|
const componentNode = componentNodes.find((cn) => cn.name === node.data.name)
|
||||||
|
if (componentNode && componentNode.version > node.data.version) {
|
||||||
|
cloneNodes[i].data = updateOutdatedNodeData(componentNode, node.data)
|
||||||
|
toBeRemovedEdges.push(...updateOutdatedNodeEdge(cloneNodes[i].data, cloneEdges))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNodes(cloneNodes)
|
||||||
|
setEdges(cloneEdges.filter((edge) => !toBeRemovedEdges.includes(edge)))
|
||||||
|
setDirty()
|
||||||
|
setIsSyncNodesButtonEnabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
const saveChatflowSuccess = () => {
|
const saveChatflowSuccess = () => {
|
||||||
dispatch({ type: REMOVE_DIRTY })
|
dispatch({ type: REMOVE_DIRTY })
|
||||||
enqueueSnackbar({
|
enqueueSnackbar({
|
||||||
@@ -347,6 +377,21 @@ const Canvas = () => {
|
|||||||
else setIsUpsertButtonEnabled(false)
|
else setIsUpsertButtonEnabled(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkIfSyncNodesAvailable = (nodes) => {
|
||||||
|
const componentNodes = canvas.componentNodes
|
||||||
|
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
const node = nodes[i]
|
||||||
|
const componentNode = componentNodes.find((cn) => cn.name === node.data.name)
|
||||||
|
if (componentNode && componentNode.version > node.data.version) {
|
||||||
|
setIsSyncNodesButtonEnabled(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSyncNodesButtonEnabled(false)
|
||||||
|
}
|
||||||
|
|
||||||
// ==============================|| useEffect ||============================== //
|
// ==============================|| useEffect ||============================== //
|
||||||
|
|
||||||
// Get specific chatflow successful
|
// Get specific chatflow successful
|
||||||
@@ -416,11 +461,16 @@ const Canvas = () => {
|
|||||||
if (canvasDataStore.chatflow) {
|
if (canvasDataStore.chatflow) {
|
||||||
const flowData = canvasDataStore.chatflow.flowData ? JSON.parse(canvasDataStore.chatflow.flowData) : []
|
const flowData = canvasDataStore.chatflow.flowData ? JSON.parse(canvasDataStore.chatflow.flowData) : []
|
||||||
checkIfUpsertAvailable(flowData.nodes || [], flowData.edges || [])
|
checkIfUpsertAvailable(flowData.nodes || [], flowData.edges || [])
|
||||||
|
checkIfSyncNodesAvailable(flowData.nodes || [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [canvasDataStore.chatflow])
|
}, [canvasDataStore.chatflow])
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
setIsSyncNodesButtonEnabled(false)
|
||||||
|
setIsUpsertButtonEnabled(false)
|
||||||
if (chatflowId) {
|
if (chatflowId) {
|
||||||
getSpecificChatflowApi.request(chatflowId)
|
getSpecificChatflowApi.request(chatflowId)
|
||||||
} else {
|
} else {
|
||||||
@@ -532,6 +582,26 @@ const Canvas = () => {
|
|||||||
/>
|
/>
|
||||||
<Background color='#aaa' gap={16} />
|
<Background color='#aaa' gap={16} />
|
||||||
<AddNodes nodesData={getNodesApi.data} node={selectedNode} />
|
<AddNodes nodesData={getNodesApi.data} node={selectedNode} />
|
||||||
|
{isSyncNodesButtonEnabled && (
|
||||||
|
<Fab
|
||||||
|
sx={{
|
||||||
|
left: 40,
|
||||||
|
top: 20,
|
||||||
|
color: 'white',
|
||||||
|
background: 'orange',
|
||||||
|
'&:hover': {
|
||||||
|
background: 'orange',
|
||||||
|
backgroundImage: `linear-gradient(rgb(0 0 0/10%) 0 0)`
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size='small'
|
||||||
|
aria-label='sync'
|
||||||
|
title='Sync Nodes'
|
||||||
|
onClick={() => syncNodes()}
|
||||||
|
>
|
||||||
|
<IconRefreshAlert />
|
||||||
|
</Fab>
|
||||||
|
)}
|
||||||
{isUpsertButtonEnabled && <VectorStorePopUp chatflowid={chatflowId} />}
|
{isUpsertButtonEnabled && <VectorStorePopUp chatflowid={chatflowId} />}
|
||||||
<ChatPopUp chatflowid={chatflowId} />
|
<ChatPopUp chatflowid={chatflowId} />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
|
|||||||
Reference in New Issue
Block a user