merged chat_and_cache into dev
This commit is contained in:
commit
8a169c9782
35 changed files with 1632 additions and 966 deletions
|
|
@ -15,6 +15,11 @@ CUSTOM_NODES = {
|
|||
"utilities": {
|
||||
"SQLDatabase": nodes.SQLDatabaseNode(),
|
||||
},
|
||||
"chains": {
|
||||
"SeriesCharacterChain": nodes.SeriesCharacterChainNode(),
|
||||
"TimeTravelGuideChain": nodes.TimeTravelGuideChainNode(),
|
||||
"MidJourneyPromptChain": nodes.MidJourneyPromptChainNode(),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class SQLAgent(AgentExecutor):
|
|||
def from_toolkit_and_llm(cls, llm: BaseLLM, database_uri: str, **kwargs: Any):
|
||||
"""Construct a sql agent from an LLM and tools."""
|
||||
db = SQLDatabase.from_uri(database_uri)
|
||||
toolkit = SQLDatabaseToolkit(db=db)
|
||||
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
|
||||
|
||||
# The right code should be this, but there is a problem with tools = toolkit.get_tools()
|
||||
# related to `OPENAI_API_KEY`
|
||||
|
|
@ -274,7 +274,11 @@ class InitializeAgent(AgentExecutor):
|
|||
|
||||
@classmethod
|
||||
def initialize(
|
||||
cls, llm: BaseLLM, tools: List[Tool], agent: str, memory: BaseChatMemory
|
||||
cls,
|
||||
llm: BaseLLM,
|
||||
tools: List[Tool],
|
||||
agent: str,
|
||||
memory: Optional[BaseChatMemory] = None,
|
||||
):
|
||||
return initialize_agent(
|
||||
tools=tools,
|
||||
|
|
|
|||
|
|
@ -186,7 +186,9 @@ def load_agent_executor(agent_class: type[agent_module.Agent], params, **kwargs)
|
|||
allowed_tools = params["allowed_tools"]
|
||||
llm_chain = params["llm_chain"]
|
||||
tool_names = [tool.name for tool in allowed_tools]
|
||||
agent = agent_class(allowed_tools=tool_names, llm_chain=llm_chain)
|
||||
# Agent class requires an output_parser but Agent classes
|
||||
# have a default output_parser.
|
||||
agent = agent_class(allowed_tools=tool_names, llm_chain=llm_chain) # type: ignore
|
||||
return AgentExecutor.from_agent_and_tools(
|
||||
agent=agent,
|
||||
tools=allowed_tools,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import contextlib
|
||||
import io
|
||||
from typing import Any, Dict
|
||||
from chromadb.errors import NotEnoughElementsException
|
||||
from chromadb.errors import NotEnoughElementsException # type: ignore
|
||||
|
||||
from langflow.cache.base import compute_dict_hash, load_cache, memoize_dict
|
||||
from langflow.graph.graph import Graph
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class TemplateFieldCreator(BaseModel, ABC):
|
|||
options: list[str] = []
|
||||
name: str = ""
|
||||
display_name: Optional[str] = None
|
||||
advanced: bool = True
|
||||
|
||||
def to_dict(self):
|
||||
result = self.dict()
|
||||
|
|
@ -225,6 +226,19 @@ class FrontendNode(BaseModel):
|
|||
field.is_list = True
|
||||
if "api_key" in key and "OpenAI" in str(name):
|
||||
field.display_name = "OpenAI API Key"
|
||||
field.required = True
|
||||
field.required = False
|
||||
if field.value is None:
|
||||
field.value = ""
|
||||
# If the field.name contains api or api and key, then it might be an api key
|
||||
# other conditions are to make sure that it is not an input or output variable
|
||||
if "api" in key.lower() and "key" in key.lower():
|
||||
field.required = False
|
||||
|
||||
if "kwargs" in field.name.lower():
|
||||
field.advanced = True
|
||||
field.required = False
|
||||
field.show = False
|
||||
# If the field.name contains api or api and key, then it might be an api key
|
||||
# other conditions are to make sure that it is not an input or output variable
|
||||
if "api" in key.lower() and "key" in key.lower():
|
||||
field.required = False
|
||||
|
|
|
|||
|
|
@ -101,6 +101,107 @@ class PythonFunctionNode(FrontendNode):
|
|||
return super().to_dict()
|
||||
|
||||
|
||||
class MidJourneyPromptChainNode(FrontendNode):
|
||||
name: str = "MidJourneyPromptChain"
|
||||
template: Template = Template(
|
||||
type_name="MidJourneyPromptChain",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="BaseLanguageModel",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
multiline=False,
|
||||
name="llm",
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = "MidJourneyPromptChain is a chain you can use to generate new MidJourney prompts."
|
||||
base_classes: list[str] = [
|
||||
"LLMChain",
|
||||
"BaseCustomChain",
|
||||
"Chain",
|
||||
"ConversationChain",
|
||||
"MidJourneyPromptChain",
|
||||
]
|
||||
|
||||
|
||||
class TimeTravelGuideChainNode(FrontendNode):
|
||||
name: str = "TimeTravelGuideChain"
|
||||
template: Template = Template(
|
||||
type_name="TimeTravelGuideChain",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="BaseLanguageModel",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
multiline=False,
|
||||
name="llm",
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = "Time travel guide chain to be used in the flow."
|
||||
base_classes: list[str] = [
|
||||
"LLMChain",
|
||||
"BaseCustomChain",
|
||||
"TimeTravelGuideChain",
|
||||
"Chain",
|
||||
"ConversationChain",
|
||||
]
|
||||
|
||||
|
||||
class SeriesCharacterChainNode(FrontendNode):
|
||||
name: str = "SeriesCharacterChain"
|
||||
template: Template = Template(
|
||||
type_name="SeriesCharacterChain",
|
||||
fields=[
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
multiline=False,
|
||||
name="character",
|
||||
),
|
||||
TemplateField(
|
||||
field_type="str",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
multiline=False,
|
||||
name="series",
|
||||
),
|
||||
TemplateField(
|
||||
field_type="BaseLanguageModel",
|
||||
required=True,
|
||||
placeholder="",
|
||||
is_list=False,
|
||||
show=True,
|
||||
advanced=False,
|
||||
multiline=False,
|
||||
name="llm",
|
||||
),
|
||||
],
|
||||
)
|
||||
description: str = "SeriesCharacterChain is a chain you can use to have a conversation with a character from a series." # noqa
|
||||
base_classes: list[str] = [
|
||||
"LLMChain",
|
||||
"BaseCustomChain",
|
||||
"Chain",
|
||||
"ConversationChain",
|
||||
"SeriesCharacterChain",
|
||||
]
|
||||
|
||||
|
||||
class ToolNode(FrontendNode):
|
||||
name: str = "Tool"
|
||||
template: Template = Template(
|
||||
|
|
@ -418,17 +519,29 @@ class ChainFrontendNode(FrontendNode):
|
|||
def format_field(field: TemplateField, name: Optional[str] = None) -> None:
|
||||
FrontendNode.format_field(field, name)
|
||||
|
||||
field.advanced = False
|
||||
if "key" in field.name:
|
||||
field.password = False
|
||||
field.show = False
|
||||
if field.name in ["input_key", "output_key"]:
|
||||
field.required = True
|
||||
field.show = True
|
||||
field.advanced = True
|
||||
|
||||
# Separated for possible future changes
|
||||
if field.name == "prompt" and field.value is None:
|
||||
# if no prompt is provided, use the default prompt
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
if field.name == "memory":
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = False
|
||||
if field.name == "verbose":
|
||||
field.required = False
|
||||
field.show = True
|
||||
field.advanced = True
|
||||
|
||||
|
||||
class LLMFrontendNode(FrontendNode):
|
||||
|
|
@ -438,22 +551,31 @@ class LLMFrontendNode(FrontendNode):
|
|||
"huggingfacehub_api_token": "HuggingFace Hub API Token",
|
||||
}
|
||||
FrontendNode.format_field(field, name)
|
||||
SHOW_FIELDS = ["repo_id", "task", "model_kwargs"]
|
||||
SHOW_FIELDS = ["repo_id"]
|
||||
if field.name in SHOW_FIELDS:
|
||||
field.show = True
|
||||
|
||||
if "api" in field.name and ("key" in field.name or "token" in field.name):
|
||||
field.password = True
|
||||
field.show = True
|
||||
field.required = True
|
||||
# Required should be False to support
|
||||
# loading the API key from environment variables
|
||||
field.required = False
|
||||
field.advanced = False
|
||||
|
||||
if field.name == "task":
|
||||
field.required = True
|
||||
field.show = True
|
||||
field.is_list = True
|
||||
field.options = ["text-generation", "text2text-generation"]
|
||||
field.advanced = True
|
||||
|
||||
if display_name := display_names_dict.get(field.name):
|
||||
field.display_name = display_name
|
||||
if field.name == "model_kwargs":
|
||||
field.field_type = "code"
|
||||
field.advanced = True
|
||||
field.show = True
|
||||
elif field.name in ["model_name", "temperature"]:
|
||||
field.advanced = False
|
||||
field.show = True
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { Cog6ToothIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import {
|
||||
classNames,
|
||||
|
|
@ -11,6 +11,8 @@ import { typesContext } from "../../contexts/typesContext";
|
|||
import { useContext, useState, useEffect, useRef } from "react";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import NodeModal from "../../modals/NodeModal";
|
||||
import { useCallback } from "react";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
export default function GenericNode({
|
||||
|
|
@ -23,6 +25,7 @@ export default function GenericNode({
|
|||
const { setErrorData } = useContext(alertContext);
|
||||
const showError = useRef(true);
|
||||
const { types, deleteNode } = useContext(typesContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const Icon = nodeIcons[types[data.type]];
|
||||
const [validationStatus, setValidationStatus] = useState("idle");
|
||||
// State for outline color
|
||||
|
|
@ -101,17 +104,46 @@ export default function GenericNode({
|
|||
/>
|
||||
<div className="truncate">{data.type}</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
deleteNode(data.id);
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-6 h-6 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-500"></TrashIcon>
|
||||
</button>
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
className="relative"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
openPopUp(<NodeModal data={data} />);
|
||||
}}
|
||||
>
|
||||
<div className=" absolute text-red-600 -top-2 -right-1">
|
||||
{Object.keys(data.node.template).some(
|
||||
(t) =>
|
||||
data.node.template[t].advanced &&
|
||||
data.node.template[t].required
|
||||
)
|
||||
? " *"
|
||||
: ""}
|
||||
</div>
|
||||
<Cog6ToothIcon
|
||||
className={classNames(
|
||||
Object.keys(data.node.template).some(
|
||||
(t) => data.node.template[t].advanced && data.node.template[t].show
|
||||
)
|
||||
? ""
|
||||
: "hidden",
|
||||
"w-6 h-6 dark:text-gray-500 hover:animate-spin"
|
||||
)}
|
||||
></Cog6ToothIcon>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
deleteNode(data.id);
|
||||
}}
|
||||
>
|
||||
<TrashIcon className="w-6 h-6 hover:text-red-500 dark:text-gray-500 dark:hover:text-red-500"></TrashIcon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h-full py-5">
|
||||
<div className="w-full text-gray-500 px-5 text-sm">
|
||||
<div className="w-full text-gray-500 px-5 pb-3 text-sm">
|
||||
{data.node.description}
|
||||
</div>
|
||||
|
||||
|
|
@ -120,7 +152,7 @@ export default function GenericNode({
|
|||
.filter((t) => t.charAt(0) !== "_")
|
||||
.map((t: string, idx) => (
|
||||
<div key={idx}>
|
||||
{idx === 0 ? (
|
||||
{/* {idx === 0 ? (
|
||||
<div
|
||||
className={classNames(
|
||||
"px-5 py-2 mt-2 dark:text-white text-center",
|
||||
|
|
@ -138,8 +170,8 @@ export default function GenericNode({
|
|||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{data.node.template[t].show ? (
|
||||
)} */}
|
||||
{data.node.template[t].show && !data.node.template[t].advanced ? (
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={
|
||||
|
|
@ -169,9 +201,17 @@ export default function GenericNode({
|
|||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
Output
|
||||
<div
|
||||
className={classNames(
|
||||
Object.keys(data.node.template).length < 1 ? "hidden" : "",
|
||||
"w-full flex justify-center"
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
</div>
|
||||
{/* <div className="px-5 py-2 mt-2 dark:text-white text-center">
|
||||
Output
|
||||
</div> */}
|
||||
<ParameterComponent
|
||||
data={data}
|
||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,24 @@ export default function CodeAreaComponent({
|
|||
}
|
||||
}, [disabled, onChange]);
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed w-full" : "w-full"}>
|
||||
<div
|
||||
className={
|
||||
disabled ? "pointer-events-none cursor-not-allowed w-full" : "w-full"
|
||||
}
|
||||
>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<span
|
||||
onClick={() => {
|
||||
openPopUp(
|
||||
<CodeAreaModal
|
||||
value={myValue}
|
||||
setValue={(t: string) => {
|
||||
setMyValue(t);
|
||||
onChange(t);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
className={
|
||||
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
|
||||
(disabled ? " bg-gray-200" : "")
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export default function InputFileComponent({
|
|||
>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<span
|
||||
onClick={handleButtonClick}
|
||||
className={
|
||||
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
|
||||
(disabled ? " bg-gray-200" : "")
|
||||
|
|
|
|||
|
|
@ -6,30 +6,61 @@ import TextAreaModal from "../../modals/textAreaModal";
|
|||
import { TextAreaComponentType } from "../../types/components";
|
||||
import PromptAreaModal from "../../modals/promptModal";
|
||||
|
||||
export default function PromptAreaComponent({ value, onChange, disabled }:TextAreaComponentType) {
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setMyValue("");
|
||||
onChange("");
|
||||
}
|
||||
}, [disabled, onChange]);
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed w-full" : " w-full"}>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<span
|
||||
className={
|
||||
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
|
||||
(disabled ? " bg-gray-200" : "")
|
||||
}
|
||||
>
|
||||
{myValue !== "" ? myValue : 'Text empty'}
|
||||
</span>
|
||||
<button onClick={()=>{openPopUp(<PromptAreaModal value={myValue} setValue={(t:string) => {setMyValue(t); onChange(t);}}/>)}}>
|
||||
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function PromptAreaComponent({
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
}: TextAreaComponentType) {
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setMyValue("");
|
||||
onChange("");
|
||||
}
|
||||
}, [disabled, onChange]);
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
disabled ? "pointer-events-none cursor-not-allowed w-full" : " w-full"
|
||||
}
|
||||
>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<span
|
||||
onClick={() => {
|
||||
openPopUp(
|
||||
<PromptAreaModal
|
||||
value={myValue}
|
||||
setValue={(t: string) => {
|
||||
setMyValue(t);
|
||||
onChange(t);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
className={
|
||||
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
|
||||
(disabled ? " bg-gray-200" : "")
|
||||
}
|
||||
>
|
||||
{myValue !== "" ? myValue : "Text empty"}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => {
|
||||
openPopUp(
|
||||
<PromptAreaModal
|
||||
value={myValue}
|
||||
setValue={(t: string) => {
|
||||
setMyValue(t);
|
||||
onChange(t);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<ArrowTopRightOnSquareIcon className="w-6 h-6 hover:text-blue-600" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { ArrowTopRightOnSquareIcon } from "@heroicons/react/24/outline";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import CodeAreaModal from "../../modals/codeAreaModal";
|
||||
import TextAreaModal from "../../modals/textAreaModal";
|
||||
import { TextAreaComponentType } from "../../types/components";
|
||||
|
||||
|
|
@ -17,7 +16,7 @@ export default function TextAreaComponent({ value, onChange, disabled }:TextArea
|
|||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
||||
<div className="w-full flex items-center gap-3">
|
||||
<span
|
||||
<span onClick={()=>{openPopUp(<TextAreaModal value={myValue} setValue={(t:string) => {setMyValue(t); onChange(t);}}/>)}}
|
||||
className={
|
||||
"truncate block w-full text-gray-500 px-3 py-2 rounded-md border border-gray-300 dark:border-gray-700 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm" +
|
||||
(disabled ? " bg-gray-200" : "")
|
||||
|
|
|
|||
|
|
@ -11,15 +11,15 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
|
|||
return (
|
||||
<>
|
||||
<DarkProvider>
|
||||
<LocationProvider>
|
||||
<AlertProvider>
|
||||
<TabsProvider>
|
||||
<TypesProvider>
|
||||
<TypesProvider>
|
||||
<LocationProvider>
|
||||
<AlertProvider>
|
||||
<TabsProvider>
|
||||
<PopUpProvider>{children}</PopUpProvider>
|
||||
</TypesProvider>
|
||||
</TabsProvider>
|
||||
</AlertProvider>
|
||||
</LocationProvider>
|
||||
</TabsProvider>
|
||||
</AlertProvider>
|
||||
</LocationProvider>
|
||||
</TypesProvider>
|
||||
</DarkProvider>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
import { createContext } from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
//context to set JSX element on the DOM
|
||||
// context to set JSX element on the DOM
|
||||
export const PopUpContext = createContext({
|
||||
openPopUp: (popUpElement: JSX.Element) => {},
|
||||
closePopUp: () => {},
|
||||
closePopUp: () => {},
|
||||
});
|
||||
|
||||
interface PopUpProviderProps {
|
||||
children: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const PopUpProvider = ({ children }: PopUpProviderProps) => {
|
||||
const [popUpElement, setPopUpElement] = useState<JSX.Element | null>(null);
|
||||
const [popUpElements, setPopUpElements] = useState<JSX.Element[]>([]);
|
||||
|
||||
const openPopUp = (element: JSX.Element) => {
|
||||
setPopUpElement(element);
|
||||
};
|
||||
const openPopUp = (element: JSX.Element) => {
|
||||
setPopUpElements(prevPopUps => [element, ...prevPopUps]);
|
||||
};
|
||||
|
||||
const closePopUp = () => {
|
||||
setPopUpElement(null);
|
||||
};
|
||||
const closePopUp = () => {
|
||||
setPopUpElements(prevPopUps => prevPopUps.slice(1));
|
||||
};
|
||||
|
||||
return (
|
||||
<PopUpContext.Provider value={{ openPopUp, closePopUp }}>
|
||||
{children}
|
||||
{popUpElement}
|
||||
</PopUpContext.Provider>
|
||||
);
|
||||
return (
|
||||
<PopUpContext.Provider value={{ openPopUp, closePopUp }}>
|
||||
{children}
|
||||
{popUpElements[0]}
|
||||
</PopUpContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopUpProvider;
|
||||
|
|
|
|||
|
|
@ -7,9 +7,11 @@ import {
|
|||
useContext,
|
||||
} from "react";
|
||||
import { FlowType } from "../types/flow";
|
||||
import { TabsContextType } from "../types/tabs";
|
||||
import { normalCaseToSnakeCase } from "../utils";
|
||||
import { LangFlowState, TabsContextType } from "../types/tabs";
|
||||
import { normalCaseToSnakeCase, updateObject } from "../utils";
|
||||
import { alertContext } from "./alertContext";
|
||||
import { typesContext } from "./typesContext";
|
||||
import { TemplateVariableType } from "../types/api";
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
const TabsContextInitialValue: TabsContextType = {
|
||||
|
|
@ -35,6 +37,7 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
const [tabIndex, setTabIndex] = useState(0);
|
||||
const [flows, setFlows] = useState<Array<FlowType>>([]);
|
||||
const [id, setId] = useState("");
|
||||
const { templates } = useContext(typesContext);
|
||||
|
||||
const newNodeId = useRef(0);
|
||||
function incrementNodeId() {
|
||||
|
|
@ -56,19 +59,31 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
useEffect(() => {
|
||||
//get tabs locally saved
|
||||
let cookie = window.localStorage.getItem("tabsData");
|
||||
if (cookie) {
|
||||
let cookieObject = JSON.parse(cookie);
|
||||
if (cookie && Object.keys(templates).length > 0) {
|
||||
let cookieObject: LangFlowState = JSON.parse(cookie);
|
||||
cookieObject.flows.forEach((flow) => {
|
||||
flow.data.nodes.forEach((node) => {
|
||||
if (Object.keys(templates[node.data.type]["template"]).length>0) {
|
||||
node.data.node.template = updateObject(
|
||||
node.data.node.template as TemplateVariableType,
|
||||
templates[node.data.type][
|
||||
"template"
|
||||
] as unknown as TemplateVariableType
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
setTabIndex(cookieObject.tabIndex);
|
||||
setFlows(cookieObject.flows);
|
||||
setId(cookieObject.id);
|
||||
newNodeId.current = cookieObject.nodeId;
|
||||
}
|
||||
}, []);
|
||||
}, [templates]);
|
||||
function hardReset() {
|
||||
newNodeId.current = 0;
|
||||
setTabIndex(0);
|
||||
setFlows([]);
|
||||
setId(uuidv4());
|
||||
setId("");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -110,7 +125,19 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
// read the file as text
|
||||
file.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
addFlow(JSON.parse(text));
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
flow.data.nodes.forEach((node) => {
|
||||
if (Object.keys(templates[node.data.type]["template"]).length>0) {
|
||||
node.data.node.template = updateObject(
|
||||
node.data.node.template as TemplateVariableType,
|
||||
templates[node.data.type][
|
||||
"template"
|
||||
] as unknown as TemplateVariableType
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
addFlow();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { createContext, ReactNode, useState } from "react";
|
||||
import { createContext, ReactNode, useEffect, useState } from "react";
|
||||
import { Node} from "reactflow";
|
||||
import { typesContextType } from "../types/typesContext";
|
||||
import { getAll } from "../controllers/API";
|
||||
import { APIKindType } from "../types/api";
|
||||
|
||||
//context to share types adn functions from nodes to flow
|
||||
|
||||
|
|
@ -10,6 +12,10 @@ const initialValue:typesContextType = {
|
|||
deleteNode: () => {},
|
||||
types: {},
|
||||
setTypes: () => {},
|
||||
templates: {},
|
||||
setTemplates: () => {},
|
||||
data:{},
|
||||
setData:()=>{}
|
||||
};
|
||||
|
||||
export const typesContext = createContext<typesContextType>(initialValue);
|
||||
|
|
@ -17,6 +23,42 @@ export const typesContext = createContext<typesContextType>(initialValue);
|
|||
export function TypesProvider({ children }:{children:ReactNode}) {
|
||||
const [types, setTypes] = useState({});
|
||||
const [reactFlowInstance, setReactFlowInstance] = useState(null);
|
||||
const [templates, setTemplates] = useState({});
|
||||
const [data, setData] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
async function getTypes(): Promise<void> {
|
||||
// Make an asynchronous API call to retrieve all data.
|
||||
let result = await getAll();
|
||||
|
||||
// Update the state of the component with the retrieved data.
|
||||
setData(result.data);
|
||||
setTemplates(
|
||||
Object.keys(result.data).reduce((acc, curr) => {
|
||||
Object.keys(result.data[curr]).forEach((c: keyof APIKindType)=>{
|
||||
acc[c] = result.data[curr][c]
|
||||
})
|
||||
return acc;
|
||||
},{})
|
||||
);
|
||||
// Set the types by reducing over the keys of the result data and updating the accumulator.
|
||||
setTypes(
|
||||
Object.keys(result.data).reduce((acc, curr) => {
|
||||
Object.keys(result.data[curr]).forEach((c: keyof APIKindType) => {
|
||||
acc[c] = curr;
|
||||
// Add the base classes to the accumulator as well.
|
||||
result.data[curr][c].base_classes?.forEach((b) => {
|
||||
acc[b] = curr;
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
// Call the getTypes function.
|
||||
getTypes();
|
||||
}, [setTypes]);
|
||||
|
||||
function deleteNode(idx:string) {
|
||||
reactFlowInstance.setNodes(
|
||||
reactFlowInstance.getNodes().filter((n:Node) => n.id !== idx)
|
||||
|
|
@ -31,6 +73,10 @@ export function TypesProvider({ children }:{children:ReactNode}) {
|
|||
reactFlowInstance,
|
||||
setReactFlowInstance,
|
||||
deleteNode,
|
||||
setTemplates,
|
||||
templates,
|
||||
data,
|
||||
setData
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
import { useContext, useState } from "react";
|
||||
import { TabsContext } from "../../../../contexts/tabsContext";
|
||||
import InputListComponent from "../../../../components/inputListComponent";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
import ToggleComponent from "../../../../components/toggleComponent";
|
||||
import FloatComponent from "../../../../components/floatComponent";
|
||||
import IntComponent from "../../../../components/intComponent";
|
||||
import InputFileComponent from "../../../../components/inputFileComponent";
|
||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
||||
import { classNames } from "../../../../utils";
|
||||
|
||||
export default function ModalField({ data, title, required, id, name, type }) {
|
||||
const { save } = useContext(TabsContext);
|
||||
const [enabled, setEnabled] = useState(
|
||||
data.node.template[name]?.value ?? false
|
||||
);
|
||||
const display =
|
||||
type === "str" ||
|
||||
type === "int" ||
|
||||
type === "prompt" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "file" ||
|
||||
type === "code";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
"flex flex-row w-full items-center justify-between",
|
||||
display ? "" : "hidden"
|
||||
)}
|
||||
>
|
||||
{display && (
|
||||
<div>
|
||||
<span className="mx-2">{title}</span>
|
||||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{type === "str" && !data.node.template[name].options ? (
|
||||
<div className="w-1/2">
|
||||
{data.node.template[name].list ? (
|
||||
<InputListComponent
|
||||
disabled={false}
|
||||
value={
|
||||
!data.node.template[name].value ||
|
||||
data.node.template[name].value === ""
|
||||
? [""]
|
||||
: data.node.template[name].value
|
||||
}
|
||||
onChange={(t: string[]) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
) : data.node.template[name].multiline ? (
|
||||
<TextAreaComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
disabled={false}
|
||||
password={data.node.template[name].password ?? false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : type === "bool" ? (
|
||||
<div className="ml-auto">
|
||||
{" "}
|
||||
<ToggleComponent
|
||||
disabled={false}
|
||||
enabled={enabled}
|
||||
setEnabled={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
setEnabled(t);
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "float" ? (
|
||||
<div className="w-1/2">
|
||||
<FloatComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "str" && data.node.template[name].options ? (
|
||||
<div className="w-1/2">
|
||||
<Dropdown
|
||||
options={data.node.template[name].options}
|
||||
onSelect={(newValue) => (data.node.template[name].value = newValue)}
|
||||
value={data.node.template[name].value ?? "Choose an option"}
|
||||
></Dropdown>
|
||||
</div>
|
||||
) : type === "int" ? (
|
||||
<div className="w-1/2">
|
||||
<IntComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "file" ? (
|
||||
<div className="w-1/2">
|
||||
<InputFileComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
}}
|
||||
fileTypes={data.node.template[name].fileTypes}
|
||||
suffixes={data.node.template[name].suffixes}
|
||||
onFileChange={(t: string) => {
|
||||
data.node.template[name].content = t;
|
||||
save();
|
||||
}}
|
||||
></InputFileComponent>
|
||||
</div>
|
||||
) : type === "prompt" ? (
|
||||
<div className="w-1/2">
|
||||
<PromptAreaComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : type === "code" ? (
|
||||
<div className="w-1/2">
|
||||
<CodeAreaComponent
|
||||
disabled={false}
|
||||
value={data.node.template[name].value ?? ""}
|
||||
onChange={(t: string) => {
|
||||
data.node.template[name].value = t;
|
||||
save();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="hidden"></div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
144
src/frontend/src/modals/NodeModal/index.tsx
Normal file
144
src/frontend/src/modals/NodeModal/index.tsx
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { XMarkIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import { nodeColors, nodeIcons, snakeToNormalCase } from "../../utils";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import ModalField from "./components/ModalField";
|
||||
|
||||
export default function NodeModal({ data }: { data: NodeDataType }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const { types } = useContext(typesContext);
|
||||
const ref = useRef();
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
const Icon = nodeIcons[types[data.type]];
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<Icon
|
||||
className="w-10 mt-4 h-10 p-1 rounded"
|
||||
style={{
|
||||
color:
|
||||
nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
{data.type}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full w-full bg-gray-200 dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
||||
<div className="flex h-full w-full">
|
||||
<div className="overflow-hidden px-4 sm:p-4 w-full rounded-lg bg-white dark:bg-gray-800 shadow">
|
||||
<div className="flex flex-col h-full gap-5">
|
||||
{
|
||||
Object.keys(data.node.template)
|
||||
.filter((t) => t.charAt(0) !== "_"&& data.node.template[t].advanced && data.node.template[t].show)
|
||||
.map((t: string, idx) => {
|
||||
return (
|
||||
<ModalField
|
||||
key={idx}
|
||||
data={data}
|
||||
title={
|
||||
data.node.template[t].display_name
|
||||
? data.node.template[t].display_name
|
||||
: data.node.template[t].name
|
||||
? snakeToNormalCase(
|
||||
data.node.template[t].name
|
||||
)
|
||||
: snakeToNormalCase(t)
|
||||
}
|
||||
required={data.node.template[t].required}
|
||||
id={
|
||||
data.node.template[t].type +
|
||||
"|" +
|
||||
t +
|
||||
"|" +
|
||||
data.id
|
||||
}
|
||||
name={t}
|
||||
type={data.node.template[t].type}
|
||||
/>
|
||||
);
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
Done
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import {
|
||||
XMarkIcon,
|
||||
ArrowDownTrayIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ComputerDesktopIcon,
|
||||
ArrowUpTrayIcon,
|
||||
ArrowLeftIcon,
|
||||
XMarkIcon,
|
||||
ArrowDownTrayIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ComputerDesktopIcon,
|
||||
ArrowUpTrayIcon,
|
||||
ArrowLeftIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
|
|
@ -19,211 +19,214 @@ import { FlowType } from "../../types/flow";
|
|||
import { classNames } from "../../utils";
|
||||
|
||||
export default function ImportModal() {
|
||||
const [open, setOpen] = useState(true);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const ref = useRef();
|
||||
const [showExamples, setShowExamples] = useState(false);
|
||||
const [loadingExamples, setLoadingExamples] = useState(false);
|
||||
const [examples, setExamples] = useState<FlowType[]>([]);
|
||||
const { uploadFlow, addFlow } = useContext(TabsContext);
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
const [open, setOpen] = useState(true);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const ref = useRef();
|
||||
const [showExamples, setShowExamples] = useState(false);
|
||||
const [loadingExamples, setLoadingExamples] = useState(false);
|
||||
const [examples, setExamples] = useState<FlowType[]>([]);
|
||||
const { uploadFlow, addFlow } = useContext(TabsContext);
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
|
||||
function handleExamples() {
|
||||
setLoadingExamples(true);
|
||||
getExamples()
|
||||
.then((result) => {
|
||||
setLoadingExamples(false);
|
||||
setExamples(result);
|
||||
})
|
||||
.catch((error) =>
|
||||
setErrorData({
|
||||
title: "there was an error loading examples, please try again",
|
||||
list: [error.message],
|
||||
})
|
||||
);
|
||||
}
|
||||
function handleExamples() {
|
||||
setLoadingExamples(true);
|
||||
getExamples()
|
||||
.then((result) => {
|
||||
setLoadingExamples(false);
|
||||
setExamples(result);
|
||||
})
|
||||
.catch((error) =>
|
||||
setErrorData({
|
||||
title: "there was an error loading examples, please try again",
|
||||
list: [error.message],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-gray-500 dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
{showExamples && (
|
||||
<>
|
||||
<div className="z-50 absolute top-2 left-0 hidden pt-4 pl-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setShowExamples(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<ArrowLeftIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="z-50 absolute bottom-2 left-0 hidden pt-4 pl-2 sm:block">
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow_examples"
|
||||
target="_blank"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
viewBox="0 0 98 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
fill="#24292f"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ArrowUpTrayIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
{showExamples ?"Select an example":"Import from"}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"h-full w-full bg-gray-200 dark:bg-gray-900 gap-4",
|
||||
showExamples && !loadingExamples
|
||||
? "flex flex-row start justify-start items-start p-9 flex-wrap overflow-auto"
|
||||
: "flex flex-row justify-center items-center p-4"
|
||||
)}
|
||||
>
|
||||
{!showExamples && (
|
||||
<div className="flex h-full w-full justify-evenly items-center">
|
||||
<ButtonBox
|
||||
size="big"
|
||||
bgColor="bg-emerald-500"
|
||||
description="Prebuilt Examples"
|
||||
icon={
|
||||
<DocumentDuplicateIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
setShowExamples(true);
|
||||
handleExamples();
|
||||
}}
|
||||
textColor="text-emerald-400"
|
||||
title="Examples"
|
||||
></ButtonBox>
|
||||
<ButtonBox
|
||||
size="big"
|
||||
bgColor="bg-blue-500"
|
||||
description="Import from Local"
|
||||
icon={
|
||||
<ComputerDesktopIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
uploadFlow();
|
||||
setModalOpen(false);
|
||||
}}
|
||||
textColor="text-blue-500"
|
||||
title="Local file"
|
||||
></ButtonBox>
|
||||
</div>
|
||||
)}
|
||||
{showExamples && loadingExamples && (
|
||||
<div className="flex align-middle justify-center items-center">
|
||||
<LoadingComponent remSize={30} />
|
||||
</div>
|
||||
)}
|
||||
{showExamples &&
|
||||
!loadingExamples &&
|
||||
examples.map((example, index) => {
|
||||
return (
|
||||
<div id="index">
|
||||
{" "}
|
||||
<ButtonBox
|
||||
size="small"
|
||||
bgColor="bg-emerald-500"
|
||||
description={
|
||||
example.description ?? "Prebuilt Examples"
|
||||
}
|
||||
icon={
|
||||
<DocumentDuplicateIcon className="h-6 w-6 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
addFlow(example);
|
||||
setModalOpen(false);
|
||||
}}
|
||||
textColor="text-emerald-400"
|
||||
title={example.name}
|
||||
></ButtonBox>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
{showExamples && (
|
||||
<>
|
||||
<div className="z-50 absolute top-2 left-0 hidden pt-4 pl-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
|
||||
onClick={() => {
|
||||
setShowExamples(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<ArrowLeftIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="z-50 absolute bottom-2 left-1/2 transform -translate-x-1/2 hidden pt-4 pl-2 sm:block text-center">
|
||||
<a
|
||||
href="https://github.com/logspace-ai/langflow_examples"
|
||||
target="_blank"
|
||||
className="flex items-center justify-center"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
viewBox="0 0 98 96"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"
|
||||
fill="#24292f"
|
||||
/>
|
||||
</svg>
|
||||
<span className="ml-2">LangFlow Examples</span>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<ArrowUpTrayIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
{showExamples ? "Select an example" : "Import from"}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
"h-full w-full bg-gray-200 dark:bg-gray-900 gap-4",
|
||||
showExamples && !loadingExamples
|
||||
? "flex flex-row start justify-start items-start p-9 flex-wrap overflow-auto"
|
||||
: "flex flex-row justify-center items-center p-4"
|
||||
)}
|
||||
>
|
||||
{!showExamples && (
|
||||
<div className="flex h-full w-full justify-evenly items-center">
|
||||
<ButtonBox
|
||||
size="big"
|
||||
bgColor="bg-emerald-500"
|
||||
description="Prebuilt Examples"
|
||||
icon={
|
||||
<DocumentDuplicateIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
setShowExamples(true);
|
||||
handleExamples();
|
||||
}}
|
||||
textColor="text-emerald-400"
|
||||
title="Examples"
|
||||
></ButtonBox>
|
||||
<ButtonBox
|
||||
size="big"
|
||||
bgColor="bg-blue-500"
|
||||
description="Import from Local"
|
||||
icon={
|
||||
<ComputerDesktopIcon className="h-10 w-10 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
uploadFlow();
|
||||
setModalOpen(false);
|
||||
}}
|
||||
textColor="text-blue-500"
|
||||
title="Local file"
|
||||
></ButtonBox>
|
||||
</div>
|
||||
)}
|
||||
{showExamples && loadingExamples && (
|
||||
<div className="flex align-middle justify-center items-center">
|
||||
<LoadingComponent remSize={30} />
|
||||
</div>
|
||||
)}
|
||||
{showExamples &&
|
||||
!loadingExamples &&
|
||||
examples.map((example, index) => {
|
||||
return (
|
||||
<div id="index">
|
||||
{" "}
|
||||
<ButtonBox
|
||||
size="small"
|
||||
bgColor="bg-emerald-500"
|
||||
description={
|
||||
example.description ?? "Prebuilt Examples"
|
||||
}
|
||||
icon={
|
||||
<DocumentDuplicateIcon className="h-6 w-6 flex-shrink-0" />
|
||||
}
|
||||
onClick={() => {
|
||||
addFlow(example);
|
||||
setModalOpen(false);
|
||||
}}
|
||||
textColor="text-emerald-400"
|
||||
title={example.name}
|
||||
></ButtonBox>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,43 +2,14 @@ import { Bars2Icon } from "@heroicons/react/24/outline";
|
|||
import DisclosureComponent from "../DisclosureComponent";
|
||||
import { nodeColors, nodeIcons, nodeNames } from "../../../../utils";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import { getAll } from "../../../../controllers/API";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
import {
|
||||
APIClassType,
|
||||
APIKindType,
|
||||
APIObjectType,
|
||||
} from "../../../../types/api";
|
||||
|
||||
export default function ExtraSidebar() {
|
||||
const [data, setData] = useState({});
|
||||
const { setTypes } = useContext(typesContext);
|
||||
|
||||
useEffect(() => {
|
||||
async function getTypes(): Promise<void> {
|
||||
// Make an asynchronous API call to retrieve all data.
|
||||
let result = await getAll();
|
||||
|
||||
// Update the state of the component with the retrieved data.
|
||||
setData(result.data);
|
||||
|
||||
// Set the types by reducing over the keys of the result data and updating the accumulator.
|
||||
setTypes(
|
||||
Object.keys(result.data).reduce((acc, curr) => {
|
||||
Object.keys(result.data[curr]).forEach((c: keyof APIKindType) => {
|
||||
acc[c] = curr;
|
||||
// Add the base classes to the accumulator as well.
|
||||
result.data[curr][c].base_classes?.forEach((b) => {
|
||||
acc[b] = curr;
|
||||
});
|
||||
});
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
}
|
||||
// Call the getTypes function.
|
||||
getTypes();
|
||||
}, [setTypes]);
|
||||
const {data} = useContext(typesContext)
|
||||
|
||||
function onDragStart(
|
||||
event: React.DragEvent<any>,
|
||||
|
|
@ -84,7 +55,9 @@ export default function ExtraSidebar() {
|
|||
</div>
|
||||
</div>
|
||||
))}
|
||||
{Object.keys(data[d]).length===0 && <div className="text-gray-400 text-center">Coming soon</div>}
|
||||
{Object.keys(data[d]).length === 0 && (
|
||||
<div className="text-gray-400 text-center">Coming soon</div>
|
||||
)}
|
||||
</div>
|
||||
</DisclosureComponent>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -16,21 +16,23 @@ import AlertDropdown from "../../../../alerts/alertDropDown";
|
|||
import { alertContext } from "../../../../contexts/alertContext";
|
||||
import ImportModal from "../../../../modals/importModal";
|
||||
import ExportModal from "../../../../modals/exportModal";
|
||||
import { typesContext } from "../../../../contexts/typesContext";
|
||||
|
||||
export default function TabsManagerComponent() {
|
||||
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } =
|
||||
useContext(TabsContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const AlertWidth = 256;
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
const { notificationCenter, setNotificationCenter } =
|
||||
useContext(alertContext);
|
||||
useEffect(() => {
|
||||
//create the first flow
|
||||
if (flows.length === 0) {
|
||||
addFlow();
|
||||
}
|
||||
}, [addFlow, flows.length]);
|
||||
const { flows, addFlow, tabIndex, setTabIndex, uploadFlow, downloadFlow } =
|
||||
useContext(TabsContext);
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const {templates} = useContext(typesContext)
|
||||
const AlertWidth = 256;
|
||||
const { dark, setDark } = useContext(darkContext);
|
||||
const { notificationCenter, setNotificationCenter } =
|
||||
useContext(alertContext);
|
||||
useEffect(() => {
|
||||
//create the first flow
|
||||
if (flows.length === 0&& Object.keys(templates).length>0) {
|
||||
addFlow();
|
||||
}
|
||||
}, [addFlow, flows.length,templates]);
|
||||
|
||||
return (
|
||||
<div className="h-full w-full flex flex-col">
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ const nodeTypes = {
|
|||
|
||||
var _ = require("lodash");
|
||||
|
||||
export default function FlowPage({ flow }: { flow: FlowType }) {
|
||||
let { updateFlow, incrementNodeId } = useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance } =
|
||||
useContext(typesContext);
|
||||
const reactFlowWrapper = useRef(null);
|
||||
export default function FlowPage({ flow }:{flow:FlowType}) {
|
||||
let { updateFlow, incrementNodeId} =
|
||||
useContext(TabsContext);
|
||||
const { types, reactFlowInstance, setReactFlowInstance, templates } =
|
||||
useContext(typesContext);
|
||||
const reactFlowWrapper = useRef(null);
|
||||
|
||||
const { setExtraComponent, setExtraNavigation } = useContext(locationContext);
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
|
|
@ -176,46 +177,47 @@ export default function FlowPage({ flow }: { flow: FlowType }) {
|
|||
[]
|
||||
);
|
||||
|
||||
const onEdgeUpdateEnd = useCallback((_, edge) => {
|
||||
if (!edgeUpdateSuccessful.current) {
|
||||
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
||||
}
|
||||
|
||||
edgeUpdateSuccessful.current = true;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||
{Object.keys(types).length > 0 ? (
|
||||
<>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
onMove={() =>
|
||||
updateFlow({ ...flow, data: reactFlowInstance.toObject() })
|
||||
}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChangeMod}
|
||||
onConnect={onConnect}
|
||||
onLoad={setReactFlowInstance}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
onNodesDelete={onDelete}
|
||||
>
|
||||
<Background className="dark:bg-gray-900" />
|
||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600"></Controls>
|
||||
</ReactFlow>
|
||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
const onEdgeUpdateEnd = useCallback((_, edge) => {
|
||||
if (!edgeUpdateSuccessful.current) {
|
||||
setEdges((eds) => eds.filter((e) => e.id !== edge.id));
|
||||
}
|
||||
|
||||
edgeUpdateSuccessful.current = true;
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full" ref={reactFlowWrapper}>
|
||||
{Object.keys(templates).length > 0 && Object.keys(types).length > 0 ? (
|
||||
<>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
onMove={() =>
|
||||
updateFlow({ ...flow, data: reactFlowInstance.toObject() })
|
||||
}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChangeMod}
|
||||
onConnect={onConnect}
|
||||
onLoad={setReactFlowInstance}
|
||||
onInit={setReactFlowInstance}
|
||||
nodeTypes={nodeTypes}
|
||||
onEdgeUpdate={onEdgeUpdate}
|
||||
onEdgeUpdateStart={onEdgeUpdateStart}
|
||||
onEdgeUpdateEnd={onEdgeUpdateEnd}
|
||||
connectionLineComponent={ConnectionLineComponent}
|
||||
onDragOver={onDragOver}
|
||||
onDrop={onDrop}
|
||||
onNodesDelete={onDelete}
|
||||
>
|
||||
<Background className="dark:bg-gray-900"/>
|
||||
<Controls className="[&>button]:text-black [&>button]:dark:bg-gray-800 hover:[&>button]:dark:bg-gray-700 [&>button]:dark:text-gray-400 [&>button]:dark:fill-gray-400 [&>button]:dark:border-gray-600">
|
||||
</Controls>
|
||||
</ReactFlow>
|
||||
<Chat flow={flow} reactFlowInstance={reactFlowInstance} />
|
||||
</>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,3 +13,5 @@ export type TabsContextType = {
|
|||
uploadFlow: () => void;
|
||||
hardReset: () => void;
|
||||
};
|
||||
|
||||
export type LangFlowState={ tabIndex:number, flows:FlowType[], id:string, nodeId:number }
|
||||
7
src/frontend/src/types/templatesContext/index.ts
Normal file
7
src/frontend/src/types/templatesContext/index.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
const template:{[char: string]: string}={}
|
||||
|
||||
export type TemplateContextType = {
|
||||
templates: typeof template;
|
||||
setTemplates: (newState: {}) => void;
|
||||
};
|
||||
|
|
@ -1,11 +1,18 @@
|
|||
import { ReactFlowInstance } from "reactflow";
|
||||
|
||||
const types: { [char: string]: string } = {};
|
||||
const types:{[char: string]: string}={};
|
||||
const template:{[char: string]: string}={}
|
||||
const data:{[char: string]: string}={}
|
||||
|
||||
|
||||
export type typesContextType = {
|
||||
reactFlowInstance: ReactFlowInstance | null;
|
||||
setReactFlowInstance: any;
|
||||
deleteNode: (idx: string) => void;
|
||||
types: typeof types;
|
||||
setTypes: (newState: {}) => void;
|
||||
reactFlowInstance: ReactFlowInstance|null;
|
||||
setReactFlowInstance: any;
|
||||
deleteNode: (idx: string) => void;
|
||||
types: typeof types;
|
||||
setTypes: (newState: {}) => void;
|
||||
templates: typeof template;
|
||||
setTemplates: (newState: {}) => void;
|
||||
data: typeof data;
|
||||
setData: (newState: {}) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -398,3 +398,22 @@ export function removeApiKeys(flow: FlowType): FlowType {
|
|||
});
|
||||
return cleanFLow;
|
||||
}
|
||||
|
||||
export function updateObject<T extends Record<string, any>>(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;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue