Merge branch 'cz/bug/state/zustand' of personal:logspace-ai/langflow into cz/bug/state/zustand

This commit is contained in:
anovazzi1 2024-01-08 15:34:27 -03:00
commit 2f91fc0b8f
21 changed files with 250 additions and 474 deletions

View file

@ -16,17 +16,19 @@ import {
FETCH_ERROR_MESSAGE, FETCH_ERROR_MESSAGE,
} from "./constants/constants"; } from "./constants/constants";
import { AuthContext } from "./contexts/authContext"; import { AuthContext } from "./contexts/authContext";
import { locationContext } from "./contexts/locationContext";
import { getHealth, getRepoStars, getVersion } from "./controllers/API"; import { getHealth, getRepoStars, getVersion } from "./controllers/API";
import Router from "./routes"; import Router from "./routes";
import useAlertStore from "./stores/alertStore"; import useAlertStore from "./stores/alertStore";
import { useTypesStore } from "./stores/typesStore"; import { useTypesStore } from "./stores/typesStore";
import { useDarkStore } from "./stores/darkStore"; import { useDarkStore } from "./stores/darkStore";
import useFlowsManagerStore from "./stores/flowsManagerStore"; import useFlowsManagerStore from "./stores/flowsManagerStore";
import { useLocationStore } from "./stores/locationStore";
export default function App() { export default function App() {
let { setCurrent, setShowSideBar, setIsStackedOpen } = const setCurrent = useLocationStore((state) => state.setCurrent);
useContext(locationContext); const setShowSideBar = useLocationStore((state) => state.setShowSideBar);
const setIsStackedOpen = useLocationStore((state) => state.setIsStackedOpen);
let location = useLocation(); let location = useLocation();
useEffect(() => { useEffect(() => {
setCurrent(location.pathname.replace(/\/$/g, "").split("/")); setCurrent(location.pathname.replace(/\/$/g, "").split("/"));

View file

@ -6,7 +6,6 @@ import IconComponent from "../../components/genericIconComponent";
import InputComponent from "../../components/inputComponent"; import InputComponent from "../../components/inputComponent";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import { priorityFields } from "../../constants/constants"; import { priorityFields } from "../../constants/constants";
import { useSSE } from "../../contexts/SSEContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import { validationStatusType } from "../../types/components"; import { validationStatusType } from "../../types/components";
@ -80,7 +79,8 @@ export default function GenericNode({
}, [data, data.node]); }, [data, data.node]);
// State for outline color // State for outline color
const { sseData, isBuilding } = useSSE(); const sseData = useFlowStore((state) => state.sseData);
const isBuilding = useFlowStore((state) => state.isBuilding);
useEffect(() => { useEffect(() => {
setNodeDescription(data.node!.description); setNodeDescription(data.node!.description);

View file

@ -1,18 +1,16 @@
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
import { useContext, useState } from "react"; import { useState } from "react";
import Loading from "../../../components/ui/loading"; import Loading from "../../../components/ui/loading";
import { useSSE } from "../../../contexts/SSEContext";
import { postBuildInit } from "../../../controllers/API"; import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow"; import { FlowType } from "../../../types/flow";
import useAlertStore from "../../../stores/alertStore"; import useAlertStore from "../../../stores/alertStore";
import useFlowStore from "../../../stores/flowStore"; import useFlowStore from "../../../stores/flowStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { parsedDataType } from "../../../types/components"; import { parsedDataType } from "../../../types/components";
import { FlowState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils"; import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress"; import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent"; import IconComponent from "../../genericIconComponent";
import { stat } from "fs";
export default function BuildTrigger({ export default function BuildTrigger({
open, open,
@ -24,17 +22,17 @@ export default function BuildTrigger({
setIsBuilt: any; setIsBuilt: any;
isBuilt: boolean; isBuilt: boolean;
}): JSX.Element { }): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE(); const updateSSEData = useFlowStore((state) => state.updateSSEData);
const isBuilding = useFlowStore((state) => state.isBuilding);
const setIsBuilding = useFlowStore((state) => state.setIsBuilding);
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setCurrentFlowState = useFlowsManagerStore( const setFlowState = useFlowStore(
(state) => state.setCurrentFlowState (state) => state.setFlowState
); );
const saveFlow = useFlowsManagerStore((state) => state.saveFlow);
const [isIconTouched, setIsIconTouched] = useState(false);
const eventClick = isBuilding ? "pointer-events-none" : ""; const eventClick = isBuilding ? "pointer-events-none" : "";
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
@ -79,7 +77,6 @@ export default function BuildTrigger({
} }
async function streamNodeData(flow: FlowType) { async function streamNodeData(flow: FlowType) {
// Step 1: Make a POST request to send the flow data and receive a unique session ID // Step 1: Make a POST request to send the flow data and receive a unique session ID
const id = saveFlow(flow, true);
const response = await postBuildInit(flow); const response = await postBuildInit(flow);
const { flowId } = response.data; const { flowId } = response.data;
// Step 2: Use the session ID to establish an SSE connection using EventSource // Step 2: Use the session ID to establish an SSE connection using EventSource
@ -102,7 +99,7 @@ export default function BuildTrigger({
// If the event is a log, log it // If the event is a log, log it
setSuccessData({ title: parsedData.log }); setSuccessData({ title: parsedData.log });
} else if (parsedData.input_keys !== undefined) { } else if (parsedData.input_keys !== undefined) {
setCurrentFlowState(parsedData); setFlowState(parsedData);
} else { } else {
// Otherwise, process the data // Otherwise, process the data
const isValid = processStreamResult(parsedData); const isValid = processStreamResult(parsedData);
@ -148,14 +145,6 @@ export default function BuildTrigger({
} }
} }
const handleMouseEnter = () => {
setIsIconTouched(true);
};
const handleMouseLeave = () => {
setIsIconTouched(false);
};
return ( return (
<Transition <Transition
show={!open} show={!open}
@ -173,8 +162,6 @@ export default function BuildTrigger({
onClick={() => { onClick={() => {
handleBuild(flow); handleBuild(flow);
}} }}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
> >
<button> <button>
<div className="round-button-div"> <div className="round-button-div">

View file

@ -13,11 +13,9 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
export default function Chat({ flow }: ChatType): JSX.Element { export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [canOpen, setCanOpen] = useState(false);
const isBuilt = useFlowStore((state) => state.isBuilt); const isBuilt = useFlowStore((state) => state.isBuilt);
const setIsBuilt = useFlowStore((state) => state.setIsBuilt); const setIsBuilt = useFlowStore((state) => state.setIsBuilt);
const currentFlowState = useFlowsManagerStore((state) => state.currentFlowState); const flowState = useFlowStore((state) => state.flowState);
useEffect(() => { useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if ( if (
@ -58,18 +56,8 @@ export default function Chat({ flow }: ChatType): JSX.Element {
) { ) {
setIsBuilt(false); setIsBuilt(false);
} }
if (
currentFlowState &&
currentFlowState.formKeysData &&
currentFlowState.formKeysData.input_keys !== null
) {
setCanOpen(true);
} else {
setCanOpen(false);
}
prevNodesRef.current = currentNodes; prevNodesRef.current = currentNodes;
}, [currentFlowState, flow.id]); }, [flowState, flow.id]);
return ( return (
<> <>
@ -81,9 +69,8 @@ export default function Chat({ flow }: ChatType): JSX.Element {
isBuilt={isBuilt} isBuilt={isBuilt}
/> />
{isBuilt && {isBuilt &&
currentFlowState && flowState &&
currentFlowState.formKeysData && !!flowState?.input_keys && (
canOpen && (
<FormModal <FormModal
key={flow.id} key={flow.id}
flow={flow} flow={flow}
@ -92,7 +79,7 @@ export default function Chat({ flow }: ChatType): JSX.Element {
/> />
)} )}
<ChatTrigger <ChatTrigger
canOpen={canOpen} canOpen={!!flowState?.input_keys}
open={open} open={open}
setOpen={setOpen} setOpen={setOpen}
isBuilt={isBuilt} isBuilt={isBuilt}

View file

@ -24,16 +24,16 @@ const AccordionTrigger = React.forwardRef<
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger> React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => ( >(({ className, children, ...props }, ref) => (
<AccordionPrimitive.Header className="flex"> <AccordionPrimitive.Header className="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger asChild ref={ref} {...props}>
ref={ref} <div
className={cn( className={cn(
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180", "cursor-pointer flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all [&[data-state=open]>svg]:rotate-180",
className className
)} )}
{...props}
> >
{children} {children}
<ChevronDownIcon className="h-4 w-4 text-muted-foreground transition-transform duration-200" /> <ChevronDownIcon className="h-4 w-4 text-muted-foreground transition-transform duration-200" />
</div>
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
)); ));

View file

@ -1,34 +0,0 @@
import { createContext, useCallback, useContext, useState } from "react";
const initialValue = {
updateSSEData: ({}) => {},
sseData: {},
isBuilding: false,
setIsBuilding: (isBuilding: boolean) => {},
};
const SSEContext = createContext(initialValue);
export function useSSE() {
return useContext(SSEContext);
}
export function SSEProvider({ children }) {
const [sseData, setSSEData] = useState({});
const [isBuilding, setIsBuilding] = useState(false);
const updateSSEData = useCallback((newData: any) => {
setSSEData((prevData) => ({
...prevData,
...newData,
}));
}, []);
return (
<SSEContext.Provider
value={{ sseData, updateSSEData, isBuilding, setIsBuilding }}
>
{children}
</SSEContext.Provider>
);
}

View file

@ -3,9 +3,7 @@ import { BrowserRouter } from "react-router-dom";
import { ReactFlowProvider } from "reactflow"; import { ReactFlowProvider } from "reactflow";
import { TooltipProvider } from "../components/ui/tooltip"; import { TooltipProvider } from "../components/ui/tooltip";
import { ApiInterceptor } from "../controllers/API/api"; import { ApiInterceptor } from "../controllers/API/api";
import { SSEProvider } from "./SSEContext";
import { AuthProvider } from "./authContext"; import { AuthProvider } from "./authContext";
import { LocationProvider } from "./locationContext";
export default function ContextWrapper({ children }: { children: ReactNode }) { export default function ContextWrapper({ children }: { children: ReactNode }) {
//element to wrap all context //element to wrap all context
@ -15,12 +13,8 @@ export default function ContextWrapper({ children }: { children: ReactNode }) {
<AuthProvider> <AuthProvider>
<TooltipProvider> <TooltipProvider>
<ReactFlowProvider> <ReactFlowProvider>
<LocationProvider>
<ApiInterceptor /> <ApiInterceptor />
<SSEProvider>
{children} {children}
</SSEProvider>
</LocationProvider>
</ReactFlowProvider> </ReactFlowProvider>
</TooltipProvider> </TooltipProvider>
</AuthProvider> </AuthProvider>

View file

@ -1,50 +0,0 @@
import { createContext, ReactNode, useState } from "react";
import { locationContextType } from "../types/typesContext";
//initial value for location context
const initialValue = {
//actual
current: window.location.pathname.replace(/\/$/g, "").split("/"),
isStackedOpen:
window.innerWidth > 1024 && window.location.pathname.split("/")[1]
? true
: false,
setCurrent: () => {},
setIsStackedOpen: () => {},
showSideBar: window.location.pathname.split("/")[1] ? true : false,
setShowSideBar: () => {},
extraNavigation: { title: "" },
setExtraNavigation: () => {},
extraComponent: <></>,
setExtraComponent: () => {},
};
export const locationContext = createContext<locationContextType>(initialValue);
export function LocationProvider({ children }: { children: ReactNode }) {
const [current, setCurrent] = useState(initialValue.current);
const [isStackedOpen, setIsStackedOpen] = useState(
initialValue.isStackedOpen
);
const [showSideBar, setShowSideBar] = useState(initialValue.showSideBar);
const [extraNavigation, setExtraNavigation] = useState({ title: "" });
const [extraComponent, setExtraComponent] = useState(<></>);
return (
<locationContext.Provider
value={{
isStackedOpen,
setIsStackedOpen,
current,
setCurrent,
showSideBar,
setShowSideBar,
extraNavigation,
setExtraNavigation,
extraComponent,
setExtraComponent,
}}
>
{children}
</locationContext.Provider>
);
}

View file

@ -1,105 +0,0 @@
import _ from "lodash";
import {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from "react";
import { getAll, getHealth } from "../controllers/API";
import useAlertStore from "../stores/alertStore";
import { APIKindType } from "../types/api";
import { typesContextType } from "../types/typesContext";
import { AuthContext } from "./authContext";
//context to share types adn functions from nodes to flow
const initialValue: typesContextType = {
types: {},
setTypes: () => {},
templates: {},
setTemplates: () => {},
data: {},
setData: () => {},
getTypes: () => {},
setFetchError: () => {},
fetchError: false,
setFilterEdge: (filter) => {},
getFilterEdge: [],
};
export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({});
const [templates, setTemplates] = useState({});
const [data, setData] = useState({});
const [fetchError, setFetchError] = useState(false);
const setLoading = useAlertStore((state) => state.setLoading);
const [getFilterEdge, setFilterEdge] = useState([]);
async function getTypes(): Promise<void> {
// We will keep a flag to handle the case where the component is unmounted before the API call resolves.
let isMounted = true;
try {
const result = await getAll();
// Make sure to only update the state if the component is still mounted.
if (isMounted && result?.status === 200) {
setLoading(false);
let { data } = _.cloneDeep(result);
setData((old) => ({ ...old, ...data }));
setTemplates(
Object.keys(data).reduce((acc, curr) => {
Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
//prevent wrong overwriting of the component template by a group of the same type
if (!data[curr][c].flow) acc[c] = data[curr][c];
});
return acc;
}, {})
);
// Set the types by reducing over the keys of the result data and updating the accumulator.
setTypes(
// Reverse the keys so the tool world does not overlap
Object.keys(data)
.reverse()
.reduce((acc, curr) => {
Object.keys(data[curr]).forEach((c: keyof APIKindType) => {
acc[c] = curr;
// Add the base classes to the accumulator as well.
data[curr][c].base_classes?.forEach((b) => {
acc[b] = curr;
});
});
return acc;
}, {})
);
}
} catch (error) {
console.error("An error has occurred while fetching types.");
console.log(error);
await getHealth().catch((e) => {
setFetchError(true);
});
}
}
return (
<typesContext.Provider
value={{
types,
setTypes,
setTemplates,
templates,
data,
setData,
getTypes,
fetchError,
setFetchError,
setFilterEdge,
getFilterEdge,
}}
>
{children}
</typesContext.Provider>
);
}

View file

@ -31,6 +31,7 @@ import {
tabsArray, tabsArray,
} from "../../utils/utils"; } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
import useFlowStore from "../../stores/flowStore";
const ApiModal = forwardRef( const ApiModal = forwardRef(
( (
@ -49,18 +50,18 @@ const ApiModal = forwardRef(
const tweak = useRef<tweakType>([]); const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]); const tweaksList = useRef<string[]>([]);
const [getTweak, setTweak] = useState<tweakType>([]); const [getTweak, setTweak] = useState<tweakType>([]);
const currentFlowState = useFlowsManagerStore( const flowState = useFlowStore(
(state) => state.currentFlowState (state) => state.flowState
); );
const pythonApiCode = getPythonApiCode( const pythonApiCode = getPythonApiCode(
flow, flow,
autoLogin, autoLogin,
tweak.current, tweak.current,
currentFlowState flowState
); );
const curl_code = getCurlCode(flow, autoLogin, tweak.current, currentFlowState); const curl_code = getCurlCode(flow, autoLogin, tweak.current, flowState);
const pythonCode = getPythonCode(flow, tweak.current, currentFlowState); const pythonCode = getPythonCode(flow, tweak.current, flowState);
const widgetCode = getWidgetCode(flow, autoLogin, currentFlowState); const widgetCode = getWidgetCode(flow, autoLogin, flowState);
const tweaksCode = buildTweaks(flow); const tweaksCode = buildTweaks(flow);
const codesArray = [ const codesArray = [
curl_code, curl_code,
@ -171,11 +172,11 @@ const ApiModal = forwardRef(
flow, flow,
autoLogin, autoLogin,
tweak.current, tweak.current,
currentFlowState flowState
); );
const curl_code = getCurlCode(flow, autoLogin, tweak.current, currentFlowState); const curl_code = getCurlCode(flow, autoLogin, tweak.current, flowState);
const pythonCode = getPythonCode(flow, tweak.current, currentFlowState); const pythonCode = getPythonCode(flow, tweak.current, flowState);
const widgetCode = getWidgetCode(flow, autoLogin, currentFlowState); const widgetCode = getWidgetCode(flow, autoLogin, flowState);
tabs![0].code = curl_code; tabs![0].code = curl_code;
tabs![1].code = pythonApiCode; tabs![1].code = pythonApiCode;

View file

@ -40,20 +40,19 @@ export default function FormModal({
}): JSX.Element { }): JSX.Element {
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
const currentFlowState = useFlowsManagerStore( const flowState = useFlowStore(
(state) => state.currentFlowState (state) => state.flowState
); );
const setCurrentFlowState = useFlowsManagerStore( const setFlowState = useFlowStore(
(state) => state.setCurrentFlowState (state) => state.setFlowState
); );
const [chatValue, setChatValue] = useState(() => { const [chatValue, setChatValue] = useState(() => {
try { try {
const formKeysData = currentFlowState?.formKeysData; if (!flowState) {
if (!formKeysData) { throw new Error("flowState is undefined");
throw new Error("formKeysData is undefined");
} }
const inputKeys = formKeysData.input_keys; const inputKeys = flowState.input_keys;
const handleKeys = formKeysData.handle_keys; const handleKeys = flowState.handle_keys;
const keyToUse = Object.keys(inputKeys!).find( const keyToUse = Object.keys(inputKeys!).find(
(key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === "" (key) => !handleKeys?.some((j) => j === key) && inputKeys![key] === ""
@ -68,7 +67,7 @@ export default function FormModal({
}); });
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]); const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
const template = useRef(currentFlowState?.formKeysData.template ?? undefined); const template = useRef(flowState?.template ?? undefined);
const { accessToken } = useContext(AuthContext); const { accessToken } = useContext(AuthContext);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const ws = useRef<WebSocket | null>(null); const ws = useRef<WebSocket | null>(null);
@ -77,11 +76,11 @@ export default function FormModal({
const messagesRef = useRef<HTMLDivElement | null>(null); const messagesRef = useRef<HTMLDivElement | null>(null);
const [chatKey, setChatKey] = useState(() => { const [chatKey, setChatKey] = useState(() => {
if (currentFlowState?.formKeysData?.input_keys) { if (flowState?.input_keys) {
return Object.keys(currentFlowState.formKeysData.input_keys!).find( return Object.keys(flowState.input_keys!).find(
(key) => (key) =>
!currentFlowState.formKeysData.handle_keys!.some((j) => j === key) && !flowState.handle_keys!.some((j) => j === key) &&
currentFlowState.formKeysData.input_keys![key] === "" flowState.input_keys![key] === ""
); );
} }
// TODO: return a sensible default // TODO: return a sensible default
@ -387,7 +386,7 @@ export default function FormModal({
let nodeValidationErrors = validateNodes(nodes, edges); let nodeValidationErrors = validateNodes(nodes, edges);
if (nodeValidationErrors.length === 0) { if (nodeValidationErrors.length === 0) {
setLockChat(true); setLockChat(true);
let inputs = currentFlowState?.formKeysData.input_keys; let inputs = flowState?.input_keys;
setChatValue(""); setChatValue("");
const message = inputs; const message = inputs;
addChatHistory(message!, true, chatKey!, template.current); addChatHistory(message!, true, chatKey!, template.current);
@ -399,10 +398,10 @@ export default function FormModal({
description: flow.description, description: flow.description,
chatKey: chatKey!, chatKey: chatKey!,
}); });
if (currentFlowState && chatKey) { if (flowState && chatKey) {
setCurrentFlowState((old: FlowState | undefined) => { setFlowState((old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!); let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![chatKey] = ""; newFlowState.input_keys![chatKey] = "";
return newFlowState; return newFlowState;
}); });
} }
@ -415,7 +414,7 @@ export default function FormModal({
} }
function clearChat(): void { function clearChat(): void {
setChatHistory([]); setChatHistory([]);
template.current = currentFlowState?.formKeysData.template; template.current = flowState?.template;
ws.current?.send(JSON.stringify({ clear_history: true })); ws.current?.send(JSON.stringify({ clear_history: true }));
if (lockChat) setLockChat(false); if (lockChat) setLockChat(false);
} }
@ -423,7 +422,7 @@ export default function FormModal({
function handleOnCheckedChange(checked: boolean, i: string) { function handleOnCheckedChange(checked: boolean, i: string) {
if (checked === true) { if (checked === true) {
setChatKey(i); setChatKey(i);
setChatValue(currentFlowState?.formKeysData.input_keys![i] ?? ""); setChatValue(flowState?.input_keys![i] ?? "");
} else { } else {
setChatKey(null!); setChatKey(null!);
setChatValue(""); setChatValue("");
@ -432,7 +431,7 @@ export default function FormModal({
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger hidden></DialogTrigger> <DialogTrigger hidden></DialogTrigger>
{currentFlowState && currentFlowState.formKeysData && ( {flowState && flowState && (
<DialogContent className="min-w-[80vw]"> <DialogContent className="min-w-[80vw]">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center"> <DialogTitle className="flex items-center">
@ -468,8 +467,8 @@ export default function FormModal({
</div> </div>
</div> </div>
{currentFlowState?.formKeysData?.input_keys {flowState?.input_keys
? Object.keys(currentFlowState?.formKeysData.input_keys!).map( ? Object.keys(flowState?.input_keys!).map(
(key, index) => ( (key, index) => (
<div className="file-component-accordion-div" key={index}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
@ -491,7 +490,7 @@ export default function FormModal({
handleOnCheckedChange(value, key) handleOnCheckedChange(value, key)
} }
size="small" size="small"
disabled={currentFlowState.formKeysData.handle_keys!.some( disabled={flowState.handle_keys!.some(
(t) => t === key (t) => t === key
)} )}
/> />
@ -502,7 +501,7 @@ export default function FormModal({
keyValue={key} keyValue={key}
> >
<div className="file-component-tab-column"> <div className="file-component-tab-column">
{currentFlowState?.formKeysData.handle_keys!.some( {flowState?.handle_keys!.some(
(t) => t === key (t) => t === key
) && ( ) && (
<div className="font-normal text-muted-foreground "> <div className="font-normal text-muted-foreground ">
@ -512,14 +511,14 @@ export default function FormModal({
<Textarea <Textarea
className="custom-scroll" className="custom-scroll"
value={ value={
currentFlowState?.formKeysData.input_keys![key] flowState?.input_keys![key]
} }
onChange={(e) => { onChange={(e) => {
if (currentFlowState) { if (flowState) {
setCurrentFlowState( setFlowState(
(old: FlowState | undefined) => { (old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!); let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![ newFlowState.input_keys![
key key
] = e.target.value; ] = e.target.value;
return newFlowState; return newFlowState;
@ -536,7 +535,7 @@ export default function FormModal({
) )
) )
: null} : null}
{currentFlowState?.formKeysData.memory_keys!.map((key, index) => ( {flowState?.memory_keys!.map((key, index) => (
<div className="file-component-accordion-div" key={index}> <div className="file-component-accordion-div" key={index}>
<AccordionComponent <AccordionComponent
trigger={ trigger={
@ -628,11 +627,11 @@ export default function FormModal({
sendMessage={sendMessage} sendMessage={sendMessage}
setChatValue={(value) => { setChatValue={(value) => {
setChatValue(value); setChatValue(value);
if (currentFlowState && chatKey) { if (flowState && chatKey) {
setCurrentFlowState( setFlowState(
(old: FlowState | undefined) => { (old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!); let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![ newFlowState.input_keys![
chatKey chatKey
] = value; ] = value;
return newFlowState; return newFlowState;

View file

@ -1,12 +1,5 @@
import _ from "lodash"; import _ from "lodash";
import { import { MouseEvent, useCallback, useEffect, useRef, useState } from "react";
MouseEvent,
useCallback,
useContext,
useEffect,
useRef,
useState,
} from "react";
import ReactFlow, { import ReactFlow, {
Background, Background,
Connection, Connection,
@ -18,13 +11,10 @@ import ReactFlow, {
SelectionDragHandler, SelectionDragHandler,
addEdge, addEdge,
updateEdge, updateEdge,
useEdgesState,
useNodesState,
} from "reactflow"; } from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode"; import GenericNode from "../../../../CustomNodes/GenericNode";
import Chat from "../../../../components/chatComponent"; import Chat from "../../../../components/chatComponent";
import Loading from "../../../../components/ui/loading"; import Loading from "../../../../components/ui/loading";
import { locationContext } from "../../../../contexts/locationContext";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
@ -43,6 +33,7 @@ import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
import ConnectionLineComponent from "../ConnectionLineComponent"; import ConnectionLineComponent from "../ConnectionLineComponent";
import SelectionMenu from "../SelectionMenuComponent"; import SelectionMenu from "../SelectionMenuComponent";
import ExtraSidebar from "../extraSidebarComponent"; import ExtraSidebar from "../extraSidebarComponent";
import { useLocationStore } from "../../../../stores/locationStore";
const nodeTypes = { const nodeTypes = {
genericNode: GenericNode, genericNode: GenericNode,
@ -64,11 +55,6 @@ export default function Page({
const setFilterEdge = useTypesStore((state) => state.setFilterEdge); const setFilterEdge = useTypesStore((state) => state.setFilterEdge);
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance);
const setReactFlowInstance = useFlowStore( const setReactFlowInstance = useFlowStore(
(state) => state.setReactFlowInstance (state) => state.setReactFlowInstance
@ -86,6 +72,13 @@ export default function Page({
const redo = useFlowsManagerStore((state) => state.redo); const redo = useFlowsManagerStore((state) => state.redo);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const paste = useFlowStore((state) => state.paste); const paste = useFlowStore((state) => state.paste);
const resetFlow = useFlowStore((state) => state.resetFlow);
const lastCopiedSelection = useFlowStore(
(state) => state.lastCopiedSelection
);
const setLastCopiedSelection = useFlowStore(
(state) => state.setLastCopiedSelection
);
const position = useRef({ x: 0, y: 0 }); const position = useRef({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
@ -163,7 +156,12 @@ export default function Page({
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false); const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
const { setExtraComponent, setExtraNavigation } = useContext(locationContext); const setExtraComponent = useLocationStore(
(state) => state.setExtraComponent
);
const setExtraNavigation = useLocationStore(
(state) => state.setExtraNavigation
);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const edgeUpdateSuccessful = useRef(true); const edgeUpdateSuccessful = useRef(true);
@ -177,13 +175,11 @@ export default function Page({
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
if (reactFlowInstance) { if (reactFlowInstance) {
useFlowStore.setState({ resetFlow({
nodes: flow?.data?.nodes ?? [], nodes: flow?.data?.nodes ?? [],
edges: flow?.data?.edges ?? [], edges: flow?.data?.edges ?? [],
}); viewport: flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 },
reactFlowInstance.setViewport( })
flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 }
);
} }
// Clear the previous timeout // Clear the previous timeout
@ -224,8 +220,7 @@ export default function Page({
const onMoveEnd: OnMove = useCallback(() => { const onMoveEnd: OnMove = useCallback(() => {
// 👇 make moving the canvas undoable // 👇 make moving the canvas undoable
autoSaveCurrentFlow(nodes, edges, reactFlowInstance?.getViewport()!); autoSaveCurrentFlow(nodes, edges, reactFlowInstance?.getViewport()!);
} }, [takeSnapshot, autoSaveCurrentFlow, nodes, edges, reactFlowInstance]);
, [takeSnapshot, autoSaveCurrentFlow, nodes, edges, reactFlowInstance]);
const onSelectionDragStart: SelectionDragHandler = useCallback(() => { const onSelectionDragStart: SelectionDragHandler = useCallback(() => {
// 👇 make dragging a selection undoable // 👇 make dragging a selection undoable
@ -290,14 +285,16 @@ export default function Page({
const newNode: NodeType = { const newNode: NodeType = {
id: newId, id: newId,
type: "genericNode", type: "genericNode",
position: {x: 0, y:0}, position: { x: 0, y: 0 },
data: { data: {
...data, ...data,
id: newId, id: newId,
}, },
}; };
paste({ nodes: [newNode], edges: [] }, {x: event.clientX, y: event.clientY}); paste(
{ nodes: [newNode], edges: [] },
{ x: event.clientX, y: event.clientY }
);
} else if (event.dataTransfer.types.some((types) => types === "Files")) { } else if (event.dataTransfer.types.some((types) => types === "Files")) {
takeSnapshot(); takeSnapshot();
if (event.dataTransfer.files.item(0)!.type === "application/json") { if (event.dataTransfer.files.item(0)!.type === "application/json") {
@ -384,7 +381,6 @@ export default function Page({
setFilterEdge([]); setFilterEdge([]);
}, []); }, []);
return ( return (
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
{!view && <ExtraSidebar />} {!view && <ExtraSidebar />}

View file

@ -28,16 +28,51 @@ import useFlowsManagerStore from "./flowsManagerStore";
// this is our useStore hook that we can use in our components to get parts of the store and call actions // this is our useStore hook that we can use in our components to get parts of the store and call actions
const useFlowStore = create<FlowStoreType>((set, get) => ({ const useFlowStore = create<FlowStoreType>((set, get) => ({
reactFlowInstance: null, sseData: {},
setReactFlowInstance: (newState) => { flowState: undefined,
set({ reactFlowInstance: newState });
},
nodes: [], nodes: [],
edges: [], edges: [],
isBuilding: false,
isBuilt: false, isBuilt: false,
reactFlowInstance: null,
lastCopiedSelection: null,
resetFlow: ({ nodes, edges, viewport }) => {
set({
nodes,
edges,
flowState: undefined,
sseData: {},
isBuilt: false,
});
get().reactFlowInstance!.setViewport(viewport);
},
updateSSEData: (sseData) => {
set((state) => ({ sseData: { ...state.sseData, ...sseData } }));
},
setIsBuilding: (isBuilding) => {
set({ isBuilding });
},
setIsBuilt: (isBuilt) => { setIsBuilt: (isBuilt) => {
set({ isBuilt }); set({ isBuilt });
}, },
setFlowState: (flowState) => {
const newFlowState =
typeof flowState === "function"
? flowState(get().flowState)
: flowState;
if(newFlowState !== get().flowState){
set(() => ({
flowState: newFlowState,
}));
}
},
setReactFlowInstance: (newState) => {
set({ reactFlowInstance: newState });
},
onNodesChange: (changes: NodeChange[]) => { onNodesChange: (changes: NodeChange[]) => {
set({ set({
nodes: applyNodeChanges(changes, get().nodes), nodes: applyNodeChanges(changes, get().nodes),
@ -52,29 +87,30 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
let newChange = typeof change === "function" ? change(get().nodes) : change; let newChange = typeof change === "function" ? change(get().nodes) : change;
let newEdges = cleanEdges(newChange, get().edges); let newEdges = cleanEdges(newChange, get().edges);
set({ edges: newEdges }); set({ edges: newEdges, nodes: newChange, flowState: undefined, isBuilt: false, sseData: {} });
set({ nodes: newChange });
useFlowsManagerStore const flowsManager = useFlowsManagerStore.getState()
.getState()
.autoSaveCurrentFlow( flowsManager.autoSaveCurrentFlow(
newChange, newChange,
newEdges, newEdges,
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }
); );
}, },
setEdges: (change) => { setEdges: (change) => {
let newChange = typeof change === "function" ? change(get().edges) : change; let newChange = typeof change === "function" ? change(get().edges) : change;
set({ edges: newChange }); set({ edges: newChange, flowState: undefined, isBuilt: false, sseData: {} });
useFlowsManagerStore const flowsManager = useFlowsManagerStore.getState()
.getState()
.autoSaveCurrentFlow( flowsManager.autoSaveCurrentFlow(
get().nodes, get().nodes,
newChange, newChange,
get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 }
); );
}, },
setNode: (id: string, change: Node | ((oldState: Node) => Node)) => { setNode: (id: string, change: Node | ((oldState: Node) => Node)) => {
let newChange = let newChange =
@ -204,6 +240,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
}); });
set({ edges: newEdges }); set({ edges: newEdges });
}, },
setLastCopiedSelection: (newSelection) => {
set({ lastCopiedSelection: newSelection });
},
})); }));
export default useFlowStore; export default useFlowStore;

View file

@ -10,8 +10,7 @@ import {
} from "../controllers/API"; } from "../controllers/API";
import { FlowType, NodeDataType } from "../types/flow"; import { FlowType, NodeDataType } from "../types/flow";
import { FlowState } from "../types/tabs"; import { FlowState } from "../types/tabs";
import { UseUndoRedoOptions } from "../types/typesContext"; import { FlowsManagerStoreType, UseUndoRedoOptions } from "../types/zustand/flowsManager";
import { FlowsManagerStoreType } from "../types/zustand/flowsManager";
import { import {
addVersionToDuplicates, addVersionToDuplicates,
createFlowComponent, createFlowComponent,
@ -39,7 +38,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
setCurrentFlowId: (currentFlowId: string) => { setCurrentFlowId: (currentFlowId: string) => {
set((state) => ({ set((state) => ({
currentFlowId, currentFlowId,
currentFlowState: state.flowsState[state.currentFlowId],
currentFlow: state.flows.find((flow) => flow.id === currentFlowId), currentFlow: state.flows.find((flow) => flow.id === currentFlowId),
})); }));
}, },
@ -53,23 +51,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
currentFlow: undefined, currentFlow: undefined,
isLoading: true, isLoading: true,
setIsLoading: (isLoading: boolean) => set({ isLoading }), setIsLoading: (isLoading: boolean) => set({ isLoading }),
flowsState: {},
currentFlowState: undefined,
setCurrentFlowState: (
flowState: FlowState | ((oldState: FlowState | undefined) => FlowState)
) => {
const newFlowState =
typeof flowState === "function"
? flowState(get().currentFlowState)
: flowState;
set((state) => ({
flowsState: {
...state.flowsState,
[state.currentFlowId]: newFlowState,
},
currentFlowState: newFlowState,
}));
},
refreshFlows: () => { refreshFlows: () => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
set({ isLoading: true }); set({ isLoading: true });

View file

@ -0,0 +1,28 @@
import { create } from "zustand";
import { LocationStoreType } from "../types/zustand/location";
export const useLocationStore = create<LocationStoreType>((set, get) => ({
current: window.location.pathname.replace(/\/$/g, "").split("/"),
isStackedOpen:
window.innerWidth > 1024 && window.location.pathname.split("/")[1]
? true
: false,
setCurrent: (newState) => {
set({ current: newState });
},
setIsStackedOpen: (newState) => {
set({ isStackedOpen: newState });
},
showSideBar: window.location.pathname.split("/")[1] ? true : false,
setShowSideBar: (newState) => {
set({ showSideBar: newState });
},
extraNavigation: { title: "" },
setExtraNavigation: (newState) => {
set({ extraNavigation: newState });
},
extraComponent: <></>,
setExtraComponent: (newState) => {
set({ extraComponent: newState });
},
}));

View file

@ -55,16 +55,14 @@ export type FlowsContextType = {
}; };
export type FlowsState = { export type FlowsState = {
[key: string]: FlowState; [key: string]: FlowState | undefined;
}; };
export type FlowState = { export type FlowState = {
formKeysData: {
template?: string; template?: string;
input_keys?: Object; input_keys?: Object;
memory_keys?: Array<string>; memory_keys?: Array<string>;
handle_keys?: Array<string>; handle_keys?: Array<string>;
};
}; };
export type errorsVarType = { export type errorsVarType = {

View file

@ -1,89 +0,0 @@
import { Edge, Node } from "reactflow";
import { AlertItemType } from "../alerts";
export type alertContextType = {
errorData: { title: string; list?: Array<string> };
setErrorData: (newState: { title: string; list?: Array<string> }) => void;
errorOpen: boolean;
setErrorOpen: (newState: boolean) => void;
noticeData: { title: string; link?: string };
setNoticeData: (newState: { title: string; link?: string }) => void;
noticeOpen: boolean;
setNoticeOpen: (newState: boolean) => void;
successData: { title: string };
setSuccessData: (newState: { title: string }) => void;
successOpen: boolean;
setSuccessOpen: (newState: boolean) => void;
notificationCenter: boolean;
setNotificationCenter: (newState: boolean) => void;
notificationList: Array<AlertItemType>;
pushNotificationList: (Object: AlertItemType) => void;
clearNotificationList: () => void;
removeFromNotificationList: (index: string) => void;
loading: boolean;
setLoading: (newState: boolean) => void;
modalContextOpen: boolean | null;
setModalContextOpen: (newState: boolean) => void;
};
export type darkContextType = {
dark: {};
setDark: (newState: {}) => void;
stars: number;
setStars: (stars: number) => void;
gradientIndex: number;
setGradientIndex: (index: number) => void;
};
export type locationContextType = {
current: Array<string>;
setCurrent: (newState: Array<string>) => void;
isStackedOpen: boolean;
setIsStackedOpen: (newState: boolean) => void;
showSideBar: boolean;
setShowSideBar: (newState: boolean) => void;
extraNavigation: {
title: string;
options?: Array<{
name: string;
href: string;
icon: React.ElementType;
children?: Array<JSX.Element>;
}>;
};
setExtraNavigation: (newState: {
title: string;
options?: Array<{
name: string;
href: string;
icon: React.ElementType;
children?: Array<JSX.Element>;
}>;
}) => void;
extraComponent: any;
setExtraComponent: (newState: JSX.Element) => void;
};
export type undoRedoContextType = {
undo: () => void;
redo: () => void;
takeSnapshot: () => void;
};
export type UseUndoRedoOptions = {
maxHistorySize: number;
enableShortcuts: boolean;
};
export type UseUndoRedo = (options?: UseUndoRedoOptions) => {
undo: () => void;
redo: () => void;
takeSnapshot: () => void;
canUndo: boolean;
canRedo: boolean;
};
export type HistoryItem = {
nodes: Node[];
edges: Edge[];
};

View file

@ -5,11 +5,20 @@ import {
OnEdgesChange, OnEdgesChange,
OnNodesChange, OnNodesChange,
ReactFlowInstance, ReactFlowInstance,
Viewport,
} from "reactflow"; } from "reactflow";
import { FlowState } from "../../tabs";
export type FlowStoreType = { export type FlowStoreType = {
updateSSEData: (sseData: object) => void;
sseData: object;
isBuilding: boolean;
setIsBuilding: (isBuilding: boolean) => void;
resetFlow: (flow: {nodes: Node[], edges: Edge[], viewport: Viewport}) => void;
reactFlowInstance: ReactFlowInstance | null; reactFlowInstance: ReactFlowInstance | null;
setReactFlowInstance: (newState: ReactFlowInstance) => void; setReactFlowInstance: (newState: ReactFlowInstance) => void;
flowState: FlowState | undefined;
setFlowState: (state: FlowState | undefined | ((oldState: FlowState | undefined) => FlowState)) => void;
nodes: Node[]; nodes: Node[];
edges: Edge[]; edges: Edge[];
onNodesChange: OnNodesChange; onNodesChange: OnNodesChange;
@ -24,6 +33,11 @@ export type FlowStoreType = {
selection: { nodes: any; edges: any }, selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number } position: { x: number; y: number; paneX?: number; paneY?: number }
) => void; ) => void;
lastCopiedSelection: { nodes: any; edges: any } | null;
setLastCopiedSelection: (
newSelection: { nodes: any; edges: any } | null
) => void;
isBuilt: boolean; isBuilt: boolean;
setIsBuilt: (isBuilt: boolean) => void; setIsBuilt: (isBuilt: boolean) => void;
}; };

View file

@ -10,9 +10,6 @@ export type FlowsManagerStoreType = {
setCurrentFlowId: (currentFlowId: string) => void; setCurrentFlowId: (currentFlowId: string) => void;
isLoading: boolean; isLoading: boolean;
setIsLoading: (isLoading: boolean) => void; setIsLoading: (isLoading: boolean) => void;
flowsState: FlowsState;
currentFlowState: FlowState | undefined;
setCurrentFlowState: (state: FlowState | ((oldState: FlowState | undefined) => FlowState)) => void;
refreshFlows: () => Promise<void>; refreshFlows: () => Promise<void>;
saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>; saveFlow: (flow: FlowType, silent?: boolean) => Promise<void>;
autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => void; autoSaveCurrentFlow: (nodes: Node[], edges: Edge[], viewport: Viewport) => void;
@ -26,3 +23,8 @@ export type FlowsManagerStoreType = {
redo: () => void; redo: () => void;
takeSnapshot: () => void; takeSnapshot: () => void;
}; };
export type UseUndoRedoOptions = {
maxHistorySize: number;
enableShortcuts: boolean;
};

View file

@ -0,0 +1,28 @@
export type LocationStoreType = {
current: Array<string>;
setCurrent: (newState: Array<string>) => void;
isStackedOpen: boolean;
setIsStackedOpen: (newState: boolean) => void;
showSideBar: boolean;
setShowSideBar: (newState: boolean) => void;
extraNavigation: {
title: string;
options?: Array<{
name: string;
href: string;
icon: React.ElementType;
children?: Array<JSX.Element>;
}>;
};
setExtraNavigation: (newState: {
title: string;
options?: Array<{
name: string;
href: string;
icon: React.ElementType;
children?: Array<JSX.Element>;
}>;
}) => void;
extraComponent: any;
setExtraComponent: (newState: JSX.Element) => void;
};

View file

@ -217,12 +217,11 @@ export function groupByFamily(
})); }));
} }
export function buildInputs(currentFlowState?: FlowState): string { export function buildInputs(flowState?: FlowState): string {
return currentFlowState && return flowState &&
currentFlowState.formKeysData && flowState.input_keys &&
currentFlowState.formKeysData.input_keys && Object.keys(flowState.input_keys!).length > 0
Object.keys(currentFlowState.formKeysData.input_keys!).length > 0 ? JSON.stringify(flowState.input_keys)
? JSON.stringify(currentFlowState.formKeysData.input_keys)
: '{"input": "message"}'; : '{"input": "message"}';
} }
@ -297,16 +296,15 @@ export function buildTweakObject(tweak: tweakType) {
* @param {FlowsState} tabsState - The current tabs state. * @param {FlowsState} tabsState - The current tabs state.
* @returns {string} - The chat input field * @returns {string} - The chat input field
*/ */
export function getChatInputField(flow: FlowType, currentFlowState?: FlowState) { export function getChatInputField(flow: FlowType, flowState?: FlowState) {
let chat_input_field = "text"; let chat_input_field = "text";
if ( if (
currentFlowState && flowState &&
currentFlowState.formKeysData && flowState.input_keys
currentFlowState.formKeysData.input_keys
) { ) {
chat_input_field = Object.keys( chat_input_field = Object.keys(
currentFlowState.formKeysData.input_keys! flowState.input_keys!
)[0]; )[0];
} }
return chat_input_field; return chat_input_field;
@ -321,7 +319,7 @@ export function getPythonApiCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[], tweak?: any[],
currentFlowState?: FlowState flowState?: FlowState
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
@ -330,7 +328,7 @@ export function getPythonApiCode(
// node.data.id // node.data.id
// } // }
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
const inputs = buildInputs(currentFlowState); const inputs = buildInputs(flowState);
return `import requests return `import requests
from typing import Optional from typing import Optional
@ -385,11 +383,11 @@ export function getCurlCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
tweak?: any[], tweak?: any[],
currentFlowState?: FlowState flowState?: FlowState
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
const inputs = buildInputs(currentFlowState); const inputs = buildInputs(flowState);
return `curl -X POST \\ return `curl -X POST \\
${window.location.protocol}//${ ${window.location.protocol}//${
@ -413,11 +411,11 @@ export function getCurlCode(
export function getPythonCode( export function getPythonCode(
flow: FlowType, flow: FlowType,
tweak?: any[], tweak?: any[],
currentFlowState?: FlowState flowState?: FlowState
): string { ): string {
const flowName = flow.name; const flowName = flow.name;
const tweaks = buildTweaks(flow); const tweaks = buildTweaks(flow);
const inputs = buildInputs(currentFlowState); const inputs = buildInputs(flowState);
return `from langflow import load_flow_from_json return `from langflow import load_flow_from_json
TWEAKS = ${ TWEAKS = ${
tweak && tweak.length > 0 tweak && tweak.length > 0
@ -438,12 +436,12 @@ flow(inputs)`;
export function getWidgetCode( export function getWidgetCode(
flow: FlowType, flow: FlowType,
isAuth: boolean, isAuth: boolean,
currentFlowState?: FlowState flowState?: FlowState
): string { ): string {
const flowId = flow.id; const flowId = flow.id;
const flowName = flow.name; const flowName = flow.name;
const inputs = buildInputs(currentFlowState); const inputs = buildInputs(flowState);
let chat_input_field = getChatInputField(flow, currentFlowState); let chat_input_field = getChatInputField(flow, flowState);
return `<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script> return `<script src="https://cdn.jsdelivr.net/gh/logspace-ai/langflow-embedded-chat@main/dist/build/static/js/bundle.min.js"></script>
@ -454,7 +452,7 @@ chat_input_field: Input key that you want the chat to send the user message with
window_title="${flowName}" window_title="${flowName}"
flow_id="${flowId}" flow_id="${flowId}"
${ ${
currentFlowState && currentFlowState.formKeysData flowState
? `chat_inputs='${inputs}' ? `chat_inputs='${inputs}'
chat_input_field="${chat_input_field}" chat_input_field="${chat_input_field}"
` `