use-add-node.ts
Overview
The use-add-node.ts file provides a collection of React hooks and utility functions designed for managing the creation and positioning of nodes within a graph-based user interface powered by @xyflow/react. It is primarily focused on adding new nodes (operators) to a flow canvas, positioning them according to specific logic, initializing their parameters, and managing their relationships through edges.
This file integrates tightly with a centralized graph state store (useGraphStore) and supports various operator types, each with distinct initial states and behaviors. It implements complex layout logic to prevent overlap of nodes and ensure intuitive graph structures, including support for hierarchical and iteration nodes, tool nodes, and special handling of agents and subagents.
Detailed Documentation
Imports and Dependencies
React & React hooks:
useCallback,useMemoThird-party libraries: human-id (human-readable unique IDs),
lodash.lowerFirst,i18nextfor localization.Graph components and types:
Node,Connection,Position,ReactFlowInstancefrom@xyflow/react.Local state and constants:
useGraphStorefor global graph state management, and various initial values and operator enums/constants from../constant.Utility functions:
generateNodeNamesWithIncreasingIndex, getNodeDragHandle for naming and UI handle logic.
Key Concepts and Types
Operator: Enum representing types of nodes/operators (e.g., Begin, Retrieval, Agent, Tool).
Position: Enum for node connection positioning (e.g., Left, Right, Bottom).
Node: Represents a graph node with properties such as ID, type, position, data, parent-child relationships.
Edges: Connections between nodes.
useGraphStore: Centralized store managing nodes, edges, and graph operations such as adding nodes/edges and updating nodes.
Functions and Hooks
isBottomSubAgent(type: string, position: Position): boolean
Utility function to identify if a node is a "bottom sub-agent", which influences initialization and resizing behavior.
Returns true if:
typeisAgentand position isBottom, ortypeisTool
Otherwise returns
false.
useInitializeOperatorParams()
Custom hook that returns a function to initialize operator parameters based on the operator type and position.
Returns:
initialFormValuesMap: A memoized map fromOperatortype to initial form values, some enriched with the current LLM model ID fetched viauseFetchModelId().initializeOperatorParams(operatorName: Operator, position: Position) => object: Function that returns the initial form values for a specific operator and position, adding default description and user prompt for bottom sub-agents.
Usage Example:
const { initializeOperatorParams } = useInitializeOperatorParams();
const initParams = initializeOperatorParams(Operator.Agent, Position.Bottom);
useGetNodeName()
Returns a function to get the localized display name for a given node type string.
Uses
i18nextto translate the keydataflow.<lowercaseType>.Example: For type
"Agent", returns the translation fordataflow.agent.
Usage Example:
const getNodeName = useGetNodeName();
const nodeName = getNodeName('Agent'); // localized "Agent" string
useCalculateNewlyChildPosition()
Provides a function to calculate the position for a newly added child node such that it does not overlap existing child nodes.
Returns:
calculateNewlyBackChildPosition(id?: string, sourceHandle?: string) => { x: number, y: number }
Implementation Details:
Finds all child nodes connected via edges from the parent node (
id).Determines the maximum Y-position among existing children.
Positions the new child node with a horizontal offset (+300) to the right and vertically below the lowest child (+150).
If no children exist, positions it relative to the parent node.
Usage Example:
const { calculateNewlyBackChildPosition } = useCalculateNewlyChildPosition();
const newPos = calculateNewlyBackChildPosition('parentNodeId', 'sourceHandleId');
useAddChildEdge()
Hook to add an edge connecting a parent node to a newly created child node when the position is Right.
Returns:
addChildEdge(position: Position, edge: Partial<Connection>) => void
Behavior:
Adds an edge only if:
The position is
Right.The edge has defined
source,target, andsourceHandle.
Usage Example:
const { addChildEdge } = useAddChildEdge();
addChildEdge(Position.Right, {
source: 'node1',
target: 'node2',
sourceHandle: 'handle1',
});
useAddToolNode()
Hook to add a "Tool" type node as a child of an "Agent" node.
Returns:
addToolNode(newNode: Node<any>, nodeId?: string) => boolean
Implementation Details:
Checks if the parent agent node already has a tool child node.
If yes, rejects adding a new tool node (returns
false).Otherwise, positions the new tool node relative to the agent node.
Adds the node and creates an edge from the agent node to the tool node.
useResizeIterationNode()
Hook to resize a parent iteration node dynamically when new child nodes are added, preventing overlaps.
Returns:
resizeIterationNode(type: string, position: Position, parentId?: string) => void
Behavior:
If the node is not a bottom sub-agent:
Finds the maximum X among child nodes.
If the max child's X plus a fixed offset (310) exceeds the parent's X position, the parent node's width is increased and its position adjusted horizontally.
useAddNode(reactFlowInstance?: ReactFlowInstance<any, any>)
The principal hook of the file, responsible for adding nodes of various types to the graph canvas.
Parameters:
reactFlowInstance(optional): React Flow canvas instance, used to convert screen coordinates to flow coordinates.
Returns:
addCanvasNode(type: string, params?: { nodeId?: string, position: Position, id?: string, isFromConnectionDrag?: boolean }) => (event?: CanvasMouseEvent) => string | undefinedAdds a node of the specified type to the canvas, optionally linked to a parent node (
nodeId), at a specified position.addNoteNode(e: CanvasMouseEvent): voidConvenience function to add a Note node at the mouse event position.
Implementation Highlights:
Calculates node position based on event coordinates or positioning rules.
Initializes node data including unique ID, type, label, localized name, and form data.
Supports special handling for
Iterationnodes by adding a nestedIterationStartnode.Special positioning logic for
Agentnodes at the bottom position.Delegates adding
Toolnodes touseAddToolNode.Adds child edges appropriately.
Resizes parent iteration nodes if necessary.
Usage Example:
const { addCanvasNode, addNoteNode } = useAddNode(reactFlowInstance);
// Add a retrieval node to the right of node with ID 'abc123'
const nodeId = addCanvasNode(Operator.Retrieval, { nodeId: 'abc123', position: Position.Right })();
// Add a note node on canvas click event
addNoteNode(mouseEvent);
Important Implementation Details and Algorithms
Node Positioning Logic: Carefully designed to avoid node overlap by calculating max positions of existing child nodes and placing new nodes offset from them.
Iteration Node Handling: When adding an iteration node, an iteration start sub-node is automatically created and linked.
Agent and Tool Node Relationships: Agents and their associated tools have specialized positioning and edge creation rules, including constraints on multiple tools.
Localization: Node names and default form descriptions/prompts are localized via
i18next.Dynamic Node Resizing: Parent iteration nodes dynamically resize when child nodes extend beyond their bounds, maintaining visual clarity.
Interaction With Other Parts of the System
Global Graph State (
useGraphStore): This file heavily depends on the centralized store for querying and mutating the graph structure, including nodes and edges.Constants and Initial Values: References numerous pre-defined initial form values and operator enums/constants from
../constant.Localization (
i18next): For translating node names and UI text.React Flow Instance: Uses
reactFlowInstanceto convert mouse coordinates to canvas coordinates.Utility Functions: Uses helpers for generating human-readable IDs and drag handle determination.
UI Components: The nodes added here are rendered elsewhere in the system using React Flow components.
Visual Diagram
classDiagram
class useAddNode {
+addCanvasNode(type: string, params?): (event?) => string | undefined
+addNoteNode(event)
}
class useInitializeOperatorParams {
+initializeOperatorParams(operatorName: Operator, position: Position): object
+initialFormValuesMap
}
class useGetNodeName {
+getNodeName(type: string): string
}
class useCalculateNewlyChildPosition {
+calculateNewlyBackChildPosition(id?: string, sourceHandle?: string): {x:number, y:number}
}
class useAddChildEdge {
+addChildEdge(position: Position, edge: Partial<Connection>)
}
class useAddToolNode {
+addToolNode(newNode: Node, nodeId?: string): boolean
}
class useResizeIterationNode {
+resizeIterationNode(type: string, position: Position, parentId?: string)
}
useAddNode --> useInitializeOperatorParams : uses
useAddNode --> useGetNodeName : uses
useAddNode --> useCalculateNewlyChildPosition : uses
useAddNode --> useAddChildEdge : uses
useAddNode --> useAddToolNode : uses
useAddNode --> useResizeIterationNode : uses
Summary
The use-add-node.ts file is a critical part of the application's flow graph editor. It encapsulates logic for adding and positioning nodes of various operator types, initializing their state, managing parent-child relationships, and maintaining a clear visual layout. Its design leverages React hooks, centralized state management, and localization, ensuring modularity and maintainability within the larger system.
By using this file's hooks, developers can programmatically and interactively add nodes with complex relationships, supporting a rich, user-friendly graph-building experience.