Merge branch 'refactorUtils' into generic_icon_component

This commit is contained in:
anovazzi1 2023-07-18 12:51:08 -03:00
commit 17c6c7e528
50 changed files with 910 additions and 1160 deletions

View file

@ -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,

View file

@ -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({

View file

@ -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;

View file

@ -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,

View file

@ -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";

View file

@ -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({

View file

@ -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,

View file

@ -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({

View file

@ -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({

View file

@ -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,

View file

@ -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;

View file

@ -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",

View file

@ -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",

View file

@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../utils";
import { cn } from "../../utils/utils";
const Card = React.forwardRef<
HTMLDivElement,

View file

@ -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<

View file

@ -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;

View file

@ -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;

View file

@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../utils";
import { cn } from "../../utils/utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}

View file

@ -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"

View file

@ -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;

View file

@ -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<typeof ProgressPrimitive.Root>,

View file

@ -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);

View file

@ -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<typeof SeparatorPrimitive.Root>,

View file

@ -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<typeof SwitchPrimitives.Root>,

View file

@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../utils";
import { cn } from "../../utils/utils";
const Table = React.forwardRef<
HTMLTableElement,

View file

@ -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;

View file

@ -1,5 +1,5 @@
import * as React from "react";
import { cn } from "../../utils";
import { cn } from "../../utils/utils";
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}

View file

@ -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;

View file

@ -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 = `<span class="font-semibold chat-message-highlight">{${name}}</span>`;
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

View file

@ -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";

View file

@ -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);

View file

@ -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);

View file

@ -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,

View file

@ -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 }) {

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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,

View file

@ -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,

View file

@ -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({
<div className="mb-auto flex-1">
{type === TypeModal.PROMPT && (
<div className=" mr-2">
<div ref={divRef} className="max-h-20 overflow-y-auto custom-scroll">
<div
ref={divRef}
className="max-h-20 overflow-y-auto custom-scroll"
>
<div className="flex flex-wrap items-center">
<IconComponent
name="Variable"

View file

@ -1,5 +1,5 @@
import { ReactNode } from "react";
import { classNames } from "../../../utils";
import { classNames } from "../../../utils/utils";
export default function ButtonBox({
onClick,

View file

@ -21,7 +21,7 @@ import { PopUpContext } from "../../contexts/popUpContext";
import { TabsContext } from "../../contexts/tabsContext";
import { getExamples } from "../../controllers/API";
import { FlowType } from "../../types/flow";
import { classNames } from "../../utils";
import { classNames } from "../../utils/utils";
import ButtonBox from "./buttonBox";
export default function ImportModal() {

View file

@ -26,7 +26,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType } from "../../../../types/flow";
import { isValidConnection } from "../../../../utils";
import { isValidConnection } from "../../../../utils/reactflowUtils";
import ConnectionLineComponent from "../ConnectionLineComponent";
import ExtraSidebar from "../extraSidebarComponent";

View file

@ -11,11 +11,11 @@ import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal";
import { APIClassType, APIObjectType } from "../../../../types/api";
import {
classNames,
nodeColors,
nodeIconsLucide,
nodeNames,
} from "../../../../utils";
} from "../../../../utils/styleUtils";
import { classNames } from "../../../../utils/utils";
import DisclosureComponent from "../DisclosureComponent";
export default function ExtraSidebar() {

View file

@ -4,7 +4,7 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { TabsContext } from "../../../../contexts/tabsContext";
import EditNodeModal from "../../../../modals/EditNodeModal";
import { classNames } from "../../../../utils";
import { classNames } from "../../../../utils/utils";
const NodeToolbarComponent = (props) => {
const [nodeLength, setNodeLength] = useState(

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -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<string> {
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<string>, 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));
}

View file

@ -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<Node>): Array<Node> {
const sourceId = edge.source;
const targetId = edge.target;
return nodes.filter((node) => node.id === targetId || node.id === sourceId);
}

View file

@ -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<string>) {
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<T>(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));
}