From 743e176c5a0027fe0dfa0b26dea7f675771e170e Mon Sep 17 00:00:00 2001 From: MXerFix Date: Thu, 11 Apr 2024 15:39:24 +0300 Subject: [PATCH 01/11] start node fix + python conditions + new conditions structure --- frontend/package.json | 4 + frontend/src/components/nodes/DefaultNode.tsx | 18 ---- frontend/src/components/nodes/LinkNode.tsx | 16 --- frontend/src/components/nodes/StartNode.tsx | 99 ++++++++++++------- .../components/nodes/conditions/Condition.tsx | 28 +++++- .../modals/ConditionModal/ConditionModal.tsx | 97 +++++++++++------- .../components/PythonCondition.tsx | 65 ++++++++++++ .../components/UsingLLMCondition.tsx | 54 +++++++--- frontend/src/pages/NodesLayout.tsx | 32 ++++-- frontend/src/types/ConditionTypes.ts | 32 +++++- frontend/src/utils.ts | 6 +- 11 files changed, 316 insertions(+), 135 deletions(-) create mode 100644 frontend/src/modals/ConditionModal/components/PythonCondition.tsx diff --git a/frontend/package.json b/frontend/package.json index 958c2e4c..05fbf19f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@codemirror/lang-python": "^6.1.5", "@nextui-org/react": "^2.2.9", "@radix-ui/react-context-menu": "^2.1.5", "@react-spring/web": "^9.7.3", @@ -17,6 +18,9 @@ "@types/lodash": "^4.17.0", "@types/uuid": "^9.0.8", "@uidotdev/usehooks": "^2.4.1", + "@uiw/codemirror-theme-andromeda": "^4.21.25", + "@uiw/codemirror-theme-noctis-lilac": "^4.21.25", + "@uiw/react-codemirror": "^4.21.25", "axios": "^1.6.7", "classnames": "^2.5.1", "esbuild-wasm": "0.20.2", diff --git a/frontend/src/components/nodes/DefaultNode.tsx b/frontend/src/components/nodes/DefaultNode.tsx index 57760239..dda6e32f 100644 --- a/frontend/src/components/nodes/DefaultNode.tsx +++ b/frontend/src/components/nodes/DefaultNode.tsx @@ -22,23 +22,6 @@ const DefaultNode = memo(({ data }: { data: NodeDataType }) => { } = useDisclosure() const { onOpen: onNodeOpen, onClose: onNodeClose, isOpen: isNodeOpen } = useDisclosure() - const new_condition = useMemo( - () => ({ - id: data.name + "_" + v4(), - name: "New Condition", - type: "llm", - data: { - priority: 1, - transition_type: "manual", - prompt: "", - api_key: "", - action: "", - model_name: "", - }, - }), - [data.name, isConditionOpen, onConditionClose] - ) - return ( <>
@@ -88,7 +71,6 @@ const DefaultNode = memo(({ data }: { data: NodeDataType }) => {
{ const { onOpen, onClose, isOpen } = useDisclosure() const { openPopUp } = useContext(PopUpContext) - const new_condition = useMemo( - () => ({ - id: data.name + "_" + v4(), - name: "New Condition", - type: "custom", - data: { - prompt: "", - api_key: "", - action: "", - transition_type: "manual", - priority: 1, - }, - }), - [data.name, isOpen] - ) - return ( <>
diff --git a/frontend/src/components/nodes/StartNode.tsx b/frontend/src/components/nodes/StartNode.tsx index 9d43076b..8edcf3ac 100644 --- a/frontend/src/components/nodes/StartNode.tsx +++ b/frontend/src/components/nodes/StartNode.tsx @@ -6,45 +6,78 @@ import "../../index.css" import Condition from "./conditions/Condition" import Response from "./responses/Response" import { PlusIcon } from "lucide-react" +import { useDisclosure } from "@nextui-org/react" +import ConditionModal from "../../modals/ConditionModal/ConditionModal" +import NodeModal from "../../modals/NodeModal/NodeModal" +import EditNodeIcon from "../../icons/nodes/EditNodeIcon" const StartNode = memo(({ data }: { data: NodeDataType }) => { + const { + onOpen: onConditionOpen, + onClose: onConditionClose, + isOpen: isConditionOpen, + } = useDisclosure() + const { onOpen: onNodeOpen, onClose: onNodeClose, isOpen: isNodeOpen } = useDisclosure() + return ( -
-
- -

{data.name}

-
-
- -
- {data.conditions?.map((condition) => ( - +
+
+
+ - ))} +

{data.name}

+
+ +
+
+ +
+ {data.conditions?.map((condition) => ( + + ))} +
+
-
-
+ + + ) }) diff --git a/frontend/src/components/nodes/conditions/Condition.tsx b/frontend/src/components/nodes/conditions/Condition.tsx index 228cea31..b4b34e78 100644 --- a/frontend/src/components/nodes/conditions/Condition.tsx +++ b/frontend/src/components/nodes/conditions/Condition.tsx @@ -5,10 +5,17 @@ import { useEffect, useState } from "react" import { conditionLabelType } from "../../../types/ConditionTypes" import * as ContextMenu from "@radix-ui/react-context-menu" import { CONDITION_LABELS } from "../../../consts" +import ConditionModal from "../../../modals/ConditionModal/ConditionModal" +import { useDisclosure } from "@nextui-org/react" // eslint-disable-next-line @typescript-eslint/no-unused-vars const Condition = ({ data, condition }: NodeComponentConditionType) => { - const [label, setLabel] = useState(condition.data.transition_type ?? 'manual') + const [label, setLabel] = useState(condition.data.transition_type ?? "manual") + const { + onOpen: onConditionOpen, + onClose: onConditionClose, + isOpen: isConditionOpen, + } = useDisclosure() const edges = useReactFlow().getEdges() console.log(edges, condition) @@ -20,7 +27,7 @@ const Condition = ({ data, condition }: NodeComponentConditionType) => { return (
-
+
{ edge.sourceHandle === condition.id).length === 0} + isConnectable={ + edges.filter((edge) => edge.sourceHandle === condition.id).length === 0 + } position={Position.Right} type='source' id={`${condition.id}`} @@ -60,14 +69,23 @@ const Condition = ({ data, condition }: NodeComponentConditionType) => { )}
- + {Object.values(CONDITION_LABELS).map((item) => ( - setLabel(item)} className="text-sm cursor-pointer py-1 px-2 hover:bg-border rounded-lg"> + setLabel(item)} + className='text-sm cursor-pointer py-1 px-2 hover:bg-border rounded-lg'> {item} ))} + ) } diff --git a/frontend/src/modals/ConditionModal/ConditionModal.tsx b/frontend/src/modals/ConditionModal/ConditionModal.tsx index 63ed8845..eb5a8129 100644 --- a/frontend/src/modals/ConditionModal/ConditionModal.tsx +++ b/frontend/src/modals/ConditionModal/ConditionModal.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useMemo, useState } from "react" -import { conditionType } from "../../types/ConditionTypes" +import { conditionType, conditionTypeType } from "../../types/ConditionTypes" import { Button, Card, @@ -24,10 +24,11 @@ import { useReactFlow } from "reactflow" import { flowContext } from "../../contexts/flowContext" import { useParams } from "react-router-dom" import { generateNewConditionBase } from "../../utils" +import PythonCondition from "./components/PythonCondition" type ConditionModalProps = { data: NodeDataType - condition: conditionType + condition?: conditionType is_create?: boolean size?: ModalProps["size"] isOpen: boolean @@ -44,53 +45,76 @@ const ConditionModal = ({ onClose, size = "3xl", }: ConditionModalProps) => { - const [selected, setSelected] = useState("Using LLM") - const [currentCondition, setCurrentCondition] = useState(condition) + const [selected, setSelected] = useState(condition?.type ?? "python") + const setSelectedHandler = (key: conditionTypeType) => { + setCurrentCondition({ ...currentCondition, type: key }) + setSelected(key) + } + const [currentCondition, setCurrentCondition] = useState( + is_create || !condition ? generateNewConditionBase(data.name) : condition + ) const [conditions, setConditions] = useState(data.conditions ?? []) const { getNode, setNodes, getNodes } = useReactFlow() const { saveFlows, updateFlow, flows } = useContext(flowContext) const { flowId } = useParams() - useEffect(() => { - if (is_create) { - const new_condition = generateNewConditionBase(data.name) - setCurrentCondition(new_condition) - } - }, [data.name, is_create]) + // useEffect(() => { + // if (is_create) { + // const new_condition = generateNewConditionBase(data.name) + // setCurrentCondition(new_condition) + // } + // }, [data.name, is_create, isOpen]) - const tabItems = useMemo( + const tabItems: { + title: ConditionModalTab + value: conditionTypeType + }[] = useMemo( () => [ { title: "Using LLM", + value: "llm", }, { title: "Slot filling", + value: "slot", }, { title: "Button", + value: "button", }, { title: "Python code", + value: "python", }, { title: "Custom", + value: "custom", }, ], [] ) + useEffect(() => { + console.log(currentCondition) + }, [currentCondition]) + const bodyItems = useMemo( () => ({ - "Using LLM": ( + llm: ( ), - "Slot filling":
Slot filling
, - Button:
Button
, - "Python code":
Python code
, - Custom:
Custom
, + slot:
Slot filling
, + button:
Button
, + python: ( + + ), + custom:
Custom
, }), [currentCondition] ) @@ -100,22 +124,24 @@ const ConditionModal = ({ // }, [currentCondition]) const saveCondition = () => { - if (is_create) { - const nodes = getNodes() - const node = getNode(data.id) - const currentFlow = flows.find((flow) => flow.name === flowId) - if (node && currentFlow) { - const new_node = { - ...node, - data: { - ...node.data, - conditions: [...node.data.conditions, currentCondition], - }, - } - console.log(node, new_node) - setNodes(nodes.map((node) => (node.id === data.id ? new_node : node))) - updateFlow(currentFlow) + const nodes = getNodes() + const node = getNode(data.id) + const currentFlow = flows.find((flow) => flow.name === flowId) + if (node && currentFlow) { + const new_node = { + ...node, + data: { + ...node.data, + conditions: is_create + ? [...node.data.conditions, currentCondition] + : conditions.map((condition) => + condition.id === currentCondition.id ? currentCondition : condition + ), + }, } + console.log(node, new_node) + setNodes(nodes.map((node) => (node.id === data.id ? new_node : node))) + updateFlow(currentFlow) } onClose() } @@ -135,7 +161,7 @@ const ConditionModal = ({ selectedKey={selected} // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - onSelectionChange={setSelected} + onSelectionChange={setSelectedHandler} items={tabItems} classNames={{ tabList: "w-full", @@ -145,8 +171,11 @@ const ConditionModal = ({ className='bg-background w-full max-w-full'> {(item) => ( + key={item.value} + title={item.title} + onClick={() => + setCurrentCondition({ ...currentCondition, type: item.value }) + }> )} diff --git a/frontend/src/modals/ConditionModal/components/PythonCondition.tsx b/frontend/src/modals/ConditionModal/components/PythonCondition.tsx new file mode 100644 index 00000000..4f9ed090 --- /dev/null +++ b/frontend/src/modals/ConditionModal/components/PythonCondition.tsx @@ -0,0 +1,65 @@ +import { Input, Select, SelectItem, Textarea } from "@nextui-org/react" +import React, { useContext, useEffect, useMemo, useState } from "react" +import { conditionDataType, conditionType } from "../../../types/ConditionTypes" +import CodeMirror from "@uiw/react-codemirror" +import { python } from "@codemirror/lang-python" +import { noctisLilac } from "@uiw/codemirror-theme-noctis-lilac" +import { andromeda } from "@uiw/codemirror-theme-andromeda" +import { themeContext } from "../../../contexts/themeContext" + +const PythonCondition = ({ + condition, + setData, +}: { + condition: conditionType + setData: React.Dispatch> +}) => { + const { theme } = useContext(themeContext) + + useEffect(() => { + if (!condition.data.python) { + setData({ + ...condition, + type: "python", + data: { + ...condition.data, + python: { + action: `def ${condition.name}(ctx: Context, pipeline: Pipeline) -> bool:\n# enter your python condition:\nreturn True`, + }, + }, + }) + } + }, []) + + const changeConditionValue = (value: string) => { + setData({ + ...condition, + type: "python", + data: { + ...condition.data, + python: { + action: value, + }, + }, + }) + } + return ( + <> +

Action

+
+ +
+ + ) +} + +export default PythonCondition diff --git a/frontend/src/modals/ConditionModal/components/UsingLLMCondition.tsx b/frontend/src/modals/ConditionModal/components/UsingLLMCondition.tsx index 87884679..6f61ccb4 100644 --- a/frontend/src/modals/ConditionModal/components/UsingLLMCondition.tsx +++ b/frontend/src/modals/ConditionModal/components/UsingLLMCondition.tsx @@ -1,18 +1,46 @@ import { Input, Select, SelectItem, Textarea } from "@nextui-org/react" -import React, { useMemo, useState } from "react" +import React, { useEffect, useMemo, useState } from "react" import { conditionDataType, conditionType } from "../../../types/ConditionTypes" -const UsingLLMConditionSection = ({ condition, setData }: { condition: conditionType, setData: React.Dispatch> }) => { +const UsingLLMConditionSection = ({ + condition, + setData, +}: { + condition: conditionType + setData: React.Dispatch> +}) => { const modelNames = useMemo(() => ["gpt-3", "gpt-3.5-turbo", "gpt-4"], []) - // const [conditionData, setConditionData] = useState(data) - console.log(condition) - const changeConditionValue = (e: React.ChangeEvent) => { + + useEffect(() => { + if (!condition.data.llm) { + setData({ + ...condition, + type: "llm", + data: { + ...condition.data, + llm: { + model_name: "", + api_key: "", + prompt: "", + }, + }, + }) + } + }, []) + + const changeConditionValue = ( + e: React.ChangeEvent + ) => { setData({ ...condition, + type: "llm", data: { ...condition.data, - [e.target.name]: e.target.value, - } + llm: { + ...condition.data.llm!, + [e.target.name]: e.target.value, + }, + }, }) } return ( @@ -20,9 +48,9 @@ const UsingLLMConditionSection = ({ condition, setData }: { condition: condition