available nodes

This commit is contained in:
StyleZhang 2024-03-15 14:40:21 +08:00
parent 5adf94bd7d
commit 2203d9a138
8 changed files with 84 additions and 14 deletions

View File

@ -255,7 +255,7 @@ export const RETRIEVAL_OUTPUT_STRUCT = `{
export const SUPPORT_OUTPUT_VARS_NODE = [
BlockEnum.Start, BlockEnum.LLM, BlockEnum.KnowledgeRetrieval, BlockEnum.Code, BlockEnum.TemplateTransform,
BlockEnum.QuestionClassifier, BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner,
BlockEnum.HttpRequest, BlockEnum.Tool, BlockEnum.VariableAssigner,
]
const USAGE = {

View File

@ -1,9 +1,11 @@
import { useCallback, useRef } from 'react'
import produce from 'immer'
import type {
HandleType,
NodeDragHandler,
NodeMouseHandler,
OnConnect,
OnConnectStart,
} from 'reactflow'
import {
getConnectedEdges,
@ -25,16 +27,21 @@ import {
generateNewNode,
getNodesConnectedSourceOrTargetHandleIdsMap,
} from '../utils'
import { useNodesInitialData } from './use-nodes-data'
import {
useNodesExtraData,
useNodesInitialData,
} from './use-nodes-data'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useWorkflow } from './use-workflow'
export const useNodesInteractions = () => {
const store = useStoreApi()
const nodesInitialData = useNodesInitialData()
const nodesExtraData = useNodesExtraData()
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const { getAfterNodesInSameBranch } = useWorkflow()
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) => {
const {
@ -177,9 +184,27 @@ export const useNodesInteractions = () => {
return
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const nodes = getNodes()
if (connectingNodeRef.current && connectingNodeRef.current.nodeId !== node.id) {
const connectingNode: Node = nodes.find(n => n.id === connectingNodeRef.current!.nodeId)!
const handleType = connectingNodeRef.current.handleType
const currentNodeIndex = nodes.findIndex(n => n.id === node.id)
const availablePrevNodes = nodesExtraData[connectingNode.data.type].availablePrevNodes
const availableNextNodes = nodesExtraData[connectingNode.data.type].availableNextNodes
const availableNodes = handleType === 'source' ? availableNextNodes : [...availablePrevNodes, BlockEnum.Start]
const newNodes = produce(nodes, (draft) => {
if (!availableNodes.includes(draft[currentNodeIndex].data.type))
draft[currentNodeIndex].data._isInvalidConnection = true
})
setNodes(newNodes)
}
const newEdges = produce(edges, (draft) => {
const connectedEdges = getConnectedEdges([node], edges)
@ -190,7 +215,7 @@ export const useNodesInteractions = () => {
})
})
setEdges(newEdges)
}, [store])
}, [store, nodesExtraData])
const handleNodeLeave = useCallback<NodeMouseHandler>(() => {
const { runningStatus } = useStore.getState()
@ -199,9 +224,17 @@ export const useNodesInteractions = () => {
return
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
node.data._isInvalidConnection = false
})
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
draft.forEach((edge) => {
edge.data = { ...edge.data, _connectedNodeIsHovering: false }
@ -307,6 +340,19 @@ export const useNodesInteractions = () => {
handleSyncWorkflowDraft()
}, [store, handleSyncWorkflowDraft])
const handleNodeConnectStart = useCallback<OnConnectStart>((_, { nodeId, handleType }) => {
if (nodeId && handleType) {
connectingNodeRef.current = {
nodeId,
handleType,
}
}
}, [])
const handleNodeConnectEnd = useCallback(() => {
connectingNodeRef.current = null
}, [])
const handleNodeDelete = useCallback((nodeId: string) => {
const { runningStatus } = useStore.getState()
@ -596,6 +642,8 @@ export const useNodesInteractions = () => {
handleNodeSelect,
handleNodeClick,
handleNodeConnect,
handleNodeConnectStart,
handleNodeConnectEnd,
handleNodeDelete,
handleNodeChange,
handleNodeAdd,

View File

@ -9,6 +9,7 @@ import {
getOutgoers,
useStoreApi,
} from 'reactflow'
import type { Connection } from 'reactflow'
import type { ToolsMap } from '../block-selector/types'
import {
generateNewNode,
@ -21,7 +22,10 @@ import {
START_INITIAL_POSITION,
SUPPORT_OUTPUT_VARS_NODE,
} from '../constants'
import { useNodesInitialData } from './use-nodes-data'
import {
useNodesExtraData,
useNodesInitialData,
} from './use-nodes-data'
import { useStore as useAppStore } from '@/app/components/app/store'
import {
fetchNodesDefaultConfigs,
@ -38,6 +42,7 @@ export const useIsChatMode = () => {
export const useWorkflow = () => {
const store = useStoreApi()
const nodesExtraData = useNodesExtraData()
const handleLayout = useCallback(async () => {
const {
@ -169,9 +174,24 @@ export const useWorkflow = () => {
return list
}, [store])
const isValidConnection = useCallback(() => {
const isValidConnection = useCallback(({ source, target }: Connection) => {
const { getNodes } = store.getState()
const nodes = getNodes()
const sourceNode: Node = nodes.find(node => node.id === source)!
const targetNode: Node = nodes.find(node => node.id === target)!
if (sourceNode && targetNode) {
const sourceNodeAvailableNextNodes = nodesExtraData[sourceNode.data.type].availableNextNodes
const targetNodeAvailablePrevNodes = [...nodesExtraData[targetNode.data.type].availablePrevNodes, BlockEnum.Start]
if (!sourceNodeAvailableNextNodes.includes(targetNode.data.type))
return false
if (!targetNodeAvailablePrevNodes.includes(sourceNode.data.type))
return false
}
return true
}, [])
}, [store, nodesExtraData])
return {
handleLayout,

View File

@ -10,7 +10,6 @@ import ReactFlow, {
Background,
ReactFlowProvider,
useOnViewportChange,
useStoreApi,
} from 'reactflow'
import type { Viewport } from 'reactflow'
import 'reactflow/dist/style.css'
@ -62,7 +61,6 @@ const Workflow: FC<WorkflowProps> = memo(({
const showFeaturesPanel = useStore(state => state.showFeaturesPanel)
const runningStatus = useStore(s => s.runningStatus)
const { handleSyncWorkflowDraft } = useNodesSyncDraft()
const store = useStoreApi()
useEffect(() => {
setAutoFreeze(false)
@ -80,6 +78,8 @@ const Workflow: FC<WorkflowProps> = memo(({
handleNodeLeave,
handleNodeClick,
handleNodeConnect,
handleNodeConnectStart,
handleNodeConnectEnd,
} = useNodesInteractions()
const {
handleEdgeEnter,
@ -108,6 +108,7 @@ const Workflow: FC<WorkflowProps> = memo(({
}
<HelpLine />
<ReactFlow
className='workflow-inner'
nodeTypes={nodeTypes}
edgeTypes={edgeTypes}
nodes={nodes}
@ -119,6 +120,8 @@ const Workflow: FC<WorkflowProps> = memo(({
onNodeMouseLeave={handleNodeLeave}
onNodeClick={handleNodeClick}
onConnect={handleNodeConnect}
onConnectStart={handleNodeConnectStart}
onConnectEnd={handleNodeConnectEnd}
onEdgeMouseEnter={handleEdgeEnter}
onEdgeMouseLeave={handleEdgeLeave}
onEdgesChange={handleEdgesChange}

View File

@ -77,7 +77,7 @@ export const NodeTargetHandle = memo(({
onClick={handleHandleClick}
>
{
!connected && isConnectable && (
!connected && isConnectable && !data._isInvalidConnection && (
<BlockSelector
open={open}
onOpenChange={handleOpenChange}
@ -156,7 +156,7 @@ export const NodeSourceHandle = memo(({
onClick={handleHandleClick}
>
{
!connected && isConnectable && (
!connected && isConnectable && !data._isInvalidConnection && (
<BlockSelector
open={open}
onOpenChange={handleOpenChange}

View File

@ -36,7 +36,7 @@ const BaseNode: FC<BaseNodeProps> = ({
<div
className={`
flex border-[2px] rounded-2xl
${(data.selected && !data._runningStatus) ? 'border-primary-600' : 'border-transparent'}
${(data.selected && !data._runningStatus && !data._isInvalidConnection) ? 'border-primary-600' : 'border-transparent'}
`}
>
<div
@ -48,6 +48,7 @@ const BaseNode: FC<BaseNodeProps> = ({
${data._runningStatus === NodeRunningStatus.Succeeded && '!border-[#12B76A]'}
${data._runningStatus === NodeRunningStatus.Failed && '!border-[#F04438]'}
${data._runningStatus === NodeRunningStatus.Waiting && 'opacity-70'}
${data._isInvalidConnection && '!border-[#F04438]'}
`}
>
{

View File

@ -1,3 +0,0 @@
.react-flow__node {
transition: transform 0.1s ease-in-out;
}

View File

@ -26,6 +26,7 @@ export type Branch = {
}
export type CommonNodeType<T = {}> = {
_isInvalidConnection?: boolean
_connectedSourceHandleIds?: string[]
_connectedTargetHandleIds?: string[]
_targetBranches?: Branch[]