added the implementation of flowsState in all the project

This commit is contained in:
Lucas Oliveira 2024-01-05 23:28:03 -03:00
commit 82f180315b
7 changed files with 193 additions and 189 deletions

View file

@ -8,8 +8,9 @@ import { FlowType } from "../../../types/flow";
import { FlowsContext } from "../../../contexts/flowsContext";
import useAlertStore from "../../../stores/alertStore";
import useFlowStore from "../../../stores/flowStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { parsedDataType } from "../../../types/components";
import { FlowsState } from "../../../types/tabs";
import { FlowState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent";
@ -25,11 +26,14 @@ export default function BuildTrigger({
isBuilt: boolean;
}): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
const { setTabsState, saveFlow } = useContext(FlowsContext);
const { saveFlow } = useContext(FlowsContext);
const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges);
const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setCurrentFlowState = useFlowsManagerStore(
(state) => state.setCurrentFlowState
);
const [isIconTouched, setIsIconTouched] = useState(false);
const eventClick = isBuilding ? "pointer-events-none" : "";
@ -99,15 +103,7 @@ export default function BuildTrigger({
// If the event is a log, log it
setSuccessData({ title: parsedData.log });
} else if (parsedData.input_keys !== undefined) {
setTabsState((old: FlowsState) => {
return {
...old,
[flowId]: {
...old[flowId],
formKeysData: parsedData,
},
};
});
setCurrentFlowState(parsedData);
} else {
// Otherwise, process the data
const isValid = processStreamResult(parsedData);

View file

@ -10,6 +10,7 @@ import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal";
import useFlowStore from "../../stores/flowStore";
import { NodeType } from "../../types/flow";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false);
@ -17,7 +18,7 @@ export default function Chat({ flow }: ChatType): JSX.Element {
const isBuilt = useFlowStore((state) => state.isBuilt);
const setIsBuilt = useFlowStore((state) => state.setIsBuilt);
const isPending = useFlowStore((state) => state.isPending);
const { tabsState } = useContext(FlowsContext);
const currentFlowState = useFlowsManagerStore((state) => state.currentFlowState);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
@ -61,10 +62,9 @@ export default function Chat({ flow }: ChatType): JSX.Element {
setIsBuilt(false);
}
if (
tabsState &&
tabsState[flow.id] &&
tabsState[flow.id].formKeysData &&
tabsState[flow.id].formKeysData.input_keys !== null
currentFlowState &&
currentFlowState.formKeysData &&
currentFlowState.formKeysData.input_keys !== null
) {
setCanOpen(true);
} else {
@ -72,7 +72,7 @@ export default function Chat({ flow }: ChatType): JSX.Element {
}
prevNodesRef.current = currentNodes;
}, [tabsState, flow.id]);
}, [currentFlowState, flow.id]);
return (
<>
@ -84,8 +84,8 @@ export default function Chat({ flow }: ChatType): JSX.Element {
isBuilt={isBuilt}
/>
{isBuilt &&
tabsState[flow.id] &&
tabsState[flow.id].formKeysData &&
currentFlowState &&
currentFlowState.formKeysData &&
canOpen && (
<FormModal
key={flow.id}

View file

@ -18,7 +18,7 @@ import {
LANGFLOW_SUPPORTED_TYPES,
} from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { TemplateVariableType } from "../../types/api";
import { tweakType, uniqueTweakType } from "../../types/components";
import { FlowType, NodeType } from "../../types/flow/index";
@ -48,17 +48,19 @@ const ApiModal = forwardRef(
const [activeTab, setActiveTab] = useState("0");
const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]);
const { tabsState } = useContext(FlowsContext);
const [getTweak, setTweak] = useState<tweakType>([]);
const currentFlowState = useFlowsManagerStore(
(state) => state.currentFlowState
);
const pythonApiCode = getPythonApiCode(
flow,
autoLogin,
tweak.current,
tabsState
currentFlowState
);
const curl_code = getCurlCode(flow, autoLogin, tweak.current, tabsState);
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
const widgetCode = getWidgetCode(flow, autoLogin, tabsState);
const curl_code = getCurlCode(flow, autoLogin, tweak.current, currentFlowState);
const pythonCode = getPythonCode(flow, tweak.current, currentFlowState);
const widgetCode = getWidgetCode(flow, autoLogin, currentFlowState);
const tweaksCode = buildTweaks(flow);
const codesArray = [
curl_code,
@ -169,11 +171,11 @@ const ApiModal = forwardRef(
flow,
autoLogin,
tweak.current,
tabsState
currentFlowState
);
const curl_code = getCurlCode(flow, autoLogin, tweak.current, tabsState);
const pythonCode = getPythonCode(flow, tweak.current, tabsState);
const widgetCode = getWidgetCode(flow, autoLogin, tabsState);
const curl_code = getCurlCode(flow, autoLogin, tweak.current, currentFlowState);
const pythonCode = getPythonCode(flow, tweak.current, currentFlowState);
const widgetCode = getWidgetCode(flow, autoLogin, currentFlowState);
tabs![0].code = curl_code;
tabs![1].code = pythonApiCode;

View file

@ -6,7 +6,7 @@ import { classNames } from "../../utils/utils";
import ChatInput from "./chatInput";
import ChatMessage from "./chatMessage";
import _ from "lodash";
import _, { cloneDeep } from "lodash";
import AccordionComponent from "../../components/AccordionComponent";
import IconComponent from "../../components/genericIconComponent";
import ToggleShadComponent from "../../components/toggleShadComponent";
@ -22,11 +22,11 @@ import {
import { Textarea } from "../../components/ui/textarea";
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore";
import useFlowStore from "../../stores/flowStore";
import { FlowsState } from "../../types/tabs";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { FlowState, FlowsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils";
export default function FormModal({
@ -38,12 +38,17 @@ export default function FormModal({
setOpen: (open: boolean) => void;
flow: FlowType;
}): JSX.Element {
const { tabsState, setTabsState } = useContext(FlowsContext);
const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges);
const currentFlowState = useFlowsManagerStore(
(state) => state.currentFlowState
);
const setCurrentFlowState = useFlowsManagerStore(
(state) => state.setCurrentFlowState
);
const [chatValue, setChatValue] = useState(() => {
try {
const { formKeysData } = tabsState[flow.id];
const formKeysData = currentFlowState?.formKeysData;
if (!formKeysData) {
throw new Error("formKeysData is undefined");
}
@ -63,23 +68,20 @@ export default function FormModal({
});
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
const template = useRef(tabsState[flow.id].formKeysData.template);
const template = useRef(currentFlowState?.formKeysData.template ?? undefined);
const { accessToken } = useContext(AuthContext);
const setErrorData = useAlertStore((state) => state.setErrorData);
const ws = useRef<WebSocket | null>(null);
const [lockChat, setLockChat] = useState(false);
const isOpen = useRef(open);
const messagesRef = useRef<HTMLDivElement | null>(null);
const id = useRef(flow.id);
const tabsStateFlowId = tabsState[flow.id];
const tabsStateFlowIdFormKeysData = tabsStateFlowId.formKeysData;
const [chatKey, setChatKey] = useState(() => {
if (tabsState[flow.id]?.formKeysData?.input_keys) {
return Object.keys(tabsState[flow.id].formKeysData.input_keys!).find(
if (currentFlowState?.formKeysData?.input_keys) {
return Object.keys(currentFlowState.formKeysData.input_keys!).find(
(key) =>
!tabsState[flow.id].formKeysData.handle_keys!.some(
(j) => j === key
) && tabsState[flow.id].formKeysData.input_keys![key] === ""
!currentFlowState.formKeysData.handle_keys!.some((j) => j === key) &&
currentFlowState.formKeysData.input_keys![key] === ""
);
}
// TODO: return a sensible default
@ -94,9 +96,6 @@ export default function FormModal({
useEffect(() => {
isOpen.current = open;
}, [open]);
useEffect(() => {
id.current = flow.id;
}, [flow.id, tabsStateFlowId, tabsStateFlowIdFormKeysData]);
var isStream = false;
@ -297,7 +296,7 @@ export default function FormModal({
function connectWS(): void {
try {
const urlWs = getWebSocketUrl(
id.current,
flow.id,
process.env.NODE_ENV === "development"
);
const newWs = new WebSocket(urlWs);
@ -388,7 +387,7 @@ export default function FormModal({
let nodeValidationErrors = validateNodes(nodes, edges);
if (nodeValidationErrors.length === 0) {
setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys;
let inputs = currentFlowState?.formKeysData.input_keys;
setChatValue("");
const message = inputs;
addChatHistory(message!, true, chatKey!, template.current);
@ -400,12 +399,13 @@ export default function FormModal({
description: flow.description,
chatKey: chatKey!,
});
setTabsState((old: FlowsState) => {
if (!chatKey) return old;
let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys![chatKey] = "";
return newTabsState;
});
if (currentFlowState && chatKey) {
setCurrentFlowState((old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![chatKey] = "";
return newFlowState;
});
}
} else {
setErrorData({
title: "Oops! Looks like you missed some required information:",
@ -415,7 +415,7 @@ export default function FormModal({
}
function clearChat(): void {
setChatHistory([]);
template.current = tabsState[id.current].formKeysData.template;
template.current = currentFlowState?.formKeysData.template;
ws.current?.send(JSON.stringify({ clear_history: true }));
if (lockChat) setLockChat(false);
}
@ -423,7 +423,7 @@ export default function FormModal({
function handleOnCheckedChange(checked: boolean, i: string) {
if (checked === true) {
setChatKey(i);
setChatValue(tabsState[flow.id].formKeysData.input_keys![i]);
setChatValue(currentFlowState?.formKeysData.input_keys![i] ?? "");
} else {
setChatKey(null!);
setChatValue("");
@ -432,7 +432,7 @@ export default function FormModal({
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger hidden></DialogTrigger>
{tabsState[flow.id].formKeysData && (
{currentFlowState && currentFlowState.formKeysData && (
<DialogContent className="min-w-[80vw]">
<DialogHeader>
<DialogTitle className="flex items-center">
@ -468,106 +468,103 @@ export default function FormModal({
</div>
</div>
{tabsState[id.current]?.formKeysData?.input_keys
? Object.keys(
tabsState[id.current].formKeysData.input_keys!
).map((key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{key}
</Badge>
{currentFlowState?.formKeysData?.input_keys
? Object.keys(currentFlowState?.formKeysData.input_keys!).map(
(key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{key}
</Badge>
<div
className="-mb-1"
onClick={(event) => {
event.stopPropagation();
}}
>
<ToggleShadComponent
enabled={chatKey === key}
setEnabled={(value) =>
handleOnCheckedChange(value, key)
<div
className="-mb-1"
onClick={(event) => {
event.stopPropagation();
}}
>
<ToggleShadComponent
enabled={chatKey === key}
setEnabled={(value) =>
handleOnCheckedChange(value, key)
}
size="small"
disabled={currentFlowState.formKeysData.handle_keys!.some(
(t) => t === key
)}
/>
</div>
</div>
}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
{currentFlowState?.formKeysData.handle_keys!.some(
(t) => t === key
) && (
<div className="font-normal text-muted-foreground ">
Source: Component
</div>
)}
<Textarea
className="custom-scroll"
value={
currentFlowState?.formKeysData.input_keys![key]
}
onChange={(e) => {
if (currentFlowState) {
setCurrentFlowState(
(old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![
key
] = e.target.value;
return newFlowState;
}
);
}
size="small"
disabled={tabsState[
id.current
].formKeysData.handle_keys!.some(
(t) => t === key
)}
/>
</div>
}}
disabled={chatKey === key}
placeholder="Enter text..."
></Textarea>
</div>
}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
{tabsState[id.current].formKeysData.handle_keys!.some(
(t) => t === key
) && (
<div className="font-normal text-muted-foreground ">
Source: Component
</div>
)}
<Textarea
className="custom-scroll"
value={
tabsState[id.current].formKeysData.input_keys![
key
]
}
onChange={(e) => {
setTabsState((old: FlowsState) => {
let newTabsState = _.cloneDeep(old);
newTabsState[
id.current
].formKeysData.input_keys![key] =
e.target.value;
return newTabsState;
});
}}
disabled={chatKey === key}
placeholder="Enter text..."
></Textarea>
</div>
</AccordionComponent>
</div>
))
</AccordionComponent>
</div>
)
)
: null}
{tabsState[id.current].formKeysData.memory_keys!.map(
(key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{key}
</Badge>
<div className="-mb-1">
<ToggleShadComponent
enabled={chatKey === key}
setEnabled={() => {}}
size="small"
disabled={true}
/>
</div>
</div>
}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
<div className="font-normal text-muted-foreground ">
Source: Memory
{currentFlowState?.formKeysData.memory_keys!.map((key, index) => (
<div className="file-component-accordion-div" key={index}>
<AccordionComponent
trigger={
<div className="file-component-badge-div">
<Badge variant="gray" size="md">
{key}
</Badge>
<div className="-mb-1">
<ToggleShadComponent
enabled={chatKey === key}
setEnabled={() => {}}
size="small"
disabled={true}
/>
</div>
</div>
</AccordionComponent>
</div>
)
)}
}
key={index}
keyValue={key}
>
<div className="file-component-tab-column">
<div className="font-normal text-muted-foreground ">
Source: Memory
</div>
</div>
</AccordionComponent>
</div>
))}
</div>
<div className="eraser-column-arrangement">
<div className="eraser-size">
@ -631,13 +628,17 @@ export default function FormModal({
sendMessage={sendMessage}
setChatValue={(value) => {
setChatValue(value);
setTabsState((old: FlowsState) => {
let newTabsState = _.cloneDeep(old);
newTabsState[id.current].formKeysData.input_keys![
chatKey!
] = value;
return newTabsState;
});
if (currentFlowState && chatKey) {
setCurrentFlowState(
(old: FlowState | undefined) => {
let newFlowState = cloneDeep(old!);
newFlowState.formKeysData.input_keys![
chatKey
] = value;
return newFlowState;
}
);
}
}}
inputRef={ref}
/>

View file

@ -1,20 +1,29 @@
import { create } from "zustand";
import { FlowsManagerStoreType } from "../types/zustand/flowsManager";
import { FlowState } from "../types/tabs";
import { FlowsManagerStoreType } from "../types/zustand/flowsManager";
const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
currentFlowId: "",
setCurrentFlowId: (currentFlowId: string) => {
set({ currentFlowId, currentFlow: get().flows.find((flow) => flow.id === currentFlowId) });
},
set((state) => ({
currentFlowId,
currentFlowState: state.flowsState[state.currentFlowId],
currentFlow: state.flows.find((flow) => flow.id === currentFlowId),
}));
},
flows: [],
currentFlow: undefined,
isLoading: true,
setIsLoading: (isLoading: boolean) => set({ isLoading }),
flowsState: {},
currentFlowState: undefined,
setCurrentFlowState: (flowState: FlowState | ((oldState: FlowState | undefined) => FlowState)) => {
const newFlowState = typeof flowState === "function" ? flowState(get().currentFlowState) : flowState;
setCurrentFlowState: (
flowState: FlowState | ((oldState: FlowState | undefined) => FlowState)
) => {
const newFlowState =
typeof flowState === "function"
? flowState(get().currentFlowState)
: flowState;
set((state) => ({
flowsState: {
...state.flowsState,
@ -23,7 +32,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
currentFlowState: newFlowState,
}));
},
}));
export default useFlowsManagerStore;

View file

@ -59,7 +59,6 @@ export type FlowsState = {
};
export type FlowState = {
isPending: boolean;
formKeysData: {
template?: string;
input_keys?: Object;

View file

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