List logic implemented at the nodes

This commit is contained in:
Lucas Oliveira 2023-02-16 14:06:11 -03:00
commit 2a0547279c
9 changed files with 331 additions and 299 deletions

View file

@ -1,7 +1,7 @@
import { Bars3CenterLeftIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Bars3CenterLeftIcon, TrashIcon } from "@heroicons/react/24/outline";
import { Input } from "@mui/material"; import { Input } from "@mui/material";
import { Handle, Position } from "reactflow"; import { Handle, Position } from "reactflow";
import { nodeColors } from "../../utils"; import { isValidConnection, nodeColors } from "../../utils";
import ToggleComponent from "../../components/toggleComponent"; import ToggleComponent from "../../components/toggleComponent";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -32,11 +32,7 @@ export default function BooleanNode({ data }) {
type="target" type="target"
position={Position.Right} position={Position.Right}
id={data.name} id={data.name}
isValidConnection={({ sourceHandle, targetHandle }) => isValidConnection={(connection) => isValidConnection(data,connection)}
targetHandle === sourceHandle ||
data.types[targetHandle] === sourceHandle ||
sourceHandle === "str"
}
className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none" className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none"
style={{ borderLeftColor: nodeColors[data.type] }} style={{ borderLeftColor: nodeColors[data.type] }}
></Handle> ></Handle>

View file

@ -1,6 +1,6 @@
import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline"; import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline";
import Input from "../../components/inputComponent"; import Input from "../../components/inputComponent";
import { snakeToNormalCase } from "../../utils"; import { isValidConnection, snakeToNormalCase } from "../../utils";
import { Handle, Position } from "reactflow"; import { Handle, Position } from "reactflow";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
@ -11,8 +11,8 @@ export default function ChatInputNode({ data }) {
<Handle <Handle
type="target" type="target"
position={Position.Right} position={Position.Right}
id={data.name} id='str'
isValidConnection={({sourceHandle, targetHandle}) => (targetHandle === sourceHandle || data.types[targetHandle] === sourceHandle || sourceHandle === 'str')} isValidConnection={(connection) => isValidConnection(data,connection)}
className="-mr-1 bg-transparent border-solid border-l-8 border-l-blue-600 border-y-transparent border-y-8 border-r-0 rounded-none" className="-mr-1 bg-transparent border-solid border-l-8 border-l-blue-600 border-y-transparent border-y-8 border-r-0 rounded-none"
></Handle> ></Handle>
</Tooltip> </Tooltip>

View file

@ -1,7 +1,7 @@
import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline"; import { ChatBubbleBottomCenterTextIcon } from "@heroicons/react/24/outline";
import { Handle, Position } from "reactflow"; import { Handle, Position } from "reactflow";
import Input from "../../components/inputComponent"; import Input from "../../components/inputComponent";
import { snakeToNormalCase } from "../../utils"; import { isValidConnection, snakeToNormalCase } from "../../utils";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
export default function ChatOutputNode({ data }) { export default function ChatOutputNode({ data }) {
@ -10,13 +10,9 @@ export default function ChatOutputNode({ data }) {
<Tooltip title="Message: str"> <Tooltip title="Message: str">
<Handle <Handle
type="source" type="source"
isValidConnection={({ sourceHandle, targetHandle }) => isValidConnection={(connection) => isValidConnection(data,connection)}
targetHandle === sourceHandle ||
data.types[targetHandle] === sourceHandle ||
sourceHandle === "str"
}
position={Position.Left} position={Position.Left}
id="str" id={"str|output|"+data.id}
className="ml-1 bg-transparent border-solid border-l-8 border-l-white border-y-transparent border-y-8 border-r-0 rounded-none" className="ml-1 bg-transparent border-solid border-l-8 border-l-white border-y-transparent border-y-8 border-r-0 rounded-none"
></Handle> ></Handle>
</Tooltip> </Tooltip>

View file

@ -6,8 +6,9 @@ import {
import { Handle, Position } from "reactflow"; import { Handle, Position } from "reactflow";
import Dropdown from "../../components/dropdownComponent"; import Dropdown from "../../components/dropdownComponent";
import Input from "../../components/inputComponent"; import Input from "../../components/inputComponent";
import { nodeColors, nodeIcons, snakeToNormalCase } from "../../utils"; import { isValidConnection, nodeColors, nodeIcons, snakeToNormalCase } from "../../utils";
import Tooltip from "../../components/TooltipComponent"; import Tooltip from "../../components/TooltipComponent";
import { useEffect } from "react";
export default function GenericNode({ data }) { export default function GenericNode({ data }) {
const Icon = nodeIcons[data.type]; const Icon = nodeIcons[data.type];
@ -36,12 +37,8 @@ export default function GenericNode({ data }) {
<Handle <Handle
type="source" type="source"
position={Position.Left} position={Position.Left}
id={data.node.template[t].type} id={data.node.template[t].type + "|" + t + "|" + data.id}
isValidConnection={({ sourceHandle, targetHandle }) => isValidConnection={(connection) => isValidConnection(data,connection)}
targetHandle === sourceHandle ||
data.types[targetHandle] === sourceHandle ||
sourceHandle === "str"
}
className="ml-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none" className="ml-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none"
style={{ style={{
borderLeftColor: (nodeColors[(data.types[data.node.template[t].type] ?? data.node.template[t].type)]) ?? "gray", borderLeftColor: (nodeColors[(data.types[data.node.template[t].type] ?? data.node.template[t].type)]) ?? "gray",
@ -58,11 +55,7 @@ export default function GenericNode({ data }) {
type="target" type="target"
position={Position.Right} position={Position.Right}
id={data.name} id={data.name}
isValidConnection={({ sourceHandle, targetHandle }) => isValidConnection={(connection) => isValidConnection(data,connection)}
targetHandle === sourceHandle ||
data.types[targetHandle] === sourceHandle ||
sourceHandle === "str"
}
className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none" className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none"
style={{ borderLeftColor: nodeColors[data.type] }} style={{ borderLeftColor: nodeColors[data.type] }}
></Handle> ></Handle>

View file

@ -1,6 +1,6 @@
import { Bars3CenterLeftIcon, TrashIcon } from "@heroicons/react/24/outline"; import { Bars3CenterLeftIcon, TrashIcon } from "@heroicons/react/24/outline";
import Input from "../../components/inputComponent"; import Input from "../../components/inputComponent";
import { nodeColors, nodeIcons, snakeToNormalCase } from "../../utils"; import { isValidConnection, nodeColors, nodeIcons, snakeToNormalCase } from "../../utils";
import { Handle, Position } from "reactflow"; import { Handle, Position } from "reactflow";
import { useEffect } from "react"; import { useEffect } from "react";
@ -34,7 +34,7 @@ export default function InputNode({ data }) {
type="target" type="target"
position={Position.Right} position={Position.Right}
id={data.name} id={data.name}
isValidConnection={({sourceHandle, targetHandle}) => (targetHandle === sourceHandle || data.types[targetHandle] === sourceHandle || sourceHandle === 'str')} isValidConnection={(connection) => isValidConnection(data,connection)}
className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none" className="-mr-1 bg-transparent border-solid border-l-8 border-y-transparent border-y-8 border-r-0 rounded-none"
style={{borderLeftColor: nodeColors[data.type]}} style={{borderLeftColor: nodeColors[data.type]}}
></Handle> ></Handle>

View file

@ -71,7 +71,6 @@ export default function ExtraSidebar() {
onDragStart(event, { onDragStart(event, {
type: 'elements', type: 'elements',
name: 'str', name: 'str',
types: types
}) })
} }
> >

View file

@ -74,7 +74,7 @@ export default function FlowPage() {
id: newId, id: newId,
type: data.name === 'str' ? 'inputNode' : (data.name === 'chatInput' ? 'chatInputNode' : (data.name === 'chatOutput' ? 'chatOutputNode' : (data.name === 'bool' ? 'booleanNode' : 'genericNode'))), type: data.name === 'str' ? 'inputNode' : (data.name === 'chatInput' ? 'chatInputNode' : (data.name === 'chatOutput' ? 'chatOutputNode' : (data.name === 'bool' ? 'booleanNode' : 'genericNode'))),
position, position,
data: { ...data, input: '', enabled: false, instance: reactFlowInstance, onDelete: () => {setNodes(reactFlowInstance.getNodes().filter((n)=>n.id !== newId))} }, data: { ...data, id: newId, input: '', enabled: false, reactFlowInstance, onDelete: () => {setNodes(reactFlowInstance.getNodes().filter((n)=>n.id !== newId))} },
}; };
setNodes((nds) => nds.concat(newNode)); setNodes((nds) => nds.concat(newNode));
}, },

View file

@ -1,4 +1,12 @@
import { RocketLaunchIcon, LinkIcon, CpuChipIcon, LightBulbIcon, CommandLineIcon, WrenchScrewdriverIcon, ComputerDesktopIcon } from "@heroicons/react/24/outline"; import {
RocketLaunchIcon,
LinkIcon,
CpuChipIcon,
LightBulbIcon,
CommandLineIcon,
WrenchScrewdriverIcon,
ComputerDesktopIcon,
} from "@heroicons/react/24/outline";
import { Edge, Node } from "reactflow"; import { Edge, Node } from "reactflow";
export function classNames(...classes) { export function classNames(...classes) {
@ -94,8 +102,8 @@ export function classNames(...classes) {
agents: "#903BBE", agents: "#903BBE",
tools: "#FF3434", tools: "#FF3434",
memories: "#FF9135", memories: "#FF9135",
elements: "#6344BE" elements: "#6344BE",
} };
export const nodeNames = { export const nodeNames = {
prompts: "Prompts", prompts: "Prompts",
@ -105,8 +113,7 @@ export function classNames(...classes) {
tools: "Tools", tools: "Tools",
memories: "Memories", memories: "Memories",
elements: "Elements", elements: "Elements",
} };
export const nodeIcons = { export const nodeIcons = {
agents: RocketLaunchIcon, agents: RocketLaunchIcon,
@ -115,8 +122,8 @@ export function classNames(...classes) {
llms: LightBulbIcon, llms: LightBulbIcon,
prompts: CommandLineIcon, prompts: CommandLineIcon,
tools: WrenchScrewdriverIcon, tools: WrenchScrewdriverIcon,
elements: ComputerDesktopIcon elements: ComputerDesktopIcon,
} };
export const bgColors = { export const bgColors = {
white: "bg-white", white: "bg-white",
@ -213,7 +220,7 @@ export function classNames(...classes) {
"!": 0.2, "!": 0.2,
'"': 0.3, '"': 0.3,
"#": 0.5, "#": 0.5,
"$": 0.5, $: 0.5,
"%": 0.5, "%": 0.5,
"&": 0.5, "&": 0.5,
"(": 0.2, "(": 0.2,
@ -235,7 +242,7 @@ export function classNames(...classes) {
"\\": 0.5, "\\": 0.5,
"]": 0.2, "]": 0.2,
"^": 0.5, "^": 0.5,
"_": 0.2, _: 0.2,
"`": 0.5, "`": 0.5,
"{": 0.2, "{": 0.2,
"|": 0.2, "|": 0.2,
@ -282,32 +289,73 @@ export function classNames(...classes) {
export function toCamelCase(str: string) { export function toCamelCase(str: string) {
return str return str
.split(' ') .split(" ")
.map((word, index) => (index === 0 ? word.toLowerCase() : word[0].toUpperCase() + word.slice(1).toLowerCase())) .map((word, index) =>
.join(''); index === 0
}; ? word.toLowerCase()
: word[0].toUpperCase() + word.slice(1).toLowerCase()
)
.join("");
}
export function toFirstUpperCase(str: string) { export function toFirstUpperCase(str: string) {
return str return str
.split(' ') .split(" ")
.map((word, index) => (word[0].toUpperCase() + word.slice(1).toLowerCase())) .map((word, index) => word[0].toUpperCase() + word.slice(1).toLowerCase())
.join(''); .join("");
}; }
export function snakeToNormalCase(str: string) { export function snakeToNormalCase(str: string) {
return str return str
.split('_') .split("_")
.map((word, index) => {if(index === 0){return word[0].toUpperCase() + word.slice(1).toLowerCase()} return word.toLowerCase()}) .map((word, index) => {
.join(' '); if (index === 0) {
}; return word[0].toUpperCase() + word.slice(1).toLowerCase();
}
export function roundNumber(x, decimals){ return word.toLowerCase();
return Math.round(x * Math.pow(10, decimals))/Math.pow(10, decimals) })
.join(" ");
} }
export function roundNumber(x, decimals) {
return Math.round(x * Math.pow(10, decimals)) / Math.pow(10, decimals);
}
export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> { export function getConnectedNodes(edge: Edge, nodes: Array<Node>): Array<Node> {
const sourceId = edge.source; const sourceId = edge.source;
const targetId = edge.target; const targetId = edge.target;
const connectedNodes = nodes.filter(node=>node.id===sourceId||node.id===targetId) const connectedNodes = nodes.filter(
return connectedNodes (node) => node.id === sourceId || node.id === targetId
);
return connectedNodes;
}
export function isValidConnection(
data,
{ source, target, sourceHandle, targetHandle }
) {
if (
targetHandle === sourceHandle.split("|")[0] ||
sourceHandle.split("|")[0] === "str" ||
data.types[targetHandle] === sourceHandle.split("|")[0]
) {
let sourceNode = data.reactFlowInstance.getNode(source).data.node;
if (!sourceNode) {
if (
!data.reactFlowInstance
.getEdges()
.find((e) => e.sourceHandle === sourceHandle)
) {
return true;
}
} else if (
(!sourceNode.template[sourceHandle.split("|")[1]].list &&
!data.reactFlowInstance
.getEdges()
.find((e) => e.sourceHandle === sourceHandle)) ||
sourceNode.template[sourceHandle.split("|")[1]].list
) {
return true;
}
}
return false;
} }