diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index 6e483a99f..2d93ca1ac 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -17,16 +17,20 @@ import { PopUpContext } from "../../../../contexts/popUpContext"; import { TabsContext } from "../../../../contexts/tabsContext"; import { typesContext } from "../../../../contexts/typesContext"; import { ParameterComponentType } from "../../../../types/components"; -import { cleanEdges } from "../../../../util/reactflowUtils"; +import { + cleanEdges, + isValidConnection, +} from "../../../../utils/reactflowUtils"; +import { + nodeColors, + nodeIconsLucide, + nodeNames, +} from "../../../../utils/styleUtils"; import { classNames, getRandomKeyByssmm, groupByFamily, - isValidConnection, - nodeColors, - nodeIconsLucide, - nodeNames, -} from "../../../../utils"; +} from "../../../../utils/utils"; export default function ParameterComponent({ left, diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 5b3f75e18..ba506c855 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -10,12 +10,8 @@ import { typesContext } from "../../contexts/typesContext"; import NodeModal from "../../modals/NodeModal"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import { NodeDataType } from "../../types/flow"; -import { - classNames, - nodeColors, - nodeIconsLucide, - toTitleCase, -} from "../../utils"; +import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; +import { classNames, toTitleCase } from "../../utils/utils"; import ParameterComponent from "./components/parameterComponent"; export default function GenericNode({ diff --git a/src/frontend/src/components/ReactTooltipComponent/index.tsx b/src/frontend/src/components/ReactTooltipComponent/index.tsx index f49e252a6..0f0c64b89 100644 --- a/src/frontend/src/components/ReactTooltipComponent/index.tsx +++ b/src/frontend/src/components/ReactTooltipComponent/index.tsx @@ -3,7 +3,7 @@ import type { FC } from "react"; import React from "react"; import { Tooltip as ReactTooltip } from "react-tooltip"; import "react-tooltip/dist/react-tooltip.css"; -import { classNames } from "../../utils"; +import { classNames } from "../../utils/utils"; type TooltipProps = { selector: string; diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index be4c01eb9..12c409047 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -1,7 +1,7 @@ import { useContext } from "react"; import { TabsContext } from "../../contexts/tabsContext"; import { FlowType } from "../../types/flow"; -import { gradients } from "../../utils"; +import { gradients } from "../../utils/styleUtils"; import IconComponent from "../genericIconComponent"; import { Card, diff --git a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx index 553a2becf..4eb69546e 100644 --- a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx +++ b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx @@ -6,9 +6,9 @@ import { alertContext } from "../../../contexts/alertContext"; import { typesContext } from "../../../contexts/typesContext"; import { postBuildInit } from "../../../controllers/API"; import { FlowType } from "../../../types/flow"; -import { validateNodes } from "../../../utils"; import { TabsContext } from "../../../contexts/tabsContext"; +import { validateNodes } from "../../../utils/reactflowUtils"; import RadialProgressComponent from "../../RadialProgress"; import IconComponent from "../../genericIconComponent"; diff --git a/src/frontend/src/components/dropdownComponent/index.tsx b/src/frontend/src/components/dropdownComponent/index.tsx index 7b6db405c..9b614c7ae 100644 --- a/src/frontend/src/components/dropdownComponent/index.tsx +++ b/src/frontend/src/components/dropdownComponent/index.tsx @@ -2,7 +2,7 @@ import { Listbox, Transition } from "@headlessui/react"; import { Fragment, useContext, useEffect, useState } from "react"; import { PopUpContext } from "../../contexts/popUpContext"; import { DropDownComponentType } from "../../types/components"; -import { classNames } from "../../utils"; +import { classNames } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; export default function Dropdown({ diff --git a/src/frontend/src/components/inputComponent/index.tsx b/src/frontend/src/components/inputComponent/index.tsx index 8da5c428e..8d564bdcd 100644 --- a/src/frontend/src/components/inputComponent/index.tsx +++ b/src/frontend/src/components/inputComponent/index.tsx @@ -2,7 +2,7 @@ import { useContext, useEffect, useState } from "react"; import { PopUpContext } from "../../contexts/popUpContext"; import { TabsContext } from "../../contexts/tabsContext"; import { InputComponentType } from "../../types/components"; -import { classNames } from "../../utils"; +import { classNames } from "../../utils/utils"; export default function InputComponent({ value, diff --git a/src/frontend/src/components/promptComponent/index.tsx b/src/frontend/src/components/promptComponent/index.tsx index 52d14ea3f..80ddd7c88 100644 --- a/src/frontend/src/components/promptComponent/index.tsx +++ b/src/frontend/src/components/promptComponent/index.tsx @@ -1,11 +1,11 @@ import { useContext, useEffect, useState } from "react"; + import { PopUpContext } from "../../contexts/popUpContext"; +import { typesContext } from "../../contexts/typesContext"; +import { postValidatePrompt } from "../../controllers/API"; import GenericModal from "../../modals/genericModal"; import { TextAreaComponentType } from "../../types/components"; import { TypeModal } from "../../utils"; - -import { typesContext } from "../../contexts/typesContext"; -import { postValidatePrompt } from "../../controllers/API"; import IconComponent from "../genericIconComponent"; export default function PromptAreaComponent({ diff --git a/src/frontend/src/components/textAreaComponent/index.tsx b/src/frontend/src/components/textAreaComponent/index.tsx index fafa1e703..511694bf8 100644 --- a/src/frontend/src/components/textAreaComponent/index.tsx +++ b/src/frontend/src/components/textAreaComponent/index.tsx @@ -1,10 +1,9 @@ import { useContext, useEffect, useState } from "react"; +import { TypeModal } from "../../constants"; import { PopUpContext } from "../../contexts/popUpContext"; +import { TabsContext } from "../../contexts/tabsContext"; import GenericModal from "../../modals/genericModal"; import { TextAreaComponentType } from "../../types/components"; -import { TypeModal } from "../../utils"; - -import { TabsContext } from "../../contexts/tabsContext"; import IconComponent from "../genericIconComponent"; export default function TextAreaComponent({ diff --git a/src/frontend/src/components/toggleComponent/index.tsx b/src/frontend/src/components/toggleComponent/index.tsx index 81a2b2d8b..107e65b20 100644 --- a/src/frontend/src/components/toggleComponent/index.tsx +++ b/src/frontend/src/components/toggleComponent/index.tsx @@ -1,7 +1,7 @@ import { Switch } from "@headlessui/react"; import { useEffect } from "react"; import { ToggleComponentType } from "../../types/components"; -import { classNames } from "../../utils"; +import { classNames } from "../../utils/utils"; export default function ToggleComponent({ enabled, diff --git a/src/frontend/src/components/ui/accordion.tsx b/src/frontend/src/components/ui/accordion.tsx index 4b5a12384..ced816cfc 100644 --- a/src/frontend/src/components/ui/accordion.tsx +++ b/src/frontend/src/components/ui/accordion.tsx @@ -3,7 +3,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion"; import { ChevronDownIcon } from "@radix-ui/react-icons"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Accordion = AccordionPrimitive.Root; diff --git a/src/frontend/src/components/ui/badge.tsx b/src/frontend/src/components/ui/badge.tsx index 5464f382e..c101e3114 100644 --- a/src/frontend/src/components/ui/badge.tsx +++ b/src/frontend/src/components/ui/badge.tsx @@ -1,6 +1,6 @@ import { cva, type VariantProps } from "class-variance-authority"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const badgeVariants = cva( "inline-flex items-center border rounded-full px-2.5 font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index 170494927..becc8d5f1 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -1,7 +1,7 @@ import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", diff --git a/src/frontend/src/components/ui/card.tsx b/src/frontend/src/components/ui/card.tsx index 0c5863871..585a81a76 100644 --- a/src/frontend/src/components/ui/card.tsx +++ b/src/frontend/src/components/ui/card.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Card = React.forwardRef< HTMLDivElement, diff --git a/src/frontend/src/components/ui/checkbox.tsx b/src/frontend/src/components/ui/checkbox.tsx index 0948f93cc..55d805027 100644 --- a/src/frontend/src/components/ui/checkbox.tsx +++ b/src/frontend/src/components/ui/checkbox.tsx @@ -2,7 +2,7 @@ import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; const Checkbox = React.forwardRef< diff --git a/src/frontend/src/components/ui/dialog.tsx b/src/frontend/src/components/ui/dialog.tsx index 169c3742c..d68f281fb 100644 --- a/src/frontend/src/components/ui/dialog.tsx +++ b/src/frontend/src/components/ui/dialog.tsx @@ -1,6 +1,6 @@ import * as DialogPrimitive from "@radix-ui/react-dialog"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; const Dialog = DialogPrimitive.Root; diff --git a/src/frontend/src/components/ui/dropdown-menu.tsx b/src/frontend/src/components/ui/dropdown-menu.tsx index 2a4758d33..c61e09fc1 100644 --- a/src/frontend/src/components/ui/dropdown-menu.tsx +++ b/src/frontend/src/components/ui/dropdown-menu.tsx @@ -2,7 +2,7 @@ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; const DropdownMenu = DropdownMenuPrimitive.Root; diff --git a/src/frontend/src/components/ui/input.tsx b/src/frontend/src/components/ui/input.tsx index f3a8757e8..a59b419f3 100644 --- a/src/frontend/src/components/ui/input.tsx +++ b/src/frontend/src/components/ui/input.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; export interface InputProps extends React.InputHTMLAttributes {} diff --git a/src/frontend/src/components/ui/label.tsx b/src/frontend/src/components/ui/label.tsx index 57fbc9d8a..f8d45c11d 100644 --- a/src/frontend/src/components/ui/label.tsx +++ b/src/frontend/src/components/ui/label.tsx @@ -3,7 +3,7 @@ import * as LabelPrimitive from "@radix-ui/react-label"; import { cva, type VariantProps } from "class-variance-authority"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const labelVariants = cva( "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" diff --git a/src/frontend/src/components/ui/menubar.tsx b/src/frontend/src/components/ui/menubar.tsx index 812fcd412..276f5fc5b 100644 --- a/src/frontend/src/components/ui/menubar.tsx +++ b/src/frontend/src/components/ui/menubar.tsx @@ -3,7 +3,7 @@ import * as MenubarPrimitive from "@radix-ui/react-menubar"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; import IconComponent from "../genericIconComponent"; const MenubarMenu = MenubarPrimitive.Menu; diff --git a/src/frontend/src/components/ui/progress.tsx b/src/frontend/src/components/ui/progress.tsx index a08efeb52..c2afa8a4c 100644 --- a/src/frontend/src/components/ui/progress.tsx +++ b/src/frontend/src/components/ui/progress.tsx @@ -2,7 +2,7 @@ import * as ProgressPrimitive from "@radix-ui/react-progress"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Progress = React.forwardRef< React.ElementRef, diff --git a/src/frontend/src/components/ui/rename-label.tsx b/src/frontend/src/components/ui/rename-label.tsx index e5fef11bf..81d3ce446 100644 --- a/src/frontend/src/components/ui/rename-label.tsx +++ b/src/frontend/src/components/ui/rename-label.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef, useState } from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; export default function RenameLabel(props) { const [internalState, setInternalState] = useState(false); diff --git a/src/frontend/src/components/ui/separator.tsx b/src/frontend/src/components/ui/separator.tsx index 1fa0287d8..1fec6d2ca 100644 --- a/src/frontend/src/components/ui/separator.tsx +++ b/src/frontend/src/components/ui/separator.tsx @@ -2,7 +2,7 @@ import * as SeparatorPrimitive from "@radix-ui/react-separator"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Separator = React.forwardRef< React.ElementRef, diff --git a/src/frontend/src/components/ui/switch.tsx b/src/frontend/src/components/ui/switch.tsx index 8ec7c3c80..0d4bc8224 100644 --- a/src/frontend/src/components/ui/switch.tsx +++ b/src/frontend/src/components/ui/switch.tsx @@ -2,7 +2,7 @@ import * as SwitchPrimitives from "@radix-ui/react-switch"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Switch = React.forwardRef< React.ElementRef, diff --git a/src/frontend/src/components/ui/table.tsx b/src/frontend/src/components/ui/table.tsx index 9b7bdd8ce..80a74f378 100644 --- a/src/frontend/src/components/ui/table.tsx +++ b/src/frontend/src/components/ui/table.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Table = React.forwardRef< HTMLTableElement, diff --git a/src/frontend/src/components/ui/tabs.tsx b/src/frontend/src/components/ui/tabs.tsx index 8c382e35e..1a5d42254 100644 --- a/src/frontend/src/components/ui/tabs.tsx +++ b/src/frontend/src/components/ui/tabs.tsx @@ -2,7 +2,7 @@ import * as TabsPrimitive from "@radix-ui/react-tabs"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const Tabs = TabsPrimitive.Root; diff --git a/src/frontend/src/components/ui/textarea.tsx b/src/frontend/src/components/ui/textarea.tsx index fc0cda2ff..64aec41a1 100644 --- a/src/frontend/src/components/ui/textarea.tsx +++ b/src/frontend/src/components/ui/textarea.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; export interface TextareaProps extends React.TextareaHTMLAttributes {} diff --git a/src/frontend/src/components/ui/tooltip.tsx b/src/frontend/src/components/ui/tooltip.tsx index 6bb983652..c54245a53 100644 --- a/src/frontend/src/components/ui/tooltip.tsx +++ b/src/frontend/src/components/ui/tooltip.tsx @@ -2,7 +2,7 @@ import * as TooltipPrimitive from "@radix-ui/react-tooltip"; import * as React from "react"; -import { cn } from "../../utils"; +import { cn } from "../../utils/utils"; const TooltipProvider = TooltipPrimitive.Provider; diff --git a/src/frontend/src/constants.tsx b/src/frontend/src/constants.ts similarity index 87% rename from src/frontend/src/constants.tsx rename to src/frontend/src/constants.ts index 9c167c271..a814c6efb 100644 --- a/src/frontend/src/constants.tsx +++ b/src/frontend/src/constants.ts @@ -1,10 +1,87 @@ // src/constants.tsx import { MessageSquare } from "lucide-react"; +import { IVarHighlightType } from "./types/components"; import { FlowType } from "./types/flow"; import { TabsState } from "./types/tabs"; -import { buildInputs, buildTweaks } from "./utils"; +import { buildTweaks } from "./utils/reactflowUtils"; +import { buildInputs } from "./utils/utils"; +/** + * constants fpr programming languages box on chat form + * @constant + */ +interface languageMap { + [key: string]: string | undefined; +} +/** + * invalid characters for flow name + * @constant + */ +export const INVALID_CHARACTERS = [ + " ", + ",", + ".", + ":", + ";", + "!", + "?", + "/", + "\\", + "(", + ")", + "[", + "]", + "\n", +]; + +/** + * regex to highlight the variables in the text + * @constant + */ + +export const regexHighlight = /\{([^}]+)\}/g; + +export const varHighlightHTML = ({ name }: IVarHighlightType) => { + const html = `{${name}}`; + return html; +}; + +export const programmingLanguages: languageMap = { + javascript: ".js", + python: ".py", + java: ".java", + c: ".c", + cpp: ".cpp", + "c++": ".cpp", + "c#": ".cs", + ruby: ".rb", + php: ".php", + swift: ".swift", + "objective-c": ".m", + kotlin: ".kt", + typescript: ".ts", + go: ".go", + perl: ".pl", + rust: ".rs", + scala: ".scala", + haskell: ".hs", + lua: ".lua", + shell: ".sh", + sql: ".sql", + html: ".html", + css: ".css", + // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component +}; + +/** + * enum for the different types of nodes + * @enum + */ +export enum TypeModal { + TEXT = 1, + PROMPT = 2, +} /** * Number maximum of components to scroll on tooltips * @constant @@ -17,6 +94,12 @@ export const MAX_LENGTH_TO_SCROLL_TOOLTIP = 200; */ export const MAX_WORDS_HIGHLIGHT = 79; +/** + * Limit of items before show scroll on fields modal + * @constant + */ +export const limitScrollFieldsModal = 10; + /** * The base text for subtitle of Export Dialog (Toolbar) * @constant diff --git a/src/frontend/src/contexts/tabsContext.tsx b/src/frontend/src/contexts/tabsContext.tsx index 85eb79410..f60b92635 100644 --- a/src/frontend/src/contexts/tabsContext.tsx +++ b/src/frontend/src/contexts/tabsContext.tsx @@ -20,12 +20,8 @@ import { import { APIClassType, APITemplateType } from "../types/api"; import { FlowType, NodeType } from "../types/flow"; import { TabsContextType, TabsState } from "../types/tabs"; -import { - getRandomDescription, - getRandomName, - updateIds, - updateTemplate, -} from "../utils"; +import { updateIds, updateTemplate } from "../utils/reactflowUtils"; +import { getRandomDescription, getRandomName } from "../utils/utils"; import { alertContext } from "./alertContext"; import { typesContext } from "./typesContext"; diff --git a/src/frontend/src/modals/ApiModal/index.tsx b/src/frontend/src/modals/ApiModal/index.tsx index 66dd45fdc..7a1a6a170 100644 --- a/src/frontend/src/modals/ApiModal/index.tsx +++ b/src/frontend/src/modals/ApiModal/index.tsx @@ -51,8 +51,8 @@ import { darkContext } from "../../contexts/darkContext"; import { PopUpContext } from "../../contexts/popUpContext"; import { TabsContext } from "../../contexts/tabsContext"; import { FlowType } from "../../types/flow/index"; -import { buildTweaks, classNames } from "../../utils"; - +import { buildTweaks } from "../../utils/reactflowUtils"; +import { classNames } from "../../utils/utils"; export default function ApiModal({ flow }: { flow: FlowType }) { const [open, setOpen] = useState(true); const { dark } = useContext(darkContext); diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx index 53cdb2812..86cd2be0b 100644 --- a/src/frontend/src/modals/EditNodeModal/index.tsx +++ b/src/frontend/src/modals/EditNodeModal/index.tsx @@ -29,11 +29,12 @@ import { TableHeader, TableRow, } from "../../components/ui/table"; +import { limitScrollFieldsModal } from "../../constants"; import { PopUpContext } from "../../contexts/popUpContext"; import { TabsContext } from "../../contexts/tabsContext"; import { typesContext } from "../../contexts/typesContext"; import { NodeDataType } from "../../types/flow"; -import { classNames, limitScrollFieldsModal } from "../../utils"; +import { classNames } from "../../utils/utils"; export default function EditNodeModal({ data }: { data: NodeDataType }) { const [open, setOpen] = useState(true); diff --git a/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx b/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx index ad82d0c0d..15c0984ea 100644 --- a/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx +++ b/src/frontend/src/modals/NodeModal/components/ModalField/index.tsx @@ -9,7 +9,7 @@ import IntComponent from "../../../../components/intComponent"; import PromptAreaComponent from "../../../../components/promptComponent"; import TextAreaComponent from "../../../../components/textAreaComponent"; import ToggleComponent from "../../../../components/toggleComponent"; -import { classNames } from "../../../../utils"; +import { classNames } from "../../../../utils/utils"; export default function ModalField({ data, diff --git a/src/frontend/src/modals/NodeModal/index.tsx b/src/frontend/src/modals/NodeModal/index.tsx index 71127a42a..c3f50cd21 100644 --- a/src/frontend/src/modals/NodeModal/index.tsx +++ b/src/frontend/src/modals/NodeModal/index.tsx @@ -1,16 +1,12 @@ import { Dialog, Transition } from "@headlessui/react"; import { Fragment, useContext, useRef, useState } from "react"; import IconComponent from "../../components/genericIconComponent"; +import { limitScrollFieldsModal } from "../../constants"; import { PopUpContext } from "../../contexts/popUpContext"; import { typesContext } from "../../contexts/typesContext"; import { NodeDataType } from "../../types/flow"; -import { - classNames, - limitScrollFieldsModal, - nodeColors, - nodeIconsLucide, - toTitleCase, -} from "../../utils"; +import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; +import { classNames, toTitleCase } from "../../utils/utils"; import ModalField from "./components/ModalField"; export default function NodeModal({ data }: { data: NodeDataType }) { diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index 52f302777..fbaf75a55 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -16,7 +16,7 @@ import { EXPORT_DIALOG_SUBTITLE } from "../../constants"; import { alertContext } from "../../contexts/alertContext"; import { PopUpContext } from "../../contexts/popUpContext"; import { TabsContext } from "../../contexts/tabsContext"; -import { removeApiKeys } from "../../utils"; +import { removeApiKeys } from "../../utils/reactflowUtils"; export default function ExportModal() { const [open, setOpen] = useState(true); diff --git a/src/frontend/src/modals/formModal/chatInput/index.tsx b/src/frontend/src/modals/formModal/chatInput/index.tsx index 5fd35ec9c..63f224e92 100644 --- a/src/frontend/src/modals/formModal/chatInput/index.tsx +++ b/src/frontend/src/modals/formModal/chatInput/index.tsx @@ -1,6 +1,6 @@ import { useEffect } from "react"; import IconComponent from "../../../components/genericIconComponent"; -import { classNames } from "../../../utils"; +import { classNames } from "../../../utils/utils"; export default function ChatInput({ lockChat, diff --git a/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx b/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx index 884215ebb..44011821d 100644 --- a/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx +++ b/src/frontend/src/modals/formModal/chatMessage/codeBlock/index.tsx @@ -2,7 +2,7 @@ import { IconCheck, IconClipboard, IconDownload } from "@tabler/icons-react"; import { useState } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; -import { programmingLanguages } from "../../../../utils"; +import { programmingLanguages } from "../../../../constants"; interface Props { language: string; diff --git a/src/frontend/src/modals/formModal/chatMessage/index.tsx b/src/frontend/src/modals/formModal/chatMessage/index.tsx index b250882f0..a2efa8035 100644 --- a/src/frontend/src/modals/formModal/chatMessage/index.tsx +++ b/src/frontend/src/modals/formModal/chatMessage/index.tsx @@ -10,7 +10,7 @@ import SanitizedHTMLWrapper from "../../../components/SanitizedHTMLWrapper"; import IconComponent from "../../../components/genericIconComponent"; import { THOUGHTS_ICON } from "../../../constants"; import { ChatMessageType } from "../../../types/chat"; -import { classNames } from "../../../utils"; +import { classNames } from "../../../utils/utils"; import FileCard from "../fileComponent"; import { CodeBlock } from "./codeBlock"; export default function ChatMessage({ @@ -81,7 +81,7 @@ export default function ChatMessage({ remarkPlugins={[remarkGfm, remarkMath]} rehypePlugins={[rehypeMathjax]} className="markdown prose inline-block break-words text-primary - dark:prose-invert sm:max-w-[30vw] lg:max-w-[40vw] sm:w-[30vw] lg:w-[40vw]" + dark:prose-invert sm:w-[30vw] sm:max-w-[30vw] lg:w-[40vw] lg:max-w-[40vw]" components={{ code: ({ node, diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx index b6d80458e..496210936 100644 --- a/src/frontend/src/modals/formModal/index.tsx +++ b/src/frontend/src/modals/formModal/index.tsx @@ -4,7 +4,7 @@ import { typesContext } from "../../contexts/typesContext"; import { sendAllProps } from "../../types/api"; import { ChatMessageType } from "../../types/chat"; import { FlowType } from "../../types/flow"; -import { classNames, validateNodes } from "../../utils"; +import { classNames } from "../../utils/utils"; import ChatInput from "./chatInput"; import ChatMessage from "./chatMessage"; @@ -29,6 +29,7 @@ import { import { Textarea } from "../../components/ui/textarea"; import { CHAT_FORM_DIALOG_SUBTITLE, THOUGHTS_ICON } from "../../constants"; import { TabsContext } from "../../contexts/tabsContext"; +import { validateNodes } from "../../utils/reactflowUtils"; export default function FormModal({ flow, diff --git a/src/frontend/src/modals/genericModal/index.tsx b/src/frontend/src/modals/genericModal/index.tsx index ac2a6839f..ccf484935 100644 --- a/src/frontend/src/modals/genericModal/index.tsx +++ b/src/frontend/src/modals/genericModal/index.tsx @@ -6,20 +6,21 @@ import { Badge } from "../../components/ui/badge"; import { Button } from "../../components/ui/button"; import { DialogTitle } from "../../components/ui/dialog"; import { Textarea } from "../../components/ui/textarea"; -import { MAX_WORDS_HIGHLIGHT, PROMPT_DIALOG_SUBTITLE, TEXT_DIALOG_SUBTITLE } from "../../constants"; +import { + INVALID_CHARACTERS, + MAX_WORDS_HIGHLIGHT, + PROMPT_DIALOG_SUBTITLE, + TEXT_DIALOG_SUBTITLE, + TypeModal, + regexHighlight, + varHighlightHTML, +} from "../../constants"; import { alertContext } from "../../contexts/alertContext"; import { darkContext } from "../../contexts/darkContext"; import { PopUpContext } from "../../contexts/popUpContext"; import { postValidatePrompt } from "../../controllers/API"; import { APIClassType } from "../../types/api"; -import { - INVALID_CHARACTERS, - TypeModal, - classNames, - getRandomKeyByssmm, - regexHighlight, - varHighlightHTML, -} from "../../utils"; +import { classNames, getRandomKeyByssmm } from "../../utils/utils"; import BaseModal from "../baseModal"; export default function GenericModal({ @@ -120,15 +121,16 @@ export default function GenericModal({ ); }; - function getClassByNumberLength(){ + function getClassByNumberLength() { let sumOfCaracteres: number = 0; - wordsHighlight.forEach(element => { - sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length + wordsHighlight.forEach((element) => { + sumOfCaracteres = sumOfCaracteres + element.replace(/[{}]/g, "").length; }); - return sumOfCaracteres > MAX_WORDS_HIGHLIGHT ? "code-highlight" : "code-nohighlight" + return sumOfCaracteres > MAX_WORDS_HIGHLIGHT + ? "code-highlight" + : "code-nohighlight"; } - function validatePrompt(closeModal: boolean) { postValidatePrompt(field_name, inputValue, nodeClass) .then((apiReturn) => { @@ -234,7 +236,10 @@ export default function GenericModal({
{type === TypeModal.PROMPT && (
-
+
{ const [nodeLength, setNodeLength] = useState( diff --git a/src/frontend/src/util/reactflowUtils.ts b/src/frontend/src/util/reactflowUtils.ts deleted file mode 100644 index da04cc75c..000000000 --- a/src/frontend/src/util/reactflowUtils.ts +++ /dev/null @@ -1,46 +0,0 @@ -import _ from "lodash"; -import { cleanEdgesType } from "./../types/utils/reactflowUtils"; - -export function cleanEdges({ - flow: { edges, nodes }, - updateEdge, -}: cleanEdgesType) { - let newEdges = _.cloneDeep(edges); - edges.forEach((edge) => { - // check if the source and target node still exists - const sourceNode = nodes.find((node) => node.id === edge.source); - const targetNode = nodes.find((node) => node.id === edge.target); - if (!sourceNode || !targetNode) { - newEdges = newEdges.filter((e) => e.id !== edge.id); - } - // check if the source and target handle still exists - if (sourceNode && targetNode) { - const sourceHandle = edge.sourceHandle; //right - const targetHandle = edge.targetHandle; //left - if (targetHandle) { - const field = targetHandle.split("|")[1]; - const id = - (targetNode.data.node.template[field]?.input_types?.join(";") ?? - targetNode.data.node.template[field]?.type) + - "|" + - field + - "|" + - targetNode.data.id; - if (id !== targetHandle) { - newEdges = newEdges.filter((e) => e.id !== edge.id); - } - } - if (sourceHandle) { - const id = [ - sourceNode.data.type, - sourceNode.data.id, - ...sourceNode.data.node.base_classes, - ].join("|"); - if (id !== sourceHandle) { - newEdges = newEdges.filter((e) => e.id !== edge.id); - } - } - } - }); - updateEdge(newEdges); -} diff --git a/src/frontend/src/utils.ts b/src/frontend/src/utils.ts deleted file mode 100644 index ab620a368..000000000 --- a/src/frontend/src/utils.ts +++ /dev/null @@ -1,1027 +0,0 @@ -import clsx, { ClassValue } from "clsx"; -import _ from "lodash"; -import { - Bell, - Check, - CheckCircle2, - ChevronDown, - ChevronLeft, - ChevronRight, - ChevronsUpDown, - Circle, - Clipboard, - Code2, - Compass, - Copy, - Cpu, - Download, - DownloadCloud, - Eraser, - ExternalLink, - File, - FileDown, - FileSearch, - FileSearch2, - FileText, - FileUp, - Fingerprint, - Gift, - GitFork, - GithubIcon, - Hammer, - HelpCircle, - Home, - Info, - Laptop2, - Layers, - Lightbulb, - Link, - Lock, - LucideSend, - Menu, - MessageCircle, - MessagesSquare, - MoonIcon, - Paperclip, - Plus, - Redo, - Rocket, - Save, - Scissors, - Search, - Settings2, - Sparkles, - SunIcon, - TerminalSquare, - Trash2, - Undo, - Upload, - Users2, - Variable, - Wand2, - Wrench, - X, - XCircle, - Zap, -} from "lucide-react"; -import { Connection, Edge, Node, ReactFlowInstance } from "reactflow"; -import { twMerge } from "tailwind-merge"; -import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./flow_constants"; -import { AirbyteIcon } from "./icons/Airbyte"; -import { AnthropicIcon } from "./icons/Anthropic"; -import { BingIcon } from "./icons/Bing"; -import { ChromaIcon } from "./icons/ChromaIcon"; -import { CohereIcon } from "./icons/Cohere"; -import { EvernoteIcon } from "./icons/Evernote"; -import { FBIcon } from "./icons/FacebookMessenger"; -import { GitBookIcon } from "./icons/GitBook"; -import { GoogleIcon } from "./icons/Google"; -import { HuggingFaceIcon } from "./icons/HuggingFace"; -import { IFixIcon } from "./icons/IFixIt"; -import { MetaIcon } from "./icons/Meta"; -import { MidjourneyIcon } from "./icons/Midjorney"; -import { MongoDBIcon } from "./icons/MongoDB"; -import { NotionIcon } from "./icons/Notion"; -import { OpenAiIcon } from "./icons/OpenAi"; -import { PineconeIcon } from "./icons/Pinecone"; -import { QDrantIcon } from "./icons/QDrant"; -import { SearxIcon } from "./icons/Searx"; -import { SlackIcon } from "./icons/Slack"; -import { VertexAIIcon } from "./icons/VertexAI"; -import { HackerNewsIcon } from "./icons/hackerNews"; -import { SupabaseIcon } from "./icons/supabase"; -import { APITemplateType } from "./types/api"; -import { IVarHighlightType } from "./types/components"; -import { FlowType, NodeType } from "./types/flow"; - -export function classNames(...classes: Array) { - return classes.filter(Boolean).join(" "); -} - -export const limitScrollFieldsModal = 10; - -export enum TypeModal { - TEXT = 1, - PROMPT = 2, -} - -export const textColors = { - white: "text-white", - red: "text-red-700", - orange: "text-orange-700", - amber: "text-amber-700", - yellow: "text-yellow-700", - lime: "text-lime-700", - green: "text-green-700", - emerald: "text-emerald-700", - teal: "text-teal-700", - cyan: "text-cyan-700", - sky: "text-sky-700", - blue: "text-blue-700", - indigo: "text-indigo-700", - violet: "text-violet-700", - purple: "text-purple-700", - fuchsia: "text-fuchsia-700", - pink: "text-pink-700", - rose: "text-rose-700", - black: "text-black-700", - gray: "text-gray-700", -}; - -export const borderLColors = { - white: "border-l-white", - red: "border-l-red-500", - orange: "border-l-orange-500", - amber: "border-l-amber-500", - yellow: "border-l-yellow-500", - lime: "border-l-lime-500", - green: "border-l-green-500", - emerald: "border-l-emerald-500", - teal: "border-l-teal-500", - cyan: "border-l-cyan-500", - sky: "border-l-sky-500", - blue: "border-l-blue-500", - indigo: "border-l-indigo-500", - violet: "border-l-violet-500", - purple: "border-l-purple-500", - fuchsia: "border-l-fuchsia-500", - pink: "border-l-pink-500", - rose: "border-l-rose-500", - black: "border-l-black-500", - gray: "border-l-gray-500", -}; - -export const nodeColors: { [char: string]: string } = { - prompts: "#4367BF", - llms: "#6344BE", - chains: "#FE7500", - agents: "#903BBE", - tools: "#FF3434", - memories: "#F5B85A", - advanced: "#000000", - chat: "#198BF6", - thought: "#272541", - embeddings: "#42BAA7", - documentloaders: "#7AAE42", - vectorstores: "#AA8742", - textsplitters: "#B47CB5", - toolkits: "#DB2C2C", - wrappers: "#E6277A", - utilities: "#31A3CC", - output_parsers: "#E6A627", - str: "#049524", - retrievers: "#e6b25a", - unknown: "#9CA3AF", -}; - -export const nodeNames: { [char: string]: string } = { - prompts: "Prompts", - llms: "LLMs", - chains: "Chains", - agents: "Agents", - tools: "Tools", - memories: "Memories", - advanced: "Advanced", - chat: "Chat", - embeddings: "Embeddings", - documentloaders: "Loaders", - vectorstores: "Vector Stores", - toolkits: "Toolkits", - wrappers: "Wrappers", - textsplitters: "Text Splitters", - retrievers: "Retrievers", - utilities: "Utilities", - output_parsers: "Output Parsers", - unknown: "Unknown", -}; - -export const nodeIconsLucide = { - Chroma: ChromaIcon, - AirbyteJSONLoader: AirbyteIcon, - Anthropic: AnthropicIcon, - ChatAnthropic: AnthropicIcon, - BingSearchAPIWrapper: BingIcon, - BingSearchRun: BingIcon, - Cohere: CohereIcon, - CohereEmbeddings: CohereIcon, - EverNoteLoader: EvernoteIcon, - FacebookChatLoader: FBIcon, - GitbookLoader: GitBookIcon, - GoogleSearchAPIWrapper: GoogleIcon, - GoogleSearchResults: GoogleIcon, - GoogleSearchRun: GoogleIcon, - HNLoader: HackerNewsIcon, - HuggingFaceHub: HuggingFaceIcon, - HuggingFaceEmbeddings: HuggingFaceIcon, - IFixitLoader: IFixIcon, - Meta: MetaIcon, - Midjorney: MidjourneyIcon, - MongoDBAtlasVectorSearch: MongoDBIcon, - NotionDirectoryLoader: NotionIcon, - ChatOpenAI: OpenAiIcon, - OpenAI: OpenAiIcon, - OpenAIEmbeddings: OpenAiIcon, - Pinecone: PineconeIcon, - Qdrant: QDrantIcon, - Searx: SearxIcon, - SlackDirectoryLoader: SlackIcon, - SupabaseVectorStore: SupabaseIcon, - VertexAI: VertexAIIcon, - ChatVertexAI: VertexAIIcon, - agents: Rocket, - chains: Link, - memories: Cpu, - llms: Lightbulb, - prompts: TerminalSquare, - tools: Wrench, - advanced: Laptop2, - chat: MessageCircle, - embeddings: Fingerprint, - documentloaders: Paperclip, - vectorstores: Layers, - toolkits: Hammer, - textsplitters: Scissors, - wrappers: Gift, - utilities: Wand2, - output_parsers: Compass, - retrievers: FileSearch, - unknown: HelpCircle, - - Trash2, - X, - XCircle, - Info, - CheckCircle2, - Zap, - MessagesSquare, - ExternalLink, - ChevronsUpDown, - Check, - Home, - Users2, - SunIcon, - MoonIcon, - Bell, - ChevronLeft, - ChevronDown, - Plus, - Redo, - Settings2, - Undo, - FileSearch2, - ChevronRight, - Circle, - Clipboard, - Code2, - Variable, - Download, - Eraser, - Lock, - LucideSend, - Sparkles, - DownloadCloud, - File, - FileText, - GitFork, - GithubIcon, - FileDown, - FileUp, - Menu, - Save, - Search, - Copy, - Upload, -}; - -export const gradients = [ - "bg-gradient-to-br from-gray-800 via-rose-700 to-violet-900", - "bg-gradient-to-br from-green-200 via-green-300 to-blue-500", - "bg-gradient-to-br from-yellow-200 via-yellow-400 to-yellow-700", - "bg-gradient-to-br from-green-200 via-green-400 to-purple-700", - "bg-gradient-to-br from-blue-100 via-blue-300 to-blue-500", - "bg-gradient-to-br from-purple-400 to-yellow-400", - "bg-gradient-to-br from-red-800 via-yellow-600 to-yellow-500", - "bg-gradient-to-br from-blue-300 via-green-200 to-yellow-300", - "bg-gradient-to-br from-blue-700 via-blue-800 to-gray-900", - "bg-gradient-to-br from-green-300 to-purple-400", - "bg-gradient-to-br from-yellow-200 via-pink-200 to-pink-400", - "bg-gradient-to-br from-green-500 to-green-700", - "bg-gradient-to-br from-rose-400 via-fuchsia-500 to-indigo-500", - "bg-gradient-to-br from-sky-400 to-blue-500", - "bg-gradient-to-br from-green-200 via-green-400 to-green-500", - "bg-gradient-to-br from-red-400 via-gray-300 to-blue-500", - "bg-gradient-to-br from-gray-900 to-gray-600 bg-gradient-to-r", - "bg-gradient-to-br from-rose-500 via-red-400 to-red-500", - "bg-gradient-to-br from-fuchsia-600 to-pink-600", - "bg-gradient-to-br from-emerald-500 to-lime-600", - "bg-gradient-to-br from-rose-500 to-indigo-700", - "bg-gradient-to-br bg-gradient-to-tr from-violet-500 to-orange-300", - "bg-gradient-to-br from-gray-900 via-purple-900 to-violet-600", - "bg-gradient-to-br from-yellow-200 via-red-500 to-fuchsia-500", - "bg-gradient-to-br from-sky-400 to-indigo-900", - "bg-gradient-to-br from-amber-200 via-violet-600 to-sky-900", - "bg-gradient-to-br from-amber-700 via-orange-300 to-rose-800", - "bg-gradient-to-br from-gray-300 via-fuchsia-600 to-orange-600", - "bg-gradient-to-br from-fuchsia-500 via-red-600 to-orange-400", - "bg-gradient-to-br from-sky-400 via-rose-400 to-lime-400", - "bg-gradient-to-br from-lime-600 via-yellow-300 to-red-600", -]; - -export const bgColors = { - white: "bg-white", - red: "bg-red-100", - orange: "bg-orange-100", - amber: "bg-amber-100", - yellow: "bg-yellow-100", - lime: "bg-lime-100", - green: "bg-green-100", - emerald: "bg-emerald-100", - teal: "bg-teal-100", - cyan: "bg-cyan-100", - sky: "bg-sky-100", - blue: "bg-blue-100", - indigo: "bg-indigo-100", - violet: "bg-violet-100", - purple: "bg-purple-100", - fuchsia: "bg-fuchsia-100", - pink: "bg-pink-100", - rose: "bg-rose-100", - black: "bg-black-100", - gray: "bg-gray-100", -}; - -export const bgColorsHover = { - white: "hover:bg-white", - black: "hover:bg-black-50", - gray: "hover:bg-gray-50", - red: "hover:bg-red-50", - orange: "hover:bg-orange-50", - amber: "hover:bg-amber-50", - yellow: "hover:bg-yellow-50", - lime: "hover:bg-lime-50", - green: "hover:bg-green-50", - emerald: "hover:bg-emerald-50", - teal: "hover:bg-teal-50", - cyan: "hover:bg-cyan-50", - sky: "hover:bg-sky-50", - blue: "hover:bg-blue-50", - indigo: "hover:bg-indigo-50", - violet: "hover:bg-violet-50", - purple: "hover:bg-purple-50", - fuchsia: "hover:bg-fuchsia-50", - pink: "hover:bg-pink-50", - rose: "hover:bg-rose-50", -}; - -export const textColorsHex = { - red: "rgb(185 28 28)", - orange: "rgb(194 65 12)", - amber: "rgb(180 83 9)", - yellow: "rgb(161 98 7)", - lime: "rgb(77 124 15)", - green: "rgb(21 128 61)", - emerald: "rgb(4 120 87)", - teal: "rgb(15 118 110)", - cyan: "rgb(14 116 144)", - sky: "rgb(3 105 161)", - blue: "rgb(29 78 216)", - indigo: "rgb(67 56 202)", - violet: "rgb(109 40 217)", - purple: "rgb(126 34 206)", - fuchsia: "rgb(162 28 175)", - pink: "rgb(190 24 93)", - rose: "rgb(190 18 60)", -}; - -export const bgColorsHex = { - red: "rgb(254 226 226)", - orange: "rgb(255 237 213)", - amber: "rgb(254 243 199)", - yellow: "rgb(254 249 195)", - lime: "rgb(236 252 203)", - green: "rgb(220 252 231)", - emerald: "rgb(209 250 229)", - teal: "rgb(204 251 241)", - cyan: "rgb(207 250 254)", - sky: "rgb(224 242 254)", - blue: "rgb(219 234 254)", - indigo: "rgb(224 231 255)", - violet: "rgb(237 233 254)", - purple: "rgb(243 232 255)", - fuchsia: "rgb(250 232 255)", - pink: "rgb(252 231 243)", - rose: "rgb(255 228 230)", -}; - -export const taskTypeMap: { [key: string]: string } = { - MULTICLASS_CLASSIFICATION: "Multiclass Classification", -}; - -const charWidths: { [char: string]: number } = { - " ": 0.2, - "!": 0.2, - '"': 0.3, - "#": 0.5, - $: 0.5, - "%": 0.5, - "&": 0.5, - "(": 0.2, - ")": 0.2, - "*": 0.5, - "+": 0.5, - ",": 0.2, - "-": 0.2, - ".": 0.1, - "/": 0.5, - ":": 0.2, - ";": 0.2, - "<": 0.5, - "=": 0.5, - ">": 0.5, - "?": 0.2, - "@": 0.5, - "[": 0.2, - "\\": 0.5, - "]": 0.2, - "^": 0.5, - _: 0.2, - "`": 0.5, - "{": 0.2, - "|": 0.2, - "}": 0.2, - "~": 0.5, -}; - -for (let i = 65; i <= 90; i++) { - charWidths[String.fromCharCode(i)] = 0.6; -} -for (let i = 97; i <= 122; i++) { - charWidths[String.fromCharCode(i)] = 0.5; -} - -export function measureTextWidth(text: string, fontSize: number) { - let wordWidth = 0; - for (let j = 0; j < text.length; j++) { - let char = text[j]; - let charWidth = charWidths[char] || 0.5; - wordWidth += charWidth * fontSize; - } - return wordWidth; -} - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); -} - -export function measureTextHeight( - text: string, - width: number, - fontSize: number -) { - const charHeight = fontSize; - const lineHeight = charHeight * 1.5; - const words = text.split(" "); - let lineWidth = 0; - let totalHeight = 0; - for (let i = 0; i < words.length; i++) { - let word = words[i]; - let wordWidth = measureTextWidth(word, fontSize); - if (lineWidth + wordWidth + charWidths[" "] * fontSize <= width) { - lineWidth += wordWidth + charWidths[" "] * fontSize; - } else { - totalHeight += lineHeight; - lineWidth = wordWidth; - } - } - totalHeight += lineHeight; - return totalHeight; -} - -export function toCamelCase(str: string) { - return str - .split(" ") - .map((word, index) => - index === 0 - ? word.toLowerCase() - : word[0].toUpperCase() + word.slice(1).toLowerCase() - ) - .join(""); -} -export function toFirstUpperCase(str: string) { - return str - .split(" ") - .map((word, index) => word[0].toUpperCase() + word.slice(1).toLowerCase()) - .join(""); -} - -export function snakeToSpaces(str: string) { - return str.split("_").join(" "); -} - -export function toNormalCase(str: string) { - let result = str - .split("_") - .map((word, index) => { - if (index === 0) { - return word[0].toUpperCase() + word.slice(1).toLowerCase(); - } - return word.toLowerCase(); - }) - .join(" "); - - return result - .split("-") - .map((word, index) => { - if (index === 0) { - return word[0].toUpperCase() + word.slice(1).toLowerCase(); - } - return word.toLowerCase(); - }) - .join(" "); -} - -export function normalCaseToSnakeCase(str: string) { - return str - .split(" ") - .map((word, index) => { - if (index === 0) { - return word[0].toUpperCase() + word.slice(1).toLowerCase(); - } - return word.toLowerCase(); - }) - .join("_"); -} - -export function roundNumber(x: number, decimals: number) { - return Math.round(x * Math.pow(10, decimals)) / Math.pow(10, decimals); -} - -export function getConnectedNodes(edge: Edge, nodes: Array): Array { - const sourceId = edge.source; - const targetId = edge.target; - return nodes.filter((node) => node.id === targetId || node.id === sourceId); -} - -export function isValidConnection( - { source, target, sourceHandle, targetHandle }: Connection, - reactFlowInstance: ReactFlowInstance -) { - if ( - targetHandle - .split("|")[0] - .split(";") - .some((n) => n === sourceHandle.split("|")[0]) || - sourceHandle - .split("|") - .slice(2) - .some((t) => - targetHandle - .split("|")[0] - .split(";") - .some((n) => n === t) - ) || - targetHandle.split("|")[0] === "str" - ) { - let targetNode = reactFlowInstance?.getNode(target)?.data?.node; - if (!targetNode) { - if ( - !reactFlowInstance - .getEdges() - .find((e) => e.targetHandle === targetHandle) - ) { - return true; - } - } else if ( - (!targetNode.template[targetHandle.split("|")[1]].list && - !reactFlowInstance - .getEdges() - .find((e) => e.targetHandle === targetHandle)) || - targetNode.template[targetHandle.split("|")[1]].list - ) { - return true; - } - } - return false; -} - -export function removeApiKeys(flow: FlowType): FlowType { - let cleanFLow = _.cloneDeep(flow); - cleanFLow.data.nodes.forEach((node) => { - for (const key in node.data.node.template) { - if (node.data.node.template[key].password) { - node.data.node.template[key].value = ""; - } - } - }); - return cleanFLow; -} - -export function updateObject>( - reference: T, - objectToUpdate: T -): T { - let clonedObject = _.cloneDeep(objectToUpdate); - // Loop through each key in the object to update - for (const key in clonedObject) { - // If the key is not in the reference object, delete it - if (!(key in reference)) { - delete clonedObject[key]; - } - } - // Loop through each key in the reference object - for (const key in reference) { - // If the key is not in the object to update, add it - if (!(key in clonedObject)) { - clonedObject[key] = reference[key]; - } - } - return clonedObject; -} - -export function debounce(func, wait) { - let timeout; - return function (...args) { - const context = this; - clearTimeout(timeout); - timeout = setTimeout(() => func.apply(context, args), wait); - }; -} - -export function updateTemplate( - reference: APITemplateType, - objectToUpdate: APITemplateType -): APITemplateType { - let clonedObject: APITemplateType = _.cloneDeep(reference); - - // Loop through each key in the reference object - for (const key in clonedObject) { - // If the key is not in the object to update, add it - if (objectToUpdate[key] && objectToUpdate[key].value) { - clonedObject[key].value = objectToUpdate[key].value; - } - if ( - objectToUpdate[key] && - objectToUpdate[key].advanced !== null && - objectToUpdate[key].advanced !== undefined - ) { - clonedObject[key].advanced = objectToUpdate[key].advanced; - } - } - return clonedObject; -} - -interface languageMap { - [key: string]: string | undefined; -} - -export const programmingLanguages: languageMap = { - javascript: ".js", - python: ".py", - java: ".java", - c: ".c", - cpp: ".cpp", - "c++": ".cpp", - "c#": ".cs", - ruby: ".rb", - php: ".php", - swift: ".swift", - "objective-c": ".m", - kotlin: ".kt", - typescript: ".ts", - go: ".go", - perl: ".pl", - rust: ".rs", - scala: ".scala", - haskell: ".hs", - lua: ".lua", - shell: ".sh", - sql: ".sql", - html: ".html", - css: ".css", - // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component -}; - -export function toTitleCase(str: string) { - let result = str - .split("_") - .map((word, index) => { - if (index === 0) { - return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() - ); - } - return checkUpperWords(word.toLowerCase()); - }) - .join(" "); - - return result - .split("-") - .map((word, index) => { - if (index === 0) { - return checkUpperWords( - word[0].toUpperCase() + word.slice(1).toLowerCase() - ); - } - return checkUpperWords(word.toLowerCase()); - }) - .join(" "); -} - -export const upperCaseWords: string[] = ["llm", "uri"]; -export function checkUpperWords(str: string) { - const words = str.split(" ").map((word) => { - return upperCaseWords.includes(word.toLowerCase()) - ? word.toUpperCase() - : word[0].toUpperCase() + word.slice(1).toLowerCase(); - }); - - return words.join(" "); -} - -export function updateIds(newFlow, getNodeId) { - let idsMap = {}; - - newFlow.nodes.forEach((n: NodeType) => { - // Generate a unique node ID - let newId = getNodeId(n.data.type); - idsMap[n.id] = newId; - n.id = newId; - n.data.id = newId; - // Add the new node to the list of nodes in state - }); - - newFlow.edges.forEach((e) => { - e.source = idsMap[e.source]; - e.target = idsMap[e.target]; - let sourceHandleSplitted = e.sourceHandle.split("|"); - e.sourceHandle = - sourceHandleSplitted[0] + - "|" + - e.source + - "|" + - sourceHandleSplitted.slice(2).join("|"); - let targetHandleSplitted = e.targetHandle.split("|"); - e.targetHandle = - targetHandleSplitted.slice(0, -1).join("|") + "|" + e.target; - e.id = - "reactflow__edge-" + - e.source + - e.sourceHandle + - "-" + - e.target + - e.targetHandle; - }); -} - -export function groupByFamily(data, baseClasses, left, type) { - let parentOutput: string; - let arrOfParent: string[] = []; - let arrOfType: { family: string; type: string; component: string }[] = []; - let arrOfLength: { length: number; type: string }[] = []; - let lastType = ""; - Object.keys(data).map((d) => { - Object.keys(data[d]).map((n) => { - try { - if ( - data[d][n].base_classes.some((r) => - baseClasses.split("\n").includes(r) - ) - ) { - arrOfParent.push(d); - } - if (n === type) { - parentOutput = d; - } - - if (d !== lastType) { - arrOfLength.push({ - length: Object.keys(data[d]).length, - type: d, - }); - - lastType = d; - } - } catch (e) { - console.log(e); - } - }); - }); - - Object.keys(data).map((d) => { - Object.keys(data[d]).map((n) => { - try { - baseClasses.split("\n").forEach((tol) => { - data[d][n].base_classes.forEach((data) => { - if (tol == data) { - arrOfType.push({ - family: d, - type: data, - component: n, - }); - } - }); - }); - } catch (e) { - console.log(e); - } - }); - }); - - if (left === false) { - let groupedBy = arrOfType.filter((object, index, self) => { - const foundIndex = self.findIndex( - (o) => o.family === object.family && o.type === object.type - ); - return foundIndex === index; - }); - - return groupedBy.reduce((result, item) => { - const existingGroup = result.find( - (group) => group.family === item.family - ); - - if (existingGroup) { - existingGroup.type += `, ${item.type}`; - } else { - result.push({ - family: item.family, - type: item.type, - component: item.component, - }); - } - - if (left === false) { - let resFil = result.filter((group) => group.family === parentOutput); - result = resFil; - } - - return result; - }, []); - } else { - const groupedArray = []; - const groupedData = {}; - - arrOfType.forEach((item) => { - const { family, type, component } = item; - const key = `${family}-${type}`; - - if (!groupedData[key]) { - groupedData[key] = { family, type, component: [component] }; - } else { - groupedData[key].component.push(component); - } - }); - - for (const key in groupedData) { - groupedArray.push(groupedData[key]); - } - - groupedArray.forEach((object, index, self) => { - const findObj = arrOfLength.find((x) => x.type === object.family); - if (object.component.length === findObj.length) { - self[index]["type"] = ""; - } else { - self[index]["type"] = object.component.join(", "); - } - }); - return groupedArray; - } -} - -export function buildInputs(tabsState, id) { - return tabsState && - tabsState[id] && - tabsState[id].formKeysData && - tabsState[id].formKeysData.input_keys && - Object.keys(tabsState[id].formKeysData.input_keys).length > 0 - ? JSON.stringify(tabsState[id].formKeysData.input_keys) - : '{"input": "message"}'; -} - -export function buildTweaks(flow) { - return flow.data.nodes.reduce((acc, node) => { - acc[node.data.id] = {}; - return acc; - }, {}); -} -export function validateNode( - n: NodeType, - reactFlowInstance: ReactFlowInstance -): Array { - if (!n.data?.node?.template || !Object.keys(n.data.node.template)) { - return [ - "We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!", - ]; - } - - const { - type, - node: { template }, - } = n.data; - - return Object.keys(template).reduce( - (errors: Array, t) => - errors.concat( - template[t].required && - template[t].show && - (template[t].value === undefined || - template[t].value === null || - template[t].value === "") && - !reactFlowInstance - .getEdges() - .some( - (e) => - e.targetHandle.split("|")[1] === t && - e.targetHandle.split("|")[2] === n.id - ) - ? [ - `${type} is missing ${ - template.display_name || toNormalCase(template[t].name) - }.`, - ] - : [] - ), - [] as string[] - ); -} - -export function validateNodes(reactFlowInstance: ReactFlowInstance) { - if (reactFlowInstance.getNodes().length === 0) { - return [ - "No nodes found in the flow. Please add at least one node to the flow.", - ]; - } - return reactFlowInstance - .getNodes() - .flatMap((n: NodeType) => validateNode(n, reactFlowInstance)); -} - -export function getRandomElement(array: T[]): T { - return array[Math.floor(Math.random() * array.length)]; -} -export function getRandomDescription(): string { - return getRandomElement(DESCRIPTIONS); -} - -export function getRandomName( - retry: number = 0, - noSpace: boolean = false, - maxRetries: number = 3 -): string { - const left: string[] = ADJECTIVES; - const right: string[] = NOUNS; - - const lv = getRandomElement(left); - const rv = getRandomElement(right); - - // Condition to avoid "boring wozniak" - if (lv === "boring" && rv === "wozniak") { - if (retry < maxRetries) { - return getRandomName(retry + 1, noSpace, maxRetries); - } else { - console.warn("Max retries reached, returning as is"); - } - } - - // Append a suffix if retrying and noSpace is true - if (retry > 0 && noSpace) { - const retrySuffix = Math.floor(Math.random() * 10); - return `${lv}_${rv}${retrySuffix}`; - } - - // Construct the final name - let final_name = noSpace ? `${lv}_${rv}` : `${lv} ${rv}`; - // Return title case final name - return toTitleCase(final_name); -} - -export function getRandomKeyByssmm(): string { - const now = new Date(); - const seconds = String(now.getSeconds()).padStart(2, "0"); - const milliseconds = String(now.getMilliseconds()).padStart(3, "0"); - return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001)); -} - -export const INVALID_CHARACTERS = [ - " ", - ",", - ".", - ":", - ";", - "!", - "?", - "/", - "\\", - "(", - ")", - "[", - "]", - "\n", -]; - -export const regexHighlight = /\{([^}]+)\}/g; - -export const varHighlightHTML = ({ name }: IVarHighlightType) => { - const html = `{${name}}`; - return html; -}; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts new file mode 100644 index 000000000..c4bec22c9 --- /dev/null +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -0,0 +1,221 @@ +import _ from "lodash"; +import { Connection, ReactFlowInstance } from "reactflow"; +import { APITemplateType } from "../types/api"; +import { FlowType, NodeType } from "../types/flow"; +import { cleanEdgesType } from "../types/utils/reactflowUtils"; +import { toNormalCase } from "./utils"; + +export function cleanEdges({ + flow: { edges, nodes }, + updateEdge, +}: cleanEdgesType) { + let newEdges = _.cloneDeep(edges); + edges.forEach((edge) => { + // check if the source and target node still exists + const sourceNode = nodes.find((node) => node.id === edge.source); + const targetNode = nodes.find((node) => node.id === edge.target); + if (!sourceNode || !targetNode) { + newEdges = newEdges.filter((e) => e.id !== edge.id); + } + // check if the source and target handle still exists + if (sourceNode && targetNode) { + const sourceHandle = edge.sourceHandle; //right + const targetHandle = edge.targetHandle; //left + if (targetHandle) { + const field = targetHandle.split("|")[1]; + const id = + (targetNode.data.node.template[field]?.input_types?.join(";") ?? + targetNode.data.node.template[field]?.type) + + "|" + + field + + "|" + + targetNode.data.id; + if (id !== targetHandle) { + newEdges = newEdges.filter((e) => e.id !== edge.id); + } + } + if (sourceHandle) { + const id = [ + sourceNode.data.type, + sourceNode.data.id, + ...sourceNode.data.node.base_classes, + ].join("|"); + if (id !== sourceHandle) { + newEdges = newEdges.filter((e) => e.id !== edge.id); + } + } + } + }); + updateEdge(newEdges); +} + +export function isValidConnection( + { source, target, sourceHandle, targetHandle }: Connection, + reactFlowInstance: ReactFlowInstance +) { + if ( + targetHandle + .split("|")[0] + .split(";") + .some((n) => n === sourceHandle.split("|")[0]) || + sourceHandle + .split("|") + .slice(2) + .some((t) => + targetHandle + .split("|")[0] + .split(";") + .some((n) => n === t) + ) || + targetHandle.split("|")[0] === "str" + ) { + let targetNode = reactFlowInstance?.getNode(target)?.data?.node; + if (!targetNode) { + if ( + !reactFlowInstance + .getEdges() + .find((e) => e.targetHandle === targetHandle) + ) { + return true; + } + } else if ( + (!targetNode.template[targetHandle.split("|")[1]].list && + !reactFlowInstance + .getEdges() + .find((e) => e.targetHandle === targetHandle)) || + targetNode.template[targetHandle.split("|")[1]].list + ) { + return true; + } + } + return false; +} + +export function removeApiKeys(flow: FlowType): FlowType { + let cleanFLow = _.cloneDeep(flow); + cleanFLow.data.nodes.forEach((node) => { + for (const key in node.data.node.template) { + if (node.data.node.template[key].password) { + node.data.node.template[key].value = ""; + } + } + }); + return cleanFLow; +} + +export function updateTemplate( + reference: APITemplateType, + objectToUpdate: APITemplateType +): APITemplateType { + let clonedObject: APITemplateType = _.cloneDeep(reference); + + // Loop through each key in the reference object + for (const key in clonedObject) { + // If the key is not in the object to update, add it + if (objectToUpdate[key] && objectToUpdate[key].value) { + clonedObject[key].value = objectToUpdate[key].value; + } + if ( + objectToUpdate[key] && + objectToUpdate[key].advanced !== null && + objectToUpdate[key].advanced !== undefined + ) { + clonedObject[key].advanced = objectToUpdate[key].advanced; + } + } + return clonedObject; +} + +export function updateIds(newFlow, getNodeId) { + let idsMap = {}; + + newFlow.nodes.forEach((n: NodeType) => { + // Generate a unique node ID + let newId = getNodeId(n.data.type); + idsMap[n.id] = newId; + n.id = newId; + n.data.id = newId; + // Add the new node to the list of nodes in state + }); + + newFlow.edges.forEach((e) => { + e.source = idsMap[e.source]; + e.target = idsMap[e.target]; + let sourceHandleSplitted = e.sourceHandle.split("|"); + e.sourceHandle = + sourceHandleSplitted[0] + + "|" + + e.source + + "|" + + sourceHandleSplitted.slice(2).join("|"); + let targetHandleSplitted = e.targetHandle.split("|"); + e.targetHandle = + targetHandleSplitted.slice(0, -1).join("|") + "|" + e.target; + e.id = + "reactflow__edge-" + + e.source + + e.sourceHandle + + "-" + + e.target + + e.targetHandle; + }); +} + +export function buildTweaks(flow) { + return flow.data.nodes.reduce((acc, node) => { + acc[node.data.id] = {}; + return acc; + }, {}); +} + +export function validateNode( + n: NodeType, + reactFlowInstance: ReactFlowInstance +): Array { + if (!n.data?.node?.template || !Object.keys(n.data.node.template)) { + return [ + "We've noticed a potential issue with a node in the flow. Please review it and, if necessary, submit a bug report with your exported flow file. Thank you for your help!", + ]; + } + + const { + type, + node: { template }, + } = n.data; + + return Object.keys(template).reduce( + (errors: Array, t) => + errors.concat( + template[t].required && + template[t].show && + (template[t].value === undefined || + template[t].value === null || + template[t].value === "") && + !reactFlowInstance + .getEdges() + .some( + (e) => + e.targetHandle.split("|")[1] === t && + e.targetHandle.split("|")[2] === n.id + ) + ? [ + `${type} is missing ${ + template.display_name || toNormalCase(template[t].name) + }.`, + ] + : [] + ), + [] as string[] + ); +} + +export function validateNodes(reactFlowInstance: ReactFlowInstance) { + if (reactFlowInstance.getNodes().length === 0) { + return [ + "No nodes found in the flow. Please add at least one node to the flow.", + ]; + } + return reactFlowInstance + .getNodes() + .flatMap((n: NodeType) => validateNode(n, reactFlowInstance)); +} diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts new file mode 100644 index 000000000..1a5a0bbfd --- /dev/null +++ b/src/frontend/src/utils/styleUtils.ts @@ -0,0 +1,268 @@ +import { + Bell, + Check, + CheckCircle2, + ChevronDown, + ChevronLeft, + ChevronRight, + ChevronsUpDown, + Circle, + Clipboard, + Code2, + Compass, + Copy, + Cpu, + Download, + DownloadCloud, + Eraser, + ExternalLink, + File, + FileDown, + FileSearch, + FileSearch2, + FileText, + FileUp, + Fingerprint, + Gift, + GitFork, + GithubIcon, + Hammer, + HelpCircle, + Home, + Info, + Laptop2, + Layers, + Lightbulb, + Link, + Lock, + LucideSend, + Menu, + MessageCircle, + MessagesSquare, + MoonIcon, + Paperclip, + Plus, + Redo, + Rocket, + Save, + Scissors, + Search, + Settings2, + SlackIcon, + Sparkles, + SunIcon, + TerminalSquare, + Trash2, + Undo, + Upload, + Users2, + Variable, + Wand2, + Wrench, + X, + XCircle, + Zap, +} from "lucide-react"; +import { Edge, Node } from "reactflow"; +import { AirbyteIcon } from "../icons/Airbyte"; +import { AnthropicIcon } from "../icons/Anthropic"; +import { BingIcon } from "../icons/Bing"; +import { ChromaIcon } from "../icons/ChromaIcon"; +import { CohereIcon } from "../icons/Cohere"; +import { EvernoteIcon } from "../icons/Evernote"; +import { FBIcon } from "../icons/FacebookMessenger"; +import { GitBookIcon } from "../icons/GitBook"; +import { GoogleIcon } from "../icons/Google"; +import { HuggingFaceIcon } from "../icons/HuggingFace"; +import { IFixIcon } from "../icons/IFixIt"; +import { MetaIcon } from "../icons/Meta"; +import { MidjourneyIcon } from "../icons/Midjorney"; +import { MongoDBIcon } from "../icons/MongoDB"; +import { NotionIcon } from "../icons/Notion"; +import { OpenAiIcon } from "../icons/OpenAi"; +import { PineconeIcon } from "../icons/Pinecone"; +import { QDrantIcon } from "../icons/QDrant"; +import { SearxIcon } from "../icons/Searx"; +import { VertexAIIcon } from "../icons/VertexAI"; +import { HackerNewsIcon } from "../icons/hackerNews"; +import { SupabaseIcon } from "../icons/supabase"; + +export const gradients = [ + "bg-gradient-to-br from-gray-800 via-rose-700 to-violet-900", + "bg-gradient-to-br from-green-200 via-green-300 to-blue-500", + "bg-gradient-to-br from-yellow-200 via-yellow-400 to-yellow-700", + "bg-gradient-to-br from-green-200 via-green-400 to-purple-700", + "bg-gradient-to-br from-blue-100 via-blue-300 to-blue-500", + "bg-gradient-to-br from-purple-400 to-yellow-400", + "bg-gradient-to-br from-red-800 via-yellow-600 to-yellow-500", + "bg-gradient-to-br from-blue-300 via-green-200 to-yellow-300", + "bg-gradient-to-br from-blue-700 via-blue-800 to-gray-900", + "bg-gradient-to-br from-green-300 to-purple-400", + "bg-gradient-to-br from-yellow-200 via-pink-200 to-pink-400", + "bg-gradient-to-br from-green-500 to-green-700", + "bg-gradient-to-br from-rose-400 via-fuchsia-500 to-indigo-500", + "bg-gradient-to-br from-sky-400 to-blue-500", + "bg-gradient-to-br from-green-200 via-green-400 to-green-500", + "bg-gradient-to-br from-red-400 via-gray-300 to-blue-500", + "bg-gradient-to-br from-gray-900 to-gray-600 bg-gradient-to-r", + "bg-gradient-to-br from-rose-500 via-red-400 to-red-500", + "bg-gradient-to-br from-fuchsia-600 to-pink-600", + "bg-gradient-to-br from-emerald-500 to-lime-600", + "bg-gradient-to-br from-rose-500 to-indigo-700", + "bg-gradient-to-br bg-gradient-to-tr from-violet-500 to-orange-300", + "bg-gradient-to-br from-gray-900 via-purple-900 to-violet-600", + "bg-gradient-to-br from-yellow-200 via-red-500 to-fuchsia-500", + "bg-gradient-to-br from-sky-400 to-indigo-900", + "bg-gradient-to-br from-amber-200 via-violet-600 to-sky-900", + "bg-gradient-to-br from-amber-700 via-orange-300 to-rose-800", + "bg-gradient-to-br from-gray-300 via-fuchsia-600 to-orange-600", + "bg-gradient-to-br from-fuchsia-500 via-red-600 to-orange-400", + "bg-gradient-to-br from-sky-400 via-rose-400 to-lime-400", + "bg-gradient-to-br from-lime-600 via-yellow-300 to-red-600", +]; + +export const nodeColors: { [char: string]: string } = { + prompts: "#4367BF", + llms: "#6344BE", + chains: "#FE7500", + agents: "#903BBE", + tools: "#FF3434", + memories: "#F5B85A", + advanced: "#000000", + chat: "#198BF6", + thought: "#272541", + embeddings: "#42BAA7", + documentloaders: "#7AAE42", + vectorstores: "#AA8742", + textsplitters: "#B47CB5", + toolkits: "#DB2C2C", + wrappers: "#E6277A", + utilities: "#31A3CC", + output_parsers: "#E6A627", + str: "#049524", + retrievers: "#e6b25a", + unknown: "#9CA3AF", +}; + +export const nodeNames: { [char: string]: string } = { + prompts: "Prompts", + llms: "LLMs", + chains: "Chains", + agents: "Agents", + tools: "Tools", + memories: "Memories", + advanced: "Advanced", + chat: "Chat", + embeddings: "Embeddings", + documentloaders: "Loaders", + vectorstores: "Vector Stores", + toolkits: "Toolkits", + wrappers: "Wrappers", + textsplitters: "Text Splitters", + retrievers: "Retrievers", + utilities: "Utilities", + output_parsers: "Output Parsers", + unknown: "Unknown", +}; + +export const nodeIconsLucide = { + Chroma: ChromaIcon, + AirbyteJSONLoader: AirbyteIcon, + Anthropic: AnthropicIcon, + ChatAnthropic: AnthropicIcon, + BingSearchAPIWrapper: BingIcon, + BingSearchRun: BingIcon, + Cohere: CohereIcon, + CohereEmbeddings: CohereIcon, + EverNoteLoader: EvernoteIcon, + FacebookChatLoader: FBIcon, + GitbookLoader: GitBookIcon, + GoogleSearchAPIWrapper: GoogleIcon, + GoogleSearchResults: GoogleIcon, + GoogleSearchRun: GoogleIcon, + HNLoader: HackerNewsIcon, + HuggingFaceHub: HuggingFaceIcon, + HuggingFaceEmbeddings: HuggingFaceIcon, + IFixitLoader: IFixIcon, + Meta: MetaIcon, + Midjorney: MidjourneyIcon, + MongoDBAtlasVectorSearch: MongoDBIcon, + NotionDirectoryLoader: NotionIcon, + ChatOpenAI: OpenAiIcon, + OpenAI: OpenAiIcon, + OpenAIEmbeddings: OpenAiIcon, + Pinecone: PineconeIcon, + Qdrant: QDrantIcon, + Searx: SearxIcon, + SlackDirectoryLoader: SlackIcon, + SupabaseVectorStore: SupabaseIcon, + VertexAI: VertexAIIcon, + ChatVertexAI: VertexAIIcon, + agents: Rocket, + chains: Link, + memories: Cpu, + llms: Lightbulb, + prompts: TerminalSquare, + tools: Wrench, + advanced: Laptop2, + chat: MessageCircle, + embeddings: Fingerprint, + documentloaders: Paperclip, + vectorstores: Layers, + toolkits: Hammer, + textsplitters: Scissors, + wrappers: Gift, + utilities: Wand2, + output_parsers: Compass, + retrievers: FileSearch, + unknown: HelpCircle, + Trash2, + X, + XCircle, + Info, + CheckCircle2, + Zap, + MessagesSquare, + ExternalLink, + ChevronsUpDown, + Check, + Home, + Users2, + SunIcon, + MoonIcon, + Bell, + ChevronLeft, + ChevronDown, + Plus, + Redo, + Settings2, + Undo, + FileSearch2, + ChevronRight, + Circle, + Clipboard, + Code2, + Variable, + Download, + Eraser, + Lock, + LucideSend, + Sparkles, + DownloadCloud, + File, + FileText, + GitFork, + GithubIcon, + FileDown, + FileUp, + Menu, + Save, + Search, + Copy, + Upload, +}; +export function getConnectedNodes(edge: Edge, nodes: Array): Array { + const sourceId = edge.source; + const targetId = edge.target; + return nodes.filter((node) => node.id === targetId || node.id === sourceId); +} diff --git a/src/frontend/src/utils/utils.ts b/src/frontend/src/utils/utils.ts new file mode 100644 index 000000000..37e35b643 --- /dev/null +++ b/src/frontend/src/utils/utils.ts @@ -0,0 +1,253 @@ +import clsx, { ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; +import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "../flow_constants"; + +export function classNames(...classes: Array) { + return classes.filter(Boolean).join(" "); +} + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +export function toNormalCase(str: string) { + let result = str + .split("_") + .map((word, index) => { + if (index === 0) { + return word[0].toUpperCase() + word.slice(1).toLowerCase(); + } + return word.toLowerCase(); + }) + .join(" "); + + return result + .split("-") + .map((word, index) => { + if (index === 0) { + return word[0].toUpperCase() + word.slice(1).toLowerCase(); + } + return word.toLowerCase(); + }) + .join(" "); +} + +export function normalCaseToSnakeCase(str: string) { + return str + .split(" ") + .map((word, index) => { + if (index === 0) { + return word[0].toUpperCase() + word.slice(1).toLowerCase(); + } + return word.toLowerCase(); + }) + .join("_"); +} + +export function toTitleCase(str: string) { + let result = str + .split("_") + .map((word, index) => { + if (index === 0) { + return checkUpperWords( + word[0].toUpperCase() + word.slice(1).toLowerCase() + ); + } + return checkUpperWords(word.toLowerCase()); + }) + .join(" "); + + return result + .split("-") + .map((word, index) => { + if (index === 0) { + return checkUpperWords( + word[0].toUpperCase() + word.slice(1).toLowerCase() + ); + } + return checkUpperWords(word.toLowerCase()); + }) + .join(" "); +} + +export const upperCaseWords: string[] = ["llm", "uri"]; +export function checkUpperWords(str: string) { + const words = str.split(" ").map((word) => { + return upperCaseWords.includes(word.toLowerCase()) + ? word.toUpperCase() + : word[0].toUpperCase() + word.slice(1).toLowerCase(); + }); + + return words.join(" "); +} + +export function groupByFamily(data, baseClasses, left, type) { + let parentOutput: string; + let arrOfParent: string[] = []; + let arrOfType: { family: string; type: string; component: string }[] = []; + let arrOfLength: { length: number; type: string }[] = []; + let lastType = ""; + Object.keys(data).map((d) => { + Object.keys(data[d]).map((n) => { + try { + if ( + data[d][n].base_classes.some((r) => + baseClasses.split("\n").includes(r) + ) + ) { + arrOfParent.push(d); + } + if (n === type) { + parentOutput = d; + } + + if (d !== lastType) { + arrOfLength.push({ + length: Object.keys(data[d]).length, + type: d, + }); + + lastType = d; + } + } catch (e) { + console.log(e); + } + }); + }); + + Object.keys(data).map((d) => { + Object.keys(data[d]).map((n) => { + try { + baseClasses.split("\n").forEach((tol) => { + data[d][n].base_classes.forEach((data) => { + if (tol == data) { + arrOfType.push({ + family: d, + type: data, + component: n, + }); + } + }); + }); + } catch (e) { + console.log(e); + } + }); + }); + + if (left === false) { + let groupedBy = arrOfType.filter((object, index, self) => { + const foundIndex = self.findIndex( + (o) => o.family === object.family && o.type === object.type + ); + return foundIndex === index; + }); + + return groupedBy.reduce((result, item) => { + const existingGroup = result.find( + (group) => group.family === item.family + ); + + if (existingGroup) { + existingGroup.type += `, ${item.type}`; + } else { + result.push({ + family: item.family, + type: item.type, + component: item.component, + }); + } + + if (left === false) { + let resFil = result.filter((group) => group.family === parentOutput); + result = resFil; + } + + return result; + }, []); + } else { + const groupedArray = []; + const groupedData = {}; + + arrOfType.forEach((item) => { + const { family, type, component } = item; + const key = `${family}-${type}`; + + if (!groupedData[key]) { + groupedData[key] = { family, type, component: [component] }; + } else { + groupedData[key].component.push(component); + } + }); + + for (const key in groupedData) { + groupedArray.push(groupedData[key]); + } + + groupedArray.forEach((object, index, self) => { + const findObj = arrOfLength.find((x) => x.type === object.family); + if (object.component.length === findObj.length) { + self[index]["type"] = ""; + } else { + self[index]["type"] = object.component.join(", "); + } + }); + return groupedArray; + } +} + +export function buildInputs(tabsState, id) { + return tabsState && + tabsState[id] && + tabsState[id].formKeysData && + tabsState[id].formKeysData.input_keys && + Object.keys(tabsState[id].formKeysData.input_keys).length > 0 + ? JSON.stringify(tabsState[id].formKeysData.input_keys) + : '{"input": "message"}'; +} + +export function getRandomElement(array: T[]): T { + return array[Math.floor(Math.random() * array.length)]; +} +export function getRandomDescription(): string { + return getRandomElement(DESCRIPTIONS); +} + +export function getRandomName( + retry: number = 0, + noSpace: boolean = false, + maxRetries: number = 3 +): string { + const left: string[] = ADJECTIVES; + const right: string[] = NOUNS; + + const lv = getRandomElement(left); + const rv = getRandomElement(right); + + // Condition to avoid "boring wozniak" + if (lv === "boring" && rv === "wozniak") { + if (retry < maxRetries) { + return getRandomName(retry + 1, noSpace, maxRetries); + } else { + console.warn("Max retries reached, returning as is"); + } + } + + // Append a suffix if retrying and noSpace is true + if (retry > 0 && noSpace) { + const retrySuffix = Math.floor(Math.random() * 10); + return `${lv}_${rv}${retrySuffix}`; + } + + // Construct the final name + let final_name = noSpace ? `${lv}_${rv}` : `${lv} ${rv}`; + // Return title case final name + return toTitleCase(final_name); +} + +export function getRandomKeyByssmm(): string { + const now = new Date(); + const seconds = String(now.getSeconds()).padStart(2, "0"); + const milliseconds = String(now.getMilliseconds()).padStart(3, "0"); + return seconds + milliseconds + Math.abs(Math.floor(Math.random() * 10001)); +}