Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
"use client";
import "@xyflow/react/dist/style.css";
import {
addEdge,
applyEdgeChanges,
applyNodeChanges,
Background,
type Connection,
type EdgeChange,
type Node,
type NodeChange,
type NodeProps,
type NodeTypes,
ReactFlow,
ReactFlowProvider,
} from "@xyflow/react";
import { nanoid } from "nanoid";
import { useCallback, useState } from "react";
import type { Model } from "@/components/ui/model-selector";
import { GenerateTextNode } from "../ui/flow/generate-text-node";
const GenerateTextNodeController = ({
id,
data,
...props
}: NodeProps<Node>) => {
const [model, setModel] = useState<Model>("deepseek-chat");
const [toolHandles, setToolHandles] = useState({
tools: [
{
id: "name",
name: "name",
},
],
});
const handleCreateTool = useCallback(() => {
setToolHandles({
...toolHandles,
tools: [...toolHandles.tools, { id: nanoid(), name: "name" }],
});
return true;
}, [toolHandles]);
const handleRemoveTool = useCallback(() => {
setToolHandles({
...toolHandles,
tools: toolHandles.tools.filter((tool) => tool.id !== "name"),
});
return true;
}, [toolHandles]);
const handleUpdateTool = useCallback(
(toolId: string, newName: string, newDescription?: string) => {
setToolHandles({
...toolHandles,
tools: toolHandles.tools.map((tool) =>
tool.id === toolId
? {
...tool,
name: newName,
description: newDescription,
}
: tool,
),
});
return true;
},
[toolHandles],
);
return (
<GenerateTextNode
id={id}
data={{
status: "idle",
config: { model },
dynamicHandles: toolHandles,
}}
{...props}
type="generate-text"
onModelChange={(model) => setModel(model)}
onCreateTool={handleCreateTool}
onRemoveTool={handleRemoveTool}
onUpdateTool={handleUpdateTool}
onDeleteNode={() => {}}
/>
);
};
const nodeTypes: NodeTypes = {
"generate-text": GenerateTextNodeController,
};
const initialNodes = [
{
id: "node-1",
type: "generate-text",
position: { x: 0, y: -130 },
data: {},
},
];
export function ResizableNodeDemo() {
const [nodes, setNodes] = useState<Node[]>(initialNodes);
const [edges, setEdges] = useState([]);
// Add default viewport configuration
const defaultViewport = { x: 100, y: 200, zoom: 1 };
const onNodesChange = useCallback(
(changes: NodeChange<Node>[]) =>
setNodes((nds) => applyNodeChanges(changes, nds)),
[],
);
const onEdgesChange = useCallback(
(changes: EdgeChange<never>[]) =>
setEdges((eds) => applyEdgeChanges(changes, eds)),
[],
);
const onConnect = useCallback(
(connection: Connection) => setEdges((eds) => addEdge(connection, eds)),
[],
);
return (
<div className="w-full h-full">
<ReactFlowProvider>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes}
defaultViewport={defaultViewport}
/* fitView */
>
<Background />
</ReactFlow>
</ReactFlowProvider>
</div>
);
}
Overview
The Generate Text Node is a React Flow component that provides a visual interface representing the generateText function from Vercel AI SDK. When executing your flow, this node will map to the SDK's text generation functionality. It allows you to:
- Set system-level instructions that guide the AI's behavior
- Input specific prompts for each generation
- Receive generated text as output
- Optionally define tool outputs that the AI can use to route information to different parts of your workflow
Components Used
This node is built using several React Flow Components:
- Base Node - For the core node structure and styling
- Node Header - For the node's header section
- Labeled Handle - For the input and output connection points
Installation
pnpm dlx shadcn@latest add @simple-ai/generate-text-node
Usage
The Generate Text Node requires a controller component to manage its state and handle tool-related operations. Here's how to implement it in your React Flow application:
// Controller component to manage the Generate Text Node
const GenerateTextNodeController = ({
id,
data,
...props
}: NodeProps<Node>) => {
const [model, setModel] = useState<Model>("deepseek-chat");
const [toolHandles, setToolHandles] = useState({
tools: [{ id: "name", name: "name" }],
});
// Handle tool creation
const handleCreateTool = useCallback(() => {
setToolHandles({
...toolHandles,
tools: [...toolHandles.tools, { id: nanoid(), name: "name" }],
});
return true;
}, [toolHandles]);
// Handle tool removal
const handleRemoveTool = useCallback((toolId: string) => {
setToolHandles({
...toolHandles,
tools: toolHandles.tools.filter((tool) => tool.id !== toolId),
});
}, [toolHandles]);
return (
<GenerateTextNode
id={id}
data={{
status: "idle",
config: { model },
dynamicHandles: toolHandles,
}}
{...props}
onModelChange={(model) => setModel(model)}
onCreateTool={handleCreateTool}
onRemoveTool={handleRemoveTool}
onUpdateTool={handleUpdateTool}
/>
);
};
// Register the node type
const nodeTypes = {
"generate-text": GenerateTextNodeController,
};The node provides the following connection points:
- Inputs:
system: For system-level instructions that guide the AI's behaviorprompt: For the specific text generation prompt
- Outputs:
result: The main output containing the generated text- Dynamic tool outputs: Optional outputs that can be added/removed as needed
The node's state includes:
- Model selection for text generation
- Processing status indication (idle, processing, error, success)
- Dynamic tool management (add, remove, update)