zxhlyh 45deaee762
feat: workflow new nodes (#4683)
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: Patryk Garstecki <patryk20120@yahoo.pl>
Co-authored-by: Sebastian.W <thiner@gmail.com>
Co-authored-by: 呆萌闷油瓶 <253605712@qq.com>
Co-authored-by: takatost <takatost@users.noreply.github.com>
Co-authored-by: rechardwang <wh_goodjob@163.com>
Co-authored-by: Nite Knite <nkCoding@gmail.com>
Co-authored-by: Chenhe Gu <guchenhe@gmail.com>
Co-authored-by: Joshua <138381132+joshua20231026@users.noreply.github.com>
Co-authored-by: Weaxs <459312872@qq.com>
Co-authored-by: Ikko Eltociear Ashimine <eltociear@gmail.com>
Co-authored-by: leejoo0 <81673835+leejoo0@users.noreply.github.com>
Co-authored-by: JzoNg <jzongcode@gmail.com>
Co-authored-by: sino <sino2322@gmail.com>
Co-authored-by: Vikey Chen <vikeytk@gmail.com>
Co-authored-by: wanghl <Wang-HL@users.noreply.github.com>
Co-authored-by: Haolin Wang-汪皓临 <haolin.wang@atlaslovestravel.com>
Co-authored-by: Zixuan Cheng <61724187+Theysua@users.noreply.github.com>
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
Co-authored-by: Bowen Liang <bowenliang@apache.org>
Co-authored-by: Bowen Liang <liangbowen@gf.com.cn>
Co-authored-by: fanghongtai <42790567+fanghongtai@users.noreply.github.com>
Co-authored-by: wxfanghongtai <wxfanghongtai@gf.com.cn>
Co-authored-by: Matri <qjp@bithuman.io>
Co-authored-by: Benjamin <benjaminx@gmail.com>
2024-05-27 21:57:08 +08:00

244 lines
7.0 KiB
TypeScript

import { useCallback } from 'react'
import {
useEdges,
useNodes,
useStoreApi,
} from 'reactflow'
import { useTranslation } from 'react-i18next'
import { uniqBy } from 'lodash-es'
import produce from 'immer'
import {
useIsChatMode,
useNodeDataUpdate,
useWorkflow,
} from '../../hooks'
import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../../utils'
import type {
Edge,
Node,
ValueSelector,
Var,
} from '../../types'
import { useWorkflowStore } from '../../store'
import type {
VarGroupItem,
VariableAssignerNodeType,
} from './types'
import { toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
export const useVariableAssigner = () => {
const store = useStoreApi()
const workflowStore = useWorkflowStore()
const { handleNodeDataUpdate } = useNodeDataUpdate()
const handleAssignVariableValueChange = useCallback((nodeId: string, value: ValueSelector, varDetail: Var, groupId?: string) => {
const { getNodes } = store.getState()
const nodes = getNodes()
const node: Node<VariableAssignerNodeType> = nodes.find(node => node.id === nodeId)!
let payload
if (groupId && groupId !== 'target') {
payload = {
advanced_settings: {
...node.data.advanced_settings,
groups: node.data.advanced_settings?.groups.map((group: VarGroupItem & { groupId: string }) => {
if (group.groupId === groupId && !group.variables.some(item => item.join('.') === (value as ValueSelector).join('.'))) {
return {
...group,
variables: [...group.variables, value],
output_type: varDetail.type,
}
}
return group
}),
},
}
}
else {
if (node.data.variables.some(item => item.join('.') === (value as ValueSelector).join('.')))
return
payload = {
variables: [...node.data.variables, value],
output_type: varDetail.type,
}
}
handleNodeDataUpdate({
id: nodeId,
data: payload,
})
}, [store, handleNodeDataUpdate])
const handleAddVariableInAddVariablePopupWithPosition = useCallback((
nodeId: string,
variableAssignerNodeId: string,
variableAssignerNodeHandleId: string,
value: ValueSelector,
varDetail: Var,
) => {
const {
getNodes,
setNodes,
} = store.getState()
const {
setShowAssignVariablePopup,
} = workflowStore.getState()
const newNodes = produce(getNodes(), (draft) => {
draft.forEach((node) => {
if (node.id === nodeId || node.id === variableAssignerNodeId) {
node.data = {
...node.data,
_showAddVariablePopup: false,
_holdAddVariablePopup: false,
}
}
})
})
setNodes(newNodes)
setShowAssignVariablePopup(undefined)
handleAssignVariableValueChange(variableAssignerNodeId, value, varDetail, variableAssignerNodeHandleId)
}, [store, workflowStore, handleAssignVariableValueChange])
const handleRemoveEdges = useCallback((nodeId: string, enabled: boolean) => {
const {
getNodes,
setNodes,
edges,
setEdges,
} = store.getState()
const nodes = getNodes()
const needDeleteEdges = edges.filter(edge => edge.target === nodeId)
if (!needDeleteEdges.length)
return
const currentNode = nodes.find(node => node.id === nodeId)!
const groups = currentNode.data.advanced_settings?.groups || []
let shouldKeepEdges: Edge[] = []
if (enabled) {
shouldKeepEdges = edges.filter((edge) => {
return edge.target === nodeId && edge.targetHandle === 'target'
}).map((edge) => {
return {
...edge,
targetHandle: groups[0].groupId,
}
})
}
else {
shouldKeepEdges = edges.filter((edge) => {
return edge.target === nodeId && edge.targetHandle === groups[0].groupId
}).map((edge) => {
return {
...edge,
targetHandle: 'target',
}
})
}
const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
[
...needDeleteEdges.map((needDeleteEdge) => {
return {
type: 'remove',
edge: needDeleteEdge,
}
}),
...shouldKeepEdges.map((shouldKeepEdge) => {
return {
type: 'add',
edge: shouldKeepEdge,
}
}),
],
nodes,
)
const newNodes = produce(nodes, (draft) => {
draft.forEach((node) => {
if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
node.data = {
...node.data,
...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
}
}
})
})
setNodes(newNodes)
const newEdges = produce(edges, (draft) => {
draft = draft.filter(edge => edge.target !== nodeId)
draft.push(...shouldKeepEdges)
return draft
})
setEdges(newEdges)
}, [store])
const handleGroupItemMouseEnter = useCallback((groupId: string) => {
const {
setHoveringAssignVariableGroupId,
} = workflowStore.getState()
setHoveringAssignVariableGroupId(groupId)
}, [workflowStore])
const handleGroupItemMouseLeave = useCallback(() => {
const {
connectingNodePayload,
setHoveringAssignVariableGroupId,
} = workflowStore.getState()
if (connectingNodePayload)
setHoveringAssignVariableGroupId(undefined)
}, [workflowStore])
return {
handleAddVariableInAddVariablePopupWithPosition,
handleRemoveEdges,
handleGroupItemMouseEnter,
handleGroupItemMouseLeave,
handleAssignVariableValueChange,
}
}
export const useGetAvailableVars = () => {
const { t } = useTranslation()
const nodes: Node[] = useNodes()
const edges: Edge[] = useEdges()
const { getBeforeNodesInSameBranch } = useWorkflow()
const isChatMode = useIsChatMode()
const getAvailableVars = useCallback((nodeId: string, handleId: string, filterVar: (v: Var) => boolean) => {
const availableNodes: Node[] = []
const currentNode = nodes.find(node => node.id === nodeId)!
if (!currentNode)
return []
const parentNode = nodes.find(node => node.id === currentNode.parentId)
const connectedEdges = edges.filter(edge => edge.target === nodeId && edge.targetHandle === handleId)
if (parentNode && !connectedEdges.length) {
const beforeNodes = getBeforeNodesInSameBranch(parentNode.id)
availableNodes.push(...beforeNodes)
}
else {
connectedEdges.forEach((connectedEdge) => {
const beforeNodes = getBeforeNodesInSameBranch(connectedEdge.source)
const connectedNode = nodes.find(node => node.id === connectedEdge.source)!
availableNodes.push(connectedNode, ...beforeNodes)
})
}
return toNodeAvailableVars({
parentNode,
t,
beforeNodes: uniqBy(availableNodes, 'id').filter(node => node.id !== nodeId),
isChatMode,
filterVar,
})
}, [nodes, edges, t, isChatMode, getBeforeNodesInSameBranch])
return getAvailableVars
}