hooks.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. import { useCallback } from 'react'
  2. import {
  3. useEdges,
  4. useNodes,
  5. useStoreApi,
  6. } from 'reactflow'
  7. import { useTranslation } from 'react-i18next'
  8. import { uniqBy } from 'lodash-es'
  9. import produce from 'immer'
  10. import {
  11. useIsChatMode,
  12. useNodeDataUpdate,
  13. useWorkflow,
  14. } from '../../hooks'
  15. import { getNodesConnectedSourceOrTargetHandleIdsMap } from '../../utils'
  16. import type {
  17. Edge,
  18. Node,
  19. ValueSelector,
  20. Var,
  21. } from '../../types'
  22. import { useWorkflowStore } from '../../store'
  23. import type {
  24. VarGroupItem,
  25. VariableAssignerNodeType,
  26. } from './types'
  27. import { toNodeAvailableVars } from '@/app/components/workflow/nodes/_base/components/variable/utils'
  28. export const useVariableAssigner = () => {
  29. const store = useStoreApi()
  30. const workflowStore = useWorkflowStore()
  31. const { handleNodeDataUpdate } = useNodeDataUpdate()
  32. const handleAssignVariableValueChange = useCallback((nodeId: string, value: ValueSelector, varDetail: Var, groupId?: string) => {
  33. const { getNodes } = store.getState()
  34. const nodes = getNodes()
  35. const node: Node<VariableAssignerNodeType> = nodes.find(node => node.id === nodeId)!
  36. let payload
  37. if (groupId && groupId !== 'target') {
  38. payload = {
  39. advanced_settings: {
  40. ...node.data.advanced_settings,
  41. groups: node.data.advanced_settings?.groups.map((group: VarGroupItem & { groupId: string }) => {
  42. if (group.groupId === groupId && !group.variables.some(item => item.join('.') === (value as ValueSelector).join('.'))) {
  43. return {
  44. ...group,
  45. variables: [...group.variables, value],
  46. output_type: varDetail.type,
  47. }
  48. }
  49. return group
  50. }),
  51. },
  52. }
  53. }
  54. else {
  55. if (node.data.variables.some(item => item.join('.') === (value as ValueSelector).join('.')))
  56. return
  57. payload = {
  58. variables: [...node.data.variables, value],
  59. output_type: varDetail.type,
  60. }
  61. }
  62. handleNodeDataUpdate({
  63. id: nodeId,
  64. data: payload,
  65. })
  66. }, [store, handleNodeDataUpdate])
  67. const handleAddVariableInAddVariablePopupWithPosition = useCallback((
  68. nodeId: string,
  69. variableAssignerNodeId: string,
  70. variableAssignerNodeHandleId: string,
  71. value: ValueSelector,
  72. varDetail: Var,
  73. ) => {
  74. const {
  75. getNodes,
  76. setNodes,
  77. } = store.getState()
  78. const {
  79. setShowAssignVariablePopup,
  80. } = workflowStore.getState()
  81. const newNodes = produce(getNodes(), (draft) => {
  82. draft.forEach((node) => {
  83. if (node.id === nodeId || node.id === variableAssignerNodeId) {
  84. node.data = {
  85. ...node.data,
  86. _showAddVariablePopup: false,
  87. _holdAddVariablePopup: false,
  88. }
  89. }
  90. })
  91. })
  92. setNodes(newNodes)
  93. setShowAssignVariablePopup(undefined)
  94. handleAssignVariableValueChange(variableAssignerNodeId, value, varDetail, variableAssignerNodeHandleId)
  95. }, [store, workflowStore, handleAssignVariableValueChange])
  96. const handleRemoveEdges = useCallback((nodeId: string, enabled: boolean) => {
  97. const {
  98. getNodes,
  99. setNodes,
  100. edges,
  101. setEdges,
  102. } = store.getState()
  103. const nodes = getNodes()
  104. const needDeleteEdges = edges.filter(edge => edge.target === nodeId)
  105. if (!needDeleteEdges.length)
  106. return
  107. const currentNode = nodes.find(node => node.id === nodeId)!
  108. const groups = currentNode.data.advanced_settings?.groups || []
  109. let shouldKeepEdges: Edge[] = []
  110. if (enabled) {
  111. shouldKeepEdges = edges.filter((edge) => {
  112. return edge.target === nodeId && edge.targetHandle === 'target'
  113. }).map((edge) => {
  114. return {
  115. ...edge,
  116. targetHandle: groups[0].groupId,
  117. }
  118. })
  119. }
  120. else {
  121. shouldKeepEdges = edges.filter((edge) => {
  122. return edge.target === nodeId && edge.targetHandle === groups[0].groupId
  123. }).map((edge) => {
  124. return {
  125. ...edge,
  126. targetHandle: 'target',
  127. }
  128. })
  129. }
  130. const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
  131. [
  132. ...needDeleteEdges.map((needDeleteEdge) => {
  133. return {
  134. type: 'remove',
  135. edge: needDeleteEdge,
  136. }
  137. }),
  138. ...shouldKeepEdges.map((shouldKeepEdge) => {
  139. return {
  140. type: 'add',
  141. edge: shouldKeepEdge,
  142. }
  143. }),
  144. ],
  145. nodes,
  146. )
  147. const newNodes = produce(nodes, (draft) => {
  148. draft.forEach((node) => {
  149. if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
  150. node.data = {
  151. ...node.data,
  152. ...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
  153. }
  154. }
  155. })
  156. })
  157. setNodes(newNodes)
  158. const newEdges = produce(edges, (draft) => {
  159. draft = draft.filter(edge => edge.target !== nodeId)
  160. draft.push(...shouldKeepEdges)
  161. return draft
  162. })
  163. setEdges(newEdges)
  164. }, [store])
  165. const handleGroupItemMouseEnter = useCallback((groupId: string) => {
  166. const {
  167. setHoveringAssignVariableGroupId,
  168. } = workflowStore.getState()
  169. setHoveringAssignVariableGroupId(groupId)
  170. }, [workflowStore])
  171. const handleGroupItemMouseLeave = useCallback(() => {
  172. const {
  173. connectingNodePayload,
  174. setHoveringAssignVariableGroupId,
  175. } = workflowStore.getState()
  176. if (connectingNodePayload)
  177. setHoveringAssignVariableGroupId(undefined)
  178. }, [workflowStore])
  179. return {
  180. handleAddVariableInAddVariablePopupWithPosition,
  181. handleRemoveEdges,
  182. handleGroupItemMouseEnter,
  183. handleGroupItemMouseLeave,
  184. handleAssignVariableValueChange,
  185. }
  186. }
  187. export const useGetAvailableVars = () => {
  188. const { t } = useTranslation()
  189. const nodes: Node[] = useNodes()
  190. const edges: Edge[] = useEdges()
  191. const { getBeforeNodesInSameBranch } = useWorkflow()
  192. const isChatMode = useIsChatMode()
  193. const getAvailableVars = useCallback((nodeId: string, handleId: string, filterVar: (v: Var) => boolean) => {
  194. const availableNodes: Node[] = []
  195. const currentNode = nodes.find(node => node.id === nodeId)!
  196. if (!currentNode)
  197. return []
  198. const parentNode = nodes.find(node => node.id === currentNode.parentId)
  199. const connectedEdges = edges.filter(edge => edge.target === nodeId && edge.targetHandle === handleId)
  200. if (parentNode && !connectedEdges.length) {
  201. const beforeNodes = getBeforeNodesInSameBranch(parentNode.id)
  202. availableNodes.push(...beforeNodes)
  203. }
  204. else {
  205. connectedEdges.forEach((connectedEdge) => {
  206. const beforeNodes = getBeforeNodesInSameBranch(connectedEdge.source)
  207. const connectedNode = nodes.find(node => node.id === connectedEdge.source)!
  208. availableNodes.push(connectedNode, ...beforeNodes)
  209. })
  210. }
  211. return toNodeAvailableVars({
  212. parentNode,
  213. t,
  214. beforeNodes: uniqBy(availableNodes, 'id').filter(node => node.id !== nodeId),
  215. isChatMode,
  216. filterVar,
  217. })
  218. }, [nodes, edges, t, isChatMode, getBeforeNodesInSameBranch])
  219. return getAvailableVars
  220. }