mirror of https://github.com/langgenius/dify.git
feat: implement edge source handle change functionality and enhance node interactions for HumanInput node
This commit is contained in:
parent
0e2b59d661
commit
44a688cb81
|
|
@ -151,11 +151,65 @@ export const useEdgesInteractions = () => {
|
|||
setEdges(newEdges)
|
||||
}, [store, getNodesReadOnly])
|
||||
|
||||
const handleEdgeSourceHandleChange = useCallback((nodeId: string, oldHandleId: string, newHandleId: string) => {
|
||||
if (getNodesReadOnly())
|
||||
return
|
||||
|
||||
const { getNodes, setNodes, edges, setEdges } = store.getState()
|
||||
const nodes = getNodes()
|
||||
|
||||
// Find edges connected to the old handle
|
||||
const affectedEdges = edges.filter(
|
||||
edge => edge.source === nodeId && edge.sourceHandle === oldHandleId,
|
||||
)
|
||||
|
||||
if (affectedEdges.length === 0)
|
||||
return
|
||||
|
||||
// Update node metadata: remove old handle, add new handle
|
||||
const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
|
||||
[
|
||||
...affectedEdges.map(edge => ({ type: 'remove', edge })),
|
||||
...affectedEdges.map(edge => ({
|
||||
type: 'add',
|
||||
edge: { ...edge, sourceHandle: newHandleId },
|
||||
})),
|
||||
],
|
||||
nodes,
|
||||
)
|
||||
|
||||
const newNodes = produce(nodes, (draft: Node[]) => {
|
||||
draft.forEach((node) => {
|
||||
if (nodesConnectedSourceOrTargetHandleIdsMap[node.id]) {
|
||||
node.data = {
|
||||
...node.data,
|
||||
...nodesConnectedSourceOrTargetHandleIdsMap[node.id],
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
setNodes(newNodes)
|
||||
|
||||
// Update edges to use new sourceHandle and regenerate edge IDs
|
||||
const newEdges = produce(edges, (draft) => {
|
||||
draft.forEach((edge) => {
|
||||
if (edge.source === nodeId && edge.sourceHandle === oldHandleId) {
|
||||
edge.sourceHandle = newHandleId
|
||||
edge.id = `${edge.source}-${newHandleId}-${edge.target}-${edge.targetHandle}`
|
||||
}
|
||||
})
|
||||
})
|
||||
setEdges(newEdges)
|
||||
handleSyncWorkflowDraft()
|
||||
saveStateToHistory(WorkflowHistoryEvent.EdgeSourceHandleChange)
|
||||
}, [getNodesReadOnly, store, handleSyncWorkflowDraft, saveStateToHistory])
|
||||
|
||||
return {
|
||||
handleEdgeEnter,
|
||||
handleEdgeLeave,
|
||||
handleEdgeDeleteByDeleteBranch,
|
||||
handleEdgeDelete,
|
||||
handleEdgesChange,
|
||||
handleEdgeSourceHandleChange,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -299,6 +299,7 @@ export const useNodesInteractions = () => {
|
|||
|| connectingNode.data.type === BlockEnum.VariableAggregator)
|
||||
&& node.data.type !== BlockEnum.IfElse
|
||||
&& node.data.type !== BlockEnum.QuestionClassifier
|
||||
&& node.data.type !== BlockEnum.HumanInput
|
||||
) {
|
||||
n.data._isEntering = true
|
||||
}
|
||||
|
|
@ -1017,6 +1018,7 @@ export const useNodesInteractions = () => {
|
|||
if (
|
||||
nodeType !== BlockEnum.IfElse
|
||||
&& nodeType !== BlockEnum.QuestionClassifier
|
||||
&& nodeType !== BlockEnum.HumanInput
|
||||
) {
|
||||
newNode.data._connectedSourceHandleIds = [sourceHandle]
|
||||
}
|
||||
|
|
@ -1053,6 +1055,7 @@ export const useNodesInteractions = () => {
|
|||
if (
|
||||
nodeType !== BlockEnum.IfElse
|
||||
&& nodeType !== BlockEnum.QuestionClassifier
|
||||
&& nodeType !== BlockEnum.HumanInput
|
||||
&& nodeType !== BlockEnum.LoopEnd
|
||||
) {
|
||||
newEdge = {
|
||||
|
|
@ -1244,6 +1247,7 @@ export const useNodesInteractions = () => {
|
|||
if (
|
||||
nodeType !== BlockEnum.IfElse
|
||||
&& nodeType !== BlockEnum.QuestionClassifier
|
||||
&& nodeType !== BlockEnum.HumanInput
|
||||
&& nodeType !== BlockEnum.LoopEnd
|
||||
) {
|
||||
newNextEdge = {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export const WorkflowHistoryEvent = {
|
|||
NodeDelete: 'NodeDelete',
|
||||
EdgeDelete: 'EdgeDelete',
|
||||
EdgeDeleteByDeleteBranch: 'EdgeDeleteByDeleteBranch',
|
||||
EdgeSourceHandleChange: 'EdgeSourceHandleChange',
|
||||
NodeAdd: 'NodeAdd',
|
||||
NodeResize: 'NodeResize',
|
||||
NoteAdd: 'NoteAdd',
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ export const useWorkflowNodeFinished = () => {
|
|||
|
||||
if (data.node_type === BlockEnum.QuestionClassifier)
|
||||
currentNode.data._runningBranchId = data?.outputs?.class_id
|
||||
// todo: add human-input node support
|
||||
}
|
||||
})
|
||||
setNodes(newNodes)
|
||||
|
|
|
|||
|
|
@ -1309,6 +1309,7 @@ const replaceOldVarInText = (
|
|||
)
|
||||
}
|
||||
|
||||
// todo: add human-input node support
|
||||
export const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
||||
const { data } = node
|
||||
const { type } = data
|
||||
|
|
@ -1505,6 +1506,7 @@ export const getNodeUsedVars = (node: Node): ValueSelector[] => {
|
|||
}
|
||||
|
||||
// can be used in iteration node
|
||||
// todo: add human-input node
|
||||
export const getNodeUsedVarPassToServerKey = (
|
||||
node: Node,
|
||||
valueSelector: ValueSelector,
|
||||
|
|
@ -1615,6 +1617,7 @@ export const findUsedVarNodes = (
|
|||
return res
|
||||
}
|
||||
|
||||
// todo: add human-input node
|
||||
export const updateNodeVars = (
|
||||
oldNode: Node,
|
||||
oldVarSelector: ValueSelector,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,20 @@
|
|||
import type { DeliveryMethod, HumanInputNodeType, UserAction } from '../types'
|
||||
import { produce } from 'immer'
|
||||
import { useState } from 'react'
|
||||
import { useUpdateNodeInternals } from 'reactflow'
|
||||
import {
|
||||
useNodesReadOnly,
|
||||
} from '@/app/components/workflow/hooks'
|
||||
import { useEdgesInteractions } from '@/app/components/workflow/hooks/use-edges-interactions'
|
||||
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
|
||||
import useFormContent from './use-form-content'
|
||||
|
||||
const useConfig = (id: string, payload: HumanInputNodeType) => {
|
||||
const updateNodeInternals = useUpdateNodeInternals()
|
||||
const { nodesReadOnly: readOnly } = useNodesReadOnly()
|
||||
const { inputs, setInputs } = useNodeCrud<HumanInputNodeType>(id, payload)
|
||||
const formContentHook = useFormContent(id, payload)
|
||||
const { handleEdgeDeleteByDeleteBranch, handleEdgeSourceHandleChange } = useEdgesInteractions()
|
||||
const [structuredOutputCollapsed, setStructuredOutputCollapsed] = useState(true)
|
||||
|
||||
const handleDeliveryMethodChange = (methods: DeliveryMethod[]) => {
|
||||
|
|
@ -36,6 +40,14 @@ const useConfig = (id: string, payload: HumanInputNodeType) => {
|
|||
...inputs,
|
||||
user_actions: newActions,
|
||||
})
|
||||
|
||||
// Update edges to use the new handle
|
||||
const oldAction = inputs.user_actions[index]
|
||||
|
||||
if (oldAction && oldAction.id !== updatedAction.id) {
|
||||
handleEdgeSourceHandleChange(id, oldAction.id, updatedAction.id)
|
||||
updateNodeInternals(id) // Update handles
|
||||
}
|
||||
}
|
||||
|
||||
const handleUserActionDelete = (actionId: string) => {
|
||||
|
|
@ -44,6 +56,8 @@ const useConfig = (id: string, payload: HumanInputNodeType) => {
|
|||
...inputs,
|
||||
user_actions: newActions,
|
||||
})
|
||||
// Delete edges connected to this action
|
||||
handleEdgeDeleteByDeleteBranch(id, actionId)
|
||||
}
|
||||
|
||||
const handleTimeoutChange = ({ timeout, unit }: { timeout: number, unit: 'hour' | 'day' }) => {
|
||||
|
|
|
|||
|
|
@ -389,6 +389,7 @@ export const getLayoutByDagre = async (originNodes: Node[], originEdges: Edge[])
|
|||
const edgeToPortMap = new Map<string, string>()
|
||||
|
||||
// Build nodes with ports for If/Else nodes
|
||||
// todo: add human-input node support
|
||||
nodes.forEach((node) => {
|
||||
if (node.data.type === BlockEnum.IfElse) {
|
||||
const portsResult = buildIfElseWithPorts(node, edges)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ const BaseCard = ({
|
|||
handleId="target"
|
||||
/>
|
||||
{
|
||||
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && (
|
||||
data.type !== BlockEnum.IfElse && data.type !== BlockEnum.QuestionClassifier && data.type !== BlockEnum.HumanInput && (
|
||||
<NodeSourceHandle
|
||||
id={id}
|
||||
data={data}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import IterationNode from './iteration/node'
|
|||
import LoopNode from './loop/node'
|
||||
import QuestionClassifierNode from './question-classifier/node'
|
||||
|
||||
// todo: add human-input node support
|
||||
export const NodeComponentMap: Record<string, any> = {
|
||||
[BlockEnum.QuestionClassifier]: QuestionClassifierNode,
|
||||
[BlockEnum.IfElse]: IfElseNode,
|
||||
|
|
|
|||
Loading…
Reference in New Issue