diff --git a/src/backend/langflow/components/llms/OllamaLLM.py b/src/backend/langflow/components/llms/OllamaLLM.py
index 9bf00ad5d..fae8e1917 100644
--- a/src/backend/langflow/components/llms/OllamaLLM.py
+++ b/src/backend/langflow/components/llms/OllamaLLM.py
@@ -150,10 +150,9 @@ class OllamaLLM(CustomComponent):
"top_k": top_k,
"top_p": top_p,
}
-
- # None Value remove
- llm_params = {k: v for k, v in llm_params.items() if v is not None}
+ # None Value remove
+ llm_params = {k: v for k, v in llm_params.items() if v is not None}
try:
llm = Ollama(**llm_params)
diff --git a/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py b/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py
index bc8d78909..26afd765c 100644
--- a/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py
+++ b/src/backend/langflow/components/retrievers/VectaraSelfQueryRetriver.py
@@ -15,29 +15,21 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
display_name: str = "Vectara Self Query Retriever for Vectara Vector Store"
description: str = "Implementation of Vectara Self Query Retriever"
- documentation = (
- "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
- )
+ documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
beta = True
field_config = {
"code": {"show": True},
- "vectorstore": {
- "display_name": "Vector Store",
- "info": "Input Vectara Vectore Store"
- },
- "llm": {
- "display_name": "LLM",
- "info": "For self query retriever"
- },
- "document_content_description":{
- "display_name": "Document Content Description",
+ "vectorstore": {"display_name": "Vector Store", "info": "Input Vectara Vectore Store"},
+ "llm": {"display_name": "LLM", "info": "For self query retriever"},
+ "document_content_description": {
+ "display_name": "Document Content Description",
"info": "For self query retriever",
- },
+ },
"metadata_field_info": {
- "display_name": "Metadata Field Info",
- "info": "Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {\"name\":\"speech\",\"description\":\"what name of the speech\",\"type\":\"string or list[string]\"}.\nThe keys should remain constant(name, description, type)",
- },
+ "display_name": "Metadata Field Info",
+ "info": 'Each metadata field info is a string in the form of key value pair dictionary containing additional search metadata.\nExample input: {"name":"speech","description":"what name of the speech","type":"string or list[string]"}.\nThe keys should remain constant(name, description, type)',
+ },
}
def build(
@@ -47,24 +39,19 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
llm: BaseLanguageModel,
metadata_field_info: List[str],
) -> BaseRetriever:
-
metadata_field_obj = []
for meta in metadata_field_info:
meta_obj = json.loads(meta)
- if 'name' not in meta_obj or 'description' not in meta_obj or 'type' not in meta_obj :
- raise Exception('Incorrect metadata field info format.')
+ if "name" not in meta_obj or "description" not in meta_obj or "type" not in meta_obj:
+ raise Exception("Incorrect metadata field info format.")
attribute_info = AttributeInfo(
- name = meta_obj['name'],
- description = meta_obj['description'],
- type = meta_obj['type'],
+ name=meta_obj["name"],
+ description=meta_obj["description"],
+ type=meta_obj["type"],
)
metadata_field_obj.append(attribute_info)
return SelfQueryRetriever.from_llm(
- llm,
- vectorstore,
- document_content_description,
- metadata_field_obj,
- verbose=True
- )
\ No newline at end of file
+ llm, vectorstore, document_content_description, metadata_field_obj, verbose=True
+ )
diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx
index 0898ad58d..7ccb9d1c7 100644
--- a/src/frontend/src/App.tsx
+++ b/src/frontend/src/App.tsx
@@ -16,7 +16,6 @@ import {
FETCH_ERROR_MESSAGE,
} from "./constants/constants";
import { alertContext } from "./contexts/alertContext";
-import { FlowsContext } from "./contexts/flowsContext";
import { locationContext } from "./contexts/locationContext";
import { typesContext } from "./contexts/typesContext";
import Router from "./routes";
@@ -30,7 +29,6 @@ export default function App() {
setShowSideBar(true);
setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
- const { hardReset } = useContext(FlowsContext);
const {
errorData,
@@ -136,10 +134,7 @@ export default function App() {
{
- window.localStorage.removeItem("tabsData");
- window.localStorage.clear();
- hardReset();
- window.location.href = window.location.href;
+ // any reset function
}}
FallbackComponent={CrashErrorComponent}
>
diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
index 6208faff5..fc0ed31a9 100644
--- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx
@@ -31,6 +31,7 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API";
+import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api";
import { ParameterComponentType } from "../../../../types/components";
import { NodeDataType } from "../../../../types/flow";
@@ -70,13 +71,8 @@ export default function ParameterComponent({
const { setErrorData, modalContextOpen } = useContext(alertContext);
const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0);
- const {
- tabId,
- flows,
- nodes,
- edges,
- setNode,
- } = useContext(FlowsContext);
+ const { tabId, flows } = useContext(FlowsContext);
+ const { nodes, edges, setNode } = useFlow();
const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
@@ -133,8 +129,8 @@ export default function ParameterComponent({
let newNode = cloneDeep(oldNode);
newNode.data = {
- ...newNode.data
- }
+ ...newNode.data,
+ };
newNode.data.node.template[name].value = newValue;
diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx
index eb6d27137..6d343f9cd 100644
--- a/src/frontend/src/CustomNodes/GenericNode/index.tsx
+++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx
@@ -7,7 +7,6 @@ import InputComponent from "../../components/inputComponent";
import { Textarea } from "../../components/ui/textarea";
import { priorityFields } from "../../constants/constants";
import { useSSE } from "../../contexts/SSEContext";
-import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
@@ -17,6 +16,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, cn, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent";
+import useFlow from "../../stores/flowManagerStore";
export default function GenericNode({
data,
@@ -30,7 +30,7 @@ export default function GenericNode({
yPos: number;
}): JSX.Element {
const { types } = useContext(typesContext);
- const { deleteNode, setNode } = useContext(FlowsContext);
+ const { deleteNode, setNode } = useFlow();
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [inputName, setInputName] = useState(false);
const [nodeName, setNodeName] = useState(data.node!.display_name);
diff --git a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
index eb8f281a6..65531e995 100644
--- a/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
+++ b/src/frontend/src/components/chatComponent/buildTrigger/index.tsx
@@ -3,7 +3,6 @@ import { useContext, useState } from "react";
import Loading from "../../../components/ui/loading";
import { useSSE } from "../../../contexts/SSEContext";
import { alertContext } from "../../../contexts/alertContext";
-import { typesContext } from "../../../contexts/typesContext";
import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow";
@@ -13,6 +12,7 @@ import { FlowsState } from "../../../types/tabs";
import { validateNodes } from "../../../utils/reactflowUtils";
import RadialProgressComponent from "../../RadialProgress";
import IconComponent from "../../genericIconComponent";
+import useFlow from "../../../stores/flowManagerStore";
export default function BuildTrigger({
open,
@@ -25,7 +25,8 @@ export default function BuildTrigger({
isBuilt: boolean;
}): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE();
- const { setTabsState, saveFlow, nodes, edges } = useContext(FlowsContext);
+ const { setTabsState, saveFlow } = useContext(FlowsContext);
+ const { nodes, edges } = useFlow();
const { setErrorData, setSuccessData } = useContext(alertContext);
const [isIconTouched, setIsIconTouched] = useState(false);
const eventClick = isBuilding ? "pointer-events-none" : "";
@@ -36,10 +37,7 @@ export default function BuildTrigger({
if (isBuilding) {
return;
}
- const errors = validateNodes(
- nodes,
- edges
- );
+ const errors = validateNodes(nodes, edges);
if (errors.length > 0) {
setErrorData({
title: "Oops! Looks like you missed something",
diff --git a/src/frontend/src/components/chatComponent/index.tsx b/src/frontend/src/components/chatComponent/index.tsx
index 129e1f55c..b07732a64 100644
--- a/src/frontend/src/components/chatComponent/index.tsx
+++ b/src/frontend/src/components/chatComponent/index.tsx
@@ -9,11 +9,14 @@ import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal";
import { NodeType } from "../../types/flow";
+import useFlow from "../../stores/flowManagerStore";
export default function Chat({ flow }: ChatType): JSX.Element {
const [open, setOpen] = useState(false);
const [canOpen, setCanOpen] = useState(false);
- const { tabsState, isBuilt, setIsBuilt, isPending } = useContext(FlowsContext);
+ const { isBuilt, setIsBuilt, isPending } = useFlow();
+ const { tabsState } =
+ useContext(FlowsContext);
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx
index 6a9228f2d..e4e4bbb60 100644
--- a/src/frontend/src/components/codeTabsComponent/index.tsx
+++ b/src/frontend/src/components/codeTabsComponent/index.tsx
@@ -28,7 +28,6 @@ import {
TabsTrigger,
} from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
-import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore";
import { codeTabsPropsType } from "../../types/components";
import {
@@ -41,6 +40,7 @@ import { classNames } from "../../utils/utils";
import DictComponent from "../dictComponent";
import IconComponent from "../genericIconComponent";
import KeypairListComponent from "../keypairListComponent";
+import useFlow from "../../stores/flowManagerStore";
export default function CodeTabsComponent({
flow,
@@ -53,9 +53,9 @@ export default function CodeTabsComponent({
const [isCopied, setIsCopied] = useState(false);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState([]);
- const dark = useDarkStore((state) => state.dark);
+ const {dark} = useDarkStore();
- const { setNodes } = useContext(FlowsContext);
+ const { setNodes } = useFlow();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
useEffect(() => {
diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx
index 3aa7e6340..d330500cd 100644
--- a/src/frontend/src/components/headerComponent/index.tsx
+++ b/src/frontend/src/components/headerComponent/index.tsx
@@ -8,7 +8,6 @@ import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore";
-import { useStoreStore } from "../../stores/storeStore";
import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
@@ -30,12 +29,7 @@ export default function Header(): JSX.Element {
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate();
- const hasStore = useStoreStore((state) => state.hasStore);
-
- const dark = useDarkStore((state) => state.dark);
- const setDark = useDarkStore((state) => state.updateDark);
- const stars = useDarkStore((state) => state.stars);
- const gradientIndex = useDarkStore((state) => state.gradientIndex);
+ const { dark, setDark, stars, gradientIndex } = useDarkStore();
useEffect(() => {
if (dark) {
diff --git a/src/frontend/src/components/pageLayout/index.tsx b/src/frontend/src/components/pageLayout/index.tsx
index 9af894730..fb10f9550 100644
--- a/src/frontend/src/components/pageLayout/index.tsx
+++ b/src/frontend/src/components/pageLayout/index.tsx
@@ -12,7 +12,7 @@ export default function PageLayout({
description: string;
children: React.ReactNode;
button?: React.ReactNode;
- betaIcon: boolean;
+ betaIcon?: boolean;
}) {
return (
diff --git a/src/frontend/src/components/tagsSelectorComponent/index.tsx b/src/frontend/src/components/tagsSelectorComponent/index.tsx
index ce9da74ab..a6ffd953a 100644
--- a/src/frontend/src/components/tagsSelectorComponent/index.tsx
+++ b/src/frontend/src/components/tagsSelectorComponent/index.tsx
@@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from "react";
-import { useDarkStore } from "../../stores/darkStore";
import { cn } from "../../utils/utils";
import { Badge } from "../ui/badge";
@@ -24,7 +23,6 @@ export function TagsSelector({
: selectedTags.filter((_, i) => i !== index);
setSelectedTags(newArray);
};
- const dark = useDarkStore((state) => state.dark);
const scrollContainerRef = useRef
(null);
const fadeContainerRef = useRef(null);
diff --git a/src/frontend/src/contexts/flowsContext.tsx b/src/frontend/src/contexts/flowsContext.tsx
index 6487ad334..97cdfc77b 100644
--- a/src/frontend/src/contexts/flowsContext.tsx
+++ b/src/frontend/src/contexts/flowsContext.tsx
@@ -31,7 +31,7 @@ import {
updateFlowInDatabase,
uploadFlowsToDatabase,
} from "../controllers/API";
-import { APIClassType, APITemplateType } from "../types/api";
+import { APIClassType } from "../types/api";
import { tweakType } from "../types/components";
import {
FlowType,
@@ -46,12 +46,13 @@ import {
checkOldEdgesHandles,
cleanEdges,
createFlowComponent,
+ processFlowEdges,
removeFileNameFromComponents,
scapeJSONParse,
scapedJSONStringfy,
+ updateEdges,
updateEdgesHandleIds,
updateIds,
- updateTemplate,
} from "../utils/reactflowUtils";
import {
createRandomKey,
@@ -61,10 +62,12 @@ import {
import { alertContext } from "./alertContext";
import { AuthContext } from "./authContext";
import { typesContext } from "./typesContext";
+import useFlow from "../stores/flowManagerStore";
const uid = new ShortUniqueId({ length: 5 });
const FlowsContextInitialValue: FlowsContextType = {
+ //Remove tab id and get current id from url
tabId: "",
setTabId: (index: string) => {},
isLoading: true,
@@ -75,41 +78,16 @@ const FlowsContextInitialValue: FlowsContextType = {
flowData?: FlowType,
override?: boolean
) => "",
- deleteNode: () => {},
- deleteEdge: () => {},
- incrementNodeId: () => uid(),
downloadFlow: (flow: FlowType) => {},
downloadFlows: () => {},
uploadFlows: () => {},
uploadFlow: async () => "",
- isBuilt: false,
- setIsBuilt: (state: boolean) => {},
- hardReset: () => {},
saveFlow: async (flow?: FlowType, silent?: boolean) => {},
- lastCopiedSelection: null,
- setLastCopiedSelection: (selection: any) => {},
- isPending: false,
- setPending: (pending: boolean) => {},
tabsState: {},
setTabsState: () => {},
- getNodeId: (nodeType: string) => "",
- setTweak: (tweak: any) => {},
- getTweak: [],
- paste: (
- selection: { nodes: any; edges: any },
- position: { x: number; y: number; paneX?: number; paneY?: number }
- ) => {},
saveComponent: async (component: NodeDataType, override: boolean) => "",
deleteComponent: (key: string) => {},
version: "",
- nodes: [],
- setNodes: () => {},
- setNode: () => {},
- getNode: () => undefined,
- onNodesChange: () => {},
- edges: [],
- setEdges: () => {},
- onEdgesChange: () => {},
};
export const FlowsContext = createContext(
@@ -123,107 +101,10 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const [tabId, setTabId] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [flows, setFlows] = useState>([]);
- const [id, setId] = useState(uid());
- const { reactFlowInstance, setData } = useContext(typesContext);
- const [lastCopiedSelection, setLastCopiedSelection] = useState<{
- nodes: any;
- edges: any;
- } | null>(null);
+ const { setData } = useContext(typesContext);
const [tabsState, setTabsState] = useState({});
- const [getTweak, setTweak] = useState([]);
- const [nodes, setNodesInternal, onNodesChangeInternal] = useNodesState([]);
-
- const [edges, setEdgesInternal, onEdgesChangeInternal] = useEdgesState([]);
-
- const setPending = (pending: boolean) => {
- setTabsState((prev: FlowsState) => {
- return {
- ...prev,
- [tabId]: {
- ...prev[tabId],
- isPending: pending,
- },
- };
- });
- };
-
- const isPending = tabsState[tabId]?.isPending ?? false;
-
- const onNodesChange = useCallback(
- (change: NodeChange[]) => {
- onNodesChangeInternal(change);
- if(!isPending)
- setPending(true);
- },
- [onNodesChangeInternal, setPending, isPending]
- );
-
- const onEdgesChange = useCallback(
- (edges: EdgeChange[]) => {
- onEdgesChangeInternal(edges);
- if(!isPending)
- setPending(true);
- },
- [onEdgesChangeInternal, setPending, isPending]
- );
-
- const setNodes = (change: Node[] | ((oldState: Node[]) => Node[])) => {
- let newChange = typeof change === "function" ? change(nodes) : change;
- let newEdges = cleanEdges(newChange, edges);
-
- saveCurrentFlow(
- newChange,
- newEdges,
- reactFlowInstance?.getViewport() ?? { zoom: 1, x: 0, y: 0 }
- );
- setEdgesInternal(newEdges);
- setNodesInternal(newChange);
- };
-
- const setNode = (id: string, change: Node | ((oldState: Node) => Node)) => {
- let newChange =
- typeof change === "function"
- ? change(nodes.find((node) => node.id === id)!)
- : change;
-
- setNodes((oldNodes) =>
- oldNodes.map((node) => {
- if (node.id === id) {
- return newChange;
- }
- return node;
- })
- );
- };
-
- const getNode = (id: string) => {
- return nodes.find((node) => node.id === id);
- };
-
- const setEdges = (change: Edge[] | ((oldState: Edge[]) => Edge[])) => {
- let newChange = typeof change === "function" ? change(edges) : change;
-
- saveCurrentFlow(
- nodes,
- newChange,
- reactFlowInstance?.getViewport() ?? { zoom: 1, x: 0, y: 0 }
- );
- setEdgesInternal(newChange);
- };
-
- useEffect(() => {
- if (!isAuthenticated) {
- hardReset();
- }
- }, [isAuthenticated]);
-
- const newNodeId = useRef(uid());
-
- function incrementNodeId() {
- newNodeId.current = uid();
- return newNodeId.current;
- }
+ const {nodes, edges, paste, setPending, reactFlowInstance} = useFlow();
function refreshFlows() {
setIsLoading(true);
@@ -231,7 +112,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
if (DbData) {
try {
processFlows(DbData, false);
- updateStateWithDbData(DbData);
+ setFlows(DbData);
setIsLoading(false);
} catch (e) {}
}
@@ -281,31 +162,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
});
}
- function processFlowEdges(flow: FlowType) {
- if (!flow.data || !flow.data.edges) return;
- if (checkOldEdgesHandles(flow.data.edges)) {
- const newEdges = updateEdgesHandleIds(flow.data);
- flow.data.edges = newEdges;
- }
- //update edges colors
- flow.data.edges.forEach((edge) => {
- edge.className = "";
- edge.style = { stroke: "#555" };
- });
- }
-
- function updateStateWithDbData(tabsData: FlowType[]) {
- setFlows(tabsData);
- }
-
- function hardReset() {
- newNodeId.current = uid();
- setTabId("");
- setFlows([]);
- setIsLoading(true);
- setId(uid());
- }
-
/**
* Downloads the current flow as a JSON file
*/
@@ -353,11 +209,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
link.click();
});
}
-
- function getNodeId(nodeType: string) {
- return nodeType + "-" + incrementNodeId();
- }
-
/**
* Creates a file input and listens to a change event to upload a JSON flow file.
* If the file type is application/json, the file is read and parsed into a JSON object.
@@ -466,111 +317,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
processFlows(flows.filter((flow) => flow.id !== id));
}
}
- /**
- * Add a new flow to the list of flows.
- * @param flow Optional flow to add.
- */
- function paste(
- selectionInstance: { nodes: Node[]; edges: Edge[] },
- position: { x: number; y: number; paneX?: number; paneY?: number }
- ) {
- let minimumX = Infinity;
- let minimumY = Infinity;
- let idsMap = {};
- let newNodes: Node[] = nodes;
- let newEdges = edges;
- selectionInstance.nodes.forEach((node: Node) => {
- if (node.position.y < minimumY) {
- minimumY = node.position.y;
- }
- if (node.position.x < minimumX) {
- minimumX = node.position.x;
- }
- });
-
- const insidePosition = position.paneX
- ? { x: position.paneX + position.x, y: position.paneY! + position.y }
- : reactFlowInstance!.screenToFlowPosition({
- x: position.x,
- y: position.y,
- });
-
- selectionInstance.nodes.forEach((node: NodeType) => {
- // Generate a unique node ID
- let newId = getNodeId(node.data.type);
- idsMap[node.id] = newId;
-
- // Create a new node object
- const newNode: NodeType = {
- id: newId,
- type: "genericNode",
- position: {
- x: insidePosition.x + node.position!.x - minimumX,
- y: insidePosition.y + node.position!.y - minimumY,
- },
- data: {
- ..._.cloneDeep(node.data),
- id: newId,
- },
- };
-
- // Add the new node to the list of nodes in state
- newNodes = newNodes
- .map((node) => ({ ...node, selected: false }))
- .concat({ ...newNode, selected: false });
- });
- setNodes(newNodes);
-
- selectionInstance.edges.forEach((edge: Edge) => {
- let source = idsMap[edge.source];
- let target = idsMap[edge.target];
- const sourceHandleObject: sourceHandleType = scapeJSONParse(
- edge.sourceHandle!
- );
- let sourceHandle = scapedJSONStringfy({
- ...sourceHandleObject,
- id: source,
- });
- sourceHandleObject.id = source;
-
- edge.data.sourceHandle = sourceHandleObject;
- const targetHandleObject: targetHandleType = scapeJSONParse(
- edge.targetHandle!
- );
- let targetHandle = scapedJSONStringfy({
- ...targetHandleObject,
- id: target,
- });
- targetHandleObject.id = target;
- edge.data.targetHandle = targetHandleObject;
- let id =
- "reactflow__edge-" +
- source +
- sourceHandle +
- "-" +
- target +
- targetHandle;
- newEdges = addEdge(
- {
- source,
- target,
- sourceHandle,
- targetHandle,
- id,
- data: cloneDeep(edge.data),
- style: { stroke: "#555" },
- className:
- targetHandleObject.type === "Text"
- ? "stroke-gray-800 "
- : "stroke-gray-900 ",
- animated: targetHandleObject.type === "Text",
- selected: false,
- },
- newEdges.map((edge) => ({ ...edge, selected: false }))
- );
- });
- setEdges(newEdges);
- }
const addFlow = async (
newProject: Boolean,
@@ -634,26 +380,12 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
//add animation to text type edges
updateEdges(data.edges);
// updateNodes(data.nodes, data.edges);
- if (refreshIds) updateIds(data, getNodeId); // Assuming updateIds is defined elsewhere
+ if (refreshIds) updateIds(data); // Assuming updateIds is defined elsewhere
}
return data;
};
- const updateEdges = (edges: Edge[]) => {
- if (edges)
- edges.forEach((edge) => {
- const targetHandleObject: targetHandleType = scapeJSONParse(
- edge.targetHandle!
- );
- edge.className =
- (targetHandleObject.type === "Text"
- ? "stroke-gray-800 "
- : "stroke-gray-900 ") + " stroke-connection";
- edge.animated = targetHandleObject.type === "Text";
- });
- };
-
const createNewFlow = (
flowData: ReactFlowJsonObject | null,
flow: FlowType
@@ -697,7 +429,11 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const saveTimeoutId = useRef(null);
- const saveCurrentFlow = (nodes: Node[], edges: Edge[], viewport: Viewport) => {
+ const saveCurrentFlow = (
+ nodes: Node[],
+ edges: Edge[],
+ viewport: Viewport
+ ) => {
// Clear the previous timeout if it exists.
if (saveTimeoutId.current) {
clearTimeout(saveTimeoutId.current);
@@ -710,8 +446,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
saveFlow({ ...currentFlow, data: { nodes, edges, viewport } }, true);
}
}, 300); // Delay of 300ms.
- }
-
+ };
async function saveFlow(flow?: FlowType, silent?: boolean) {
let newFlow;
@@ -765,7 +500,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}
}
- const [isBuilt, setIsBuilt] = useState(false);
// Initialize state variable for the version
const [version, setVersion] = useState("");
useEffect(() => {
@@ -774,63 +508,26 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
});
}, []);
- function deleteNode(idx: string | Array) {
- setNodes((oldNodes) =>
- oldNodes.filter((node) =>
- typeof idx === "string" ? node.id !== idx : !idx.includes(node.id)
- )
- );
- }
-
- function deleteEdge(idx: string | Array) {
- setEdges((oldEdges) =>
- oldEdges.filter((edge) =>
- typeof idx === "string" ? edge.id !== idx : !idx.includes(edge.id)
- )
- );
- }
return (
{children}
diff --git a/src/frontend/src/contexts/typesContext.tsx b/src/frontend/src/contexts/typesContext.tsx
index 5db197e20..2abaf5275 100644
--- a/src/frontend/src/contexts/typesContext.tsx
+++ b/src/frontend/src/contexts/typesContext.tsx
@@ -16,8 +16,6 @@ import { AuthContext } from "./authContext";
//context to share types adn functions from nodes to flow
const initialValue: typesContextType = {
- reactFlowInstance: null,
- setReactFlowInstance: (newState: ReactFlowInstance) => {},
types: {},
setTypes: () => {},
templates: {},
@@ -34,8 +32,6 @@ export const typesContext = createContext(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({});
- const [reactFlowInstance, setReactFlowInstance] =
- useState(null);
const [templates, setTemplates] = useState({});
const [data, setData] = useState({});
const [fetchError, setFetchError] = useState(false);
@@ -95,14 +91,11 @@ export function TypesProvider({ children }: { children: ReactNode }) {
}
}
-
return (
{},
@@ -29,7 +29,10 @@ const defaultOptions: UseUndoRedoOptions = {
export const undoRedoContext = createContext(initialValue);
export function UndoRedoProvider({ children }) {
- const { tabId, flows, setNodes, setEdges, nodes, edges } = useContext(FlowsContext);
+ const { tabId, flows } =
+ useContext(FlowsContext);
+
+ const {setNodes, setEdges, nodes, edges} = useFlow();
const [past, setPast] = useState(flows.map(() => []));
const [future, setFuture] = useState(flows.map(() => []));
diff --git a/src/frontend/src/modals/ApiModal/index.tsx b/src/frontend/src/modals/ApiModal/index.tsx
index 93d85549d..97a476363 100644
--- a/src/frontend/src/modals/ApiModal/index.tsx
+++ b/src/frontend/src/modals/ApiModal/index.tsx
@@ -48,7 +48,8 @@ const ApiModal = forwardRef(
const [activeTab, setActiveTab] = useState("0");
const tweak = useRef([]);
const tweaksList = useRef([]);
- const { setTweak, getTweak, tabsState } = useContext(FlowsContext);
+ const { tabsState } = useContext(FlowsContext);
+ const [getTweak, setTweak] = useState([]);
const pythonApiCode = getPythonApiCode(
flow,
autoLogin,
diff --git a/src/frontend/src/modals/EditNodeModal/index.tsx b/src/frontend/src/modals/EditNodeModal/index.tsx
index 0b6ab5a39..d2e4a9e77 100644
--- a/src/frontend/src/modals/EditNodeModal/index.tsx
+++ b/src/frontend/src/modals/EditNodeModal/index.tsx
@@ -29,10 +29,7 @@ import {
limitScrollFieldsModal,
} from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
-import { FlowsContext } from "../../contexts/flowsContext";
-import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow";
-import { FlowsState } from "../../types/tabs";
import {
convertObjToArray,
convertValuesToNumbers,
@@ -41,6 +38,7 @@ import {
} from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal";
+import useFlow from "../../stores/flowManagerStore";
const EditNodeModal = forwardRef(
(
@@ -59,7 +57,7 @@ const EditNodeModal = forwardRef(
) => {
const [myData, setMyData] = useState(data);
- const { setPending, edges, setNode } = useContext(FlowsContext);
+ const { setPending, edges, setNode } = useFlow();
const { setModalContextOpen } = useContext(alertContext);
function changeAdvanced(n) {
diff --git a/src/frontend/src/modals/codeAreaModal/index.tsx b/src/frontend/src/modals/codeAreaModal/index.tsx
index cba033acd..880bbe4cb 100644
--- a/src/frontend/src/modals/codeAreaModal/index.tsx
+++ b/src/frontend/src/modals/codeAreaModal/index.tsx
@@ -26,7 +26,7 @@ export default function CodeAreaModal({
readonly = false,
}: codeAreaModalPropsType): JSX.Element {
const [code, setCode] = useState(value);
- const dark = useDarkStore((state) => state.dark);
+ const {dark} = useDarkStore();
const [height, setHeight] = useState(null);
const { setErrorData, setSuccessData } = useContext(alertContext);
diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx
index f2d2424a4..89bd0593a 100644
--- a/src/frontend/src/modals/exportModal/index.tsx
+++ b/src/frontend/src/modals/exportModal/index.tsx
@@ -6,7 +6,6 @@ import { Checkbox } from "../../components/ui/checkbox";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
-import { typesContext } from "../../contexts/typesContext";
import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal";
diff --git a/src/frontend/src/modals/formModal/index.tsx b/src/frontend/src/modals/formModal/index.tsx
index 99ea3a465..71115aef8 100644
--- a/src/frontend/src/modals/formModal/index.tsx
+++ b/src/frontend/src/modals/formModal/index.tsx
@@ -1,6 +1,5 @@
import { useContext, useEffect, useRef, useState } from "react";
import { alertContext } from "../../contexts/alertContext";
-import { typesContext } from "../../contexts/typesContext";
import { sendAllProps } from "../../types/api";
import { ChatMessageType } from "../../types/chat";
import { FlowType } from "../../types/flow";
@@ -26,6 +25,7 @@ import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API";
+import useFlow from "../../stores/flowManagerStore";
import { FlowsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils";
@@ -38,7 +38,8 @@ export default function FormModal({
setOpen: (open: boolean) => void;
flow: FlowType;
}): JSX.Element {
- const { tabsState, setTabsState, nodes, edges } = useContext(FlowsContext);
+ const { tabsState, setTabsState } = useContext(FlowsContext);
+ const { nodes, edges } = useFlow();
const [chatValue, setChatValue] = useState(() => {
try {
const { formKeysData } = tabsState[flow.id];
@@ -383,10 +384,7 @@ export default function FormModal({
}, [open]);
function sendMessage(): void {
- let nodeValidationErrors = validateNodes(
- nodes,
- edges
- );
+ let nodeValidationErrors = validateNodes(nodes, edges);
if (nodeValidationErrors.length === 0) {
setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys;
diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
index ce537084b..17777adcd 100644
--- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx
@@ -17,7 +17,6 @@ import ReactFlow, {
SelectionDragHandler,
addEdge,
updateEdge,
- useReactFlow,
} from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode";
import Chat from "../../../../components/chatComponent";
@@ -27,14 +26,14 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { locationContext } from "../../../../contexts/locationContext";
import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext";
+import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api";
-import { FlowType, NodeType, targetHandleType } from "../../../../types/flow";
-import { FlowsState } from "../../../../types/tabs";
+import { FlowType, NodeType } from "../../../../types/flow";
import {
generateFlow,
generateNodeFromFlow,
+ getNodeId,
isValidConnection,
- scapeJSONParse,
validateSelection,
} from "../../../../utils/reactflowUtils";
import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
@@ -53,26 +52,33 @@ export default function Page({
flow: FlowType;
view?: boolean;
}): JSX.Element {
- let {
- uploadFlow,
- getNodeId,
- paste,
- lastCopiedSelection,
- setLastCopiedSelection,
- deleteNode,
- deleteEdge,
- } = useContext(FlowsContext);
- const {
- types,
- reactFlowInstance,
- setReactFlowInstance,
- templates,
- setFilterEdge,
- } = useContext(typesContext);
+ let { uploadFlow, saveFlow } = useContext(FlowsContext);
+ const { types, templates, setFilterEdge } = useContext(typesContext);
const reactFlowWrapper = useRef(null);
+ const [lastCopiedSelection, setLastCopiedSelection] = useState<{
+ nodes: any;
+ edges: any;
+ } | null>(null);
+
const { takeSnapshot } = useContext(undoRedoContext);
- const { nodes, edges, setNodes, setEdges, onNodesChange, onEdgesChange, setPending, saveFlow, isPending } = useContext(FlowsContext);
+
+ const {
+ reactFlowInstance,
+ setReactFlowInstance,
+ nodes,
+ edges,
+ onNodesChange,
+ onEdgesChange,
+ onConnect,
+ setNodes,
+ setEdges,
+ deleteNode,
+ deleteEdge,
+ setPending,
+ isPending,
+ paste,
+ } = useFlow();
const position = useRef({ x: 0, y: 0 });
const [lastSelection, setLastSelection] =
@@ -136,11 +142,7 @@ export default function Page({
document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("mousemove", handleMouseMove);
};
- }, [
- lastCopiedSelection,
- lastSelection,
- takeSnapshot,
- ]);
+ }, [lastCopiedSelection, lastSelection, takeSnapshot]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
@@ -155,10 +157,12 @@ export default function Page({
useEffect(() => {
setLoading(true);
- if(reactFlowInstance){
+ if (reactFlowInstance) {
reactFlowInstance.setNodes(flow?.data?.nodes ?? []);
reactFlowInstance.setEdges(flow?.data?.edges ?? []);
- reactFlowInstance.setViewport(flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 });
+ reactFlowInstance.setViewport(
+ flow?.data?.viewport ?? { zoom: 1, x: 0, y: 0 }
+ );
}
// Clear the previous timeout
@@ -177,30 +181,10 @@ export default function Page({
};
}, [flow, reactFlowInstance]);
- const onConnect = useCallback(
+ const onConnectMod = useCallback(
(params: Connection) => {
takeSnapshot();
- setEdges((eds) =>
- addEdge(
- {
- ...params,
- data: {
- targetHandle: scapeJSONParse(params.targetHandle!),
- sourceHandle: scapeJSONParse(params.sourceHandle!),
- },
- style: { stroke: "#555" },
- className:
- ((scapeJSONParse(params.targetHandle!) as targetHandleType)
- .type === "Text"
- ? "stroke-foreground "
- : "stroke-foreground ") + " stroke-connection",
- animated:
- (scapeJSONParse(params.targetHandle!) as targetHandleType)
- .type === "Text",
- },
- eds
- )
- );
+ onConnect(params);
},
[setEdges, takeSnapshot, addEdge]
);
@@ -237,10 +221,6 @@ export default function Page({
if (event.dataTransfer.types.some((types) => types === "nodedata")) {
takeSnapshot();
- // Get the current bounds of the ReactFlow wrapper element
- const reactflowBounds =
- reactFlowWrapper.current?.getBoundingClientRect();
-
// Extract the data from the drag event and parse it as a JSON object
let data: { type: string; node?: APIClassType } = JSON.parse(
event.dataTransfer.getData("nodedata")
@@ -370,10 +350,9 @@ export default function Page({
}, []);
const onMove = useCallback(() => {
- if(!isPending)
- setPending(true);
+ if (!isPending) setPending(true);
}, [setPending]);
-
+
return (
{!view && }
@@ -399,7 +378,7 @@ export default function Page({
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
- onConnect={onConnect}
+ onConnect={onConnectMod}
disableKeyboardA11y={true}
onInit={setReactFlowInstance}
nodeTypes={nodeTypes}
@@ -479,9 +458,7 @@ export default function Page({
}}
/>
- {!view && (
-
- )}
+ {!view && }
) : (
<>>
diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
index 3e5342422..bb250e2aa 100644
--- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/index.tsx
@@ -10,6 +10,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
+import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore";
import { APIClassType, APIObjectType } from "../../../../types/api";
import {
@@ -28,13 +29,11 @@ import SidebarDraggableComponent from "./sideBarDraggableComponent";
export default function ExtraSidebar(): JSX.Element {
const { data, templates, getFilterEdge, setFilterEdge } =
useContext(typesContext);
- const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, isPending } =
- useContext(FlowsContext);
+ const { flows, tabId, uploadFlow, saveFlow } = useContext(FlowsContext);
- const hasStore = useStoreStore((state) => state.hasStore);
- const hasApiKey = useStoreStore((state) => state.hasApiKey);
- const validApiKey = useStoreStore((state) => state.validApiKey);
+ const { hasStore, hasApiKey, validApiKey } = useStoreStore();
+ const { isBuilt, isPending } = useFlow();
const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState("");
@@ -292,12 +291,9 @@ export default function ExtraSidebar(): JSX.Element {
{flow && flow.data && (
diff --git a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/sideBarDraggableComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/sideBarDraggableComponent/index.tsx
index 07aacaefc..0d1bcdddb 100644
--- a/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/sideBarDraggableComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/extraSidebarComponent/sideBarDraggableComponent/index.tsx
@@ -12,6 +12,7 @@ import { APIClassType } from "../../../../../types/api";
import {
createFlowComponent,
downloadNode,
+ getNodeId,
} from "../../../../../utils/reactflowUtils";
import { removeCountFromString } from "../../../../../utils/utils";
@@ -35,7 +36,7 @@ export default function SidebarDraggableComponent({
official: boolean;
}) {
const [open, setOpen] = useState(false);
- const { getNodeId, deleteComponent, version } = useContext(FlowsContext);
+ const { deleteComponent, version } = useContext(FlowsContext);
const { autoLogin, userData } = useContext(AuthContext);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const popoverRef = useRef(null);
diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
index ab6015e54..38fc175ae 100644
--- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
+++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx
@@ -13,6 +13,7 @@ import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal";
+import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore";
import { nodeToolbarPropsType } from "../../../../types/components";
import { FlowType } from "../../../../types/flow";
@@ -49,11 +50,8 @@ export default function NodeToolbarComponent({
data.node.template[templateField].type === "NestedDict")
).length
);
- const { getNodeId } = useContext(FlowsContext);
- const hasStore = useStoreStore((state) => state.hasStore);
- const hasApiKey = useStoreStore((state) => state.hasApiKey);
- const validApiKey = useStoreStore((state) => state.validApiKey);
+ const { hasStore, hasApiKey, validApiKey } = useStoreStore();
function canMinimize() {
let countHandles: number = 0;
@@ -66,16 +64,9 @@ export default function NodeToolbarComponent({
const isMinimal = canMinimize();
const isGroup = data.node?.flow ? true : false;
- const {
- paste,
- saveComponent,
- version,
- flows,
- nodes,
- edges,
- setNodes,
- setEdges,
- } = useContext(FlowsContext);
+ const { paste, nodes, edges, setNodes, setEdges } = useFlow();
+
+ const { saveComponent, flows, version } = useContext(FlowsContext);
const { takeSnapshot } = useContext(undoRedoContext);
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
@@ -123,7 +114,7 @@ export default function NodeToolbarComponent({
case "ungroup":
takeSnapshot();
updateFlowPosition(position, data.node?.flow!);
- expandGroupNode(data, getNodeId, nodes, edges, setNodes, setEdges);
+ expandGroupNode(data, nodes, edges, setNodes, setEdges);
break;
case "override":
setShowOverrideModal(true);
diff --git a/src/frontend/src/stores/darkStore.tsx b/src/frontend/src/stores/darkStore.tsx
index 1acf37ead..3eb9a55ee 100644
--- a/src/frontend/src/stores/darkStore.tsx
+++ b/src/frontend/src/stores/darkStore.tsx
@@ -8,9 +8,9 @@ type State = {
};
type Action = {
- updateDark: (dark: State["dark"]) => void;
- updateStars: (starts: State["stars"]) => void;
- updateGradientIndex: (gradientIndex: State["gradientIndex"]) => void;
+ setDark: (dark: State["dark"]) => void;
+ setStars: (starts: State["stars"]) => void;
+ setGradientIndex: (gradientIndex: State["gradientIndex"]) => void;
};
function gradientIndexInitialState() {
@@ -23,9 +23,9 @@ export const useDarkStore = create((set) => ({
dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false,
stars: 0,
gradientIndex: gradientIndexInitialState(),
- updateDark: (dark) => set(() => ({ dark: dark })),
- updateStars: (starts) => set(() => ({ stars: starts })),
- updateGradientIndex: (gradientIndex) =>
+ setDark: (dark) => set(() => ({ dark: dark })),
+ setStars: (starts) => set(() => ({ stars: starts })),
+ setGradientIndex: (gradientIndex) =>
set(() => ({ gradientIndex: gradientIndex })),
}));
diff --git a/src/frontend/src/stores/flowManagerStore.ts b/src/frontend/src/stores/flowManagerStore.ts
new file mode 100644
index 000000000..db6501460
--- /dev/null
+++ b/src/frontend/src/stores/flowManagerStore.ts
@@ -0,0 +1,248 @@
+import { cloneDeep } from "lodash";
+import {
+ Connection,
+ Edge,
+ EdgeChange,
+ Node,
+ NodeChange,
+ OnConnect,
+ OnEdgesChange,
+ OnNodesChange,
+ ReactFlowInstance,
+ addEdge,
+ applyEdgeChanges,
+ applyNodeChanges,
+} from "reactflow";
+import { create } from "zustand";
+import {
+ NodeDataType,
+ NodeType,
+ sourceHandleType,
+ targetHandleType,
+} from "../types/flow";
+import {
+ cleanEdges,
+ getHandleId,
+ getNodeId,
+ scapeJSONParse,
+ scapedJSONStringfy,
+} from "../utils/reactflowUtils";
+
+type RFState = {
+ reactFlowInstance: ReactFlowInstance | null;
+ setReactFlowInstance: (newState: ReactFlowInstance) => void;
+ nodes: Node[];
+ edges: Edge[];
+ onNodesChange: OnNodesChange;
+ onEdgesChange: OnEdgesChange;
+ setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
+ setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
+ setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
+ getNode: (id: string) => Node | undefined;
+ onConnect: OnConnect;
+ deleteNode: (nodeId: string | Array) => void;
+ deleteEdge: (edgeId: string | Array) => void;
+ paste: (
+ selection: { nodes: any; edges: any },
+ position: { x: number; y: number; paneX?: number; paneY?: number }
+ ) => void;
+ isBuilt: boolean;
+ setIsBuilt: (isBuilt: boolean) => void;
+ isPending: boolean;
+ setPending: (pending: boolean) => void;
+};
+
+// this is our useStore hook that we can use in our components to get parts of the store and call actions
+const useFlow = create((set, get) => ({
+ reactFlowInstance: null,
+ setReactFlowInstance: (newState) => {
+ set({ reactFlowInstance: newState });
+ },
+ nodes: [],
+ edges: [],
+ isBuilt: false,
+ setIsBuilt: (isBuilt) => {
+ set({ isBuilt });
+ },
+ onNodesChange: (changes: NodeChange[]) => {
+ set({
+ nodes: applyNodeChanges(changes, get().nodes),
+ });
+ if (!get().isPending) set({ isPending: true });
+ },
+ onEdgesChange: (changes: EdgeChange[]) => {
+ set({
+ edges: applyEdgeChanges(changes, get().edges),
+ });
+ if (!get().isPending) set({ isPending: true });
+ },
+ setNodes: (change) => {
+ let newChange = typeof change === "function" ? change(get().nodes) : change;
+ let newEdges = cleanEdges(newChange, get().edges);
+
+ set({ edges: newEdges });
+ set({ nodes: newChange });
+ },
+ setEdges: (change) => {
+ let newChange = typeof change === "function" ? change(get().edges) : change;
+
+ set({ edges: newChange });
+ },
+ setNode: (id: string, change: Node | ((oldState: Node) => Node)) => {
+ let newChange =
+ typeof change === "function"
+ ? change(get().nodes.find((node) => node.id === id)!)
+ : change;
+
+ get().setNodes((oldNodes) =>
+ oldNodes.map((node) => {
+ if (node.id === id) {
+ return newChange;
+ }
+ return node;
+ })
+ );
+ },
+ getNode: (id: string) => {
+ return get().nodes.find((node) => node.id === id);
+ },
+ onConnect: (connection: Connection) => {
+ set({
+ edges: addEdge(
+ {
+ ...connection,
+ data: {
+ targetHandle: scapeJSONParse(connection.targetHandle!),
+ sourceHandle: scapeJSONParse(connection.sourceHandle!),
+ },
+ style: { stroke: "#555" },
+ className:
+ ((scapeJSONParse(connection.targetHandle!) as targetHandleType)
+ .type === "Text"
+ ? "stroke-foreground "
+ : "stroke-foreground ") + " stroke-connection",
+ animated:
+ (scapeJSONParse(connection.targetHandle!) as targetHandleType)
+ .type === "Text",
+ },
+ get().edges
+ ),
+ });
+ },
+ deleteNode: (nodeId) => {
+ get().setNodes(
+ get().nodes.filter((node) =>
+ typeof nodeId === "string"
+ ? node.id !== nodeId
+ : !nodeId.includes(node.id)
+ )
+ );
+ },
+ deleteEdge: (edgeId) => {
+ get().setEdges(
+ get().edges.filter((edge) =>
+ typeof edgeId === "string"
+ ? edge.id !== edgeId
+ : !edgeId.includes(edge.id)
+ )
+ );
+ },
+ paste: (selection, position) => {
+ let minimumX = Infinity;
+ let minimumY = Infinity;
+ let idsMap = {};
+ let newNodes: Node[] = get().nodes;
+ let newEdges = get().edges;
+ selection.nodes.forEach((node: Node) => {
+ if (node.position.y < minimumY) {
+ minimumY = node.position.y;
+ }
+ if (node.position.x < minimumX) {
+ minimumX = node.position.x;
+ }
+ });
+
+ const insidePosition = position.paneX
+ ? { x: position.paneX + position.x, y: position.paneY! + position.y }
+ : get().reactFlowInstance!.screenToFlowPosition({
+ x: position.x,
+ y: position.y,
+ });
+
+ selection.nodes.forEach((node: NodeType) => {
+ // Generate a unique node ID
+ let newId = getNodeId(node.data.type);
+ idsMap[node.id] = newId;
+
+ // Create a new node object
+ const newNode: NodeType = {
+ id: newId,
+ type: "genericNode",
+ position: {
+ x: insidePosition.x + node.position!.x - minimumX,
+ y: insidePosition.y + node.position!.y - minimumY,
+ },
+ data: {
+ ...cloneDeep(node.data),
+ id: newId,
+ },
+ };
+
+ // Add the new node to the list of nodes in state
+ newNodes = newNodes
+ .map((node) => ({ ...node, selected: false }))
+ .concat({ ...newNode, selected: false });
+ });
+ set({ nodes: newNodes });
+
+ selection.edges.forEach((edge: Edge) => {
+ let source = idsMap[edge.source];
+ let target = idsMap[edge.target];
+ const sourceHandleObject: sourceHandleType = scapeJSONParse(
+ edge.sourceHandle!
+ );
+ let sourceHandle = scapedJSONStringfy({
+ ...sourceHandleObject,
+ id: source,
+ });
+ sourceHandleObject.id = source;
+
+ edge.data.sourceHandle = sourceHandleObject;
+ const targetHandleObject: targetHandleType = scapeJSONParse(
+ edge.targetHandle!
+ );
+ let targetHandle = scapedJSONStringfy({
+ ...targetHandleObject,
+ id: target,
+ });
+ targetHandleObject.id = target;
+ edge.data.targetHandle = targetHandleObject;
+ let id = getHandleId(source, sourceHandle, target, targetHandle);
+ newEdges = addEdge(
+ {
+ source,
+ target,
+ sourceHandle,
+ targetHandle,
+ id,
+ data: cloneDeep(edge.data),
+ style: { stroke: "#555" },
+ className:
+ targetHandleObject.type === "Text"
+ ? "stroke-gray-800 "
+ : "stroke-gray-900 ",
+ animated: targetHandleObject.type === "Text",
+ selected: false,
+ },
+ newEdges.map((edge) => ({ ...edge, selected: false }))
+ );
+ });
+ set({ edges: newEdges });
+ },
+ isPending: false,
+ setPending: (pending: boolean) => {
+ set({ isPending: pending });
+ },
+}));
+
+export default useFlow;
diff --git a/src/frontend/src/types/chat/index.ts b/src/frontend/src/types/chat/index.ts
index 829f612c1..62cb4eb78 100644
--- a/src/frontend/src/types/chat/index.ts
+++ b/src/frontend/src/types/chat/index.ts
@@ -1,6 +1,6 @@
import { FlowType } from "../flow";
-export type ChatType = { flow: FlowType; };
+export type ChatType = { flow: FlowType };
export type ChatMessageType = {
message: string | Object;
template?: string;
diff --git a/src/frontend/src/types/tabs/index.ts b/src/frontend/src/types/tabs/index.ts
index 18619cfe3..c5c0e680b 100644
--- a/src/frontend/src/types/tabs/index.ts
+++ b/src/frontend/src/types/tabs/index.ts
@@ -1,35 +1,34 @@
-import { XYPosition, Node, NodeChange, Edge, EdgeChange } from "reactflow";
+import { Edge, EdgeChange, Node, NodeChange, XYPosition } from "reactflow";
import { tweakType } from "../components";
import { FlowType, NodeDataType } from "../flow";
-import { Dispatch, SetStateAction } from "react";
type OnChange = (changes: ChangesType[]) => void;
export type FlowsContextType = {
+ //keep
saveFlow: (flow?: FlowType, silent?: boolean) => Promise;
tabId: string;
+ //keep
isLoading: boolean;
setTabId: (index: string) => void;
- flows: Array;
- deleteNode: (idx: string | Array) => void;
- deleteEdge: (idx: string | Array) => void;
+ //keep
removeFlow: (id: string) => void;
+ //keep
addFlow: (
newProject: boolean,
flow?: FlowType,
override?: boolean,
position?: XYPosition
) => Promise;
- incrementNodeId: () => string;
downloadFlow: (
flow: FlowType,
flowName: string,
flowDescription?: string
) => void;
+ //keep
downloadFlows: () => void;
+ //keep
uploadFlows: () => void;
- isBuilt: boolean;
- setIsBuilt: (state: boolean) => void;
uploadFlow: ({
newProject,
file,
@@ -41,34 +40,17 @@ export type FlowsContextType = {
isComponent?: boolean;
position?: XYPosition;
}) => Promise;
- hardReset: () => void;
- getNodeId: (nodeType: string) => string;
- isPending: boolean;
- setPending: (pending: boolean) => void;
tabsState: FlowsState;
- setTabsState: (update: FlowsState | ((oldState: FlowsState) => FlowsState)) => void;
- paste: (
- selection: { nodes: any; edges: any },
- position: { x: number; y: number; paneX?: number; paneY?: number }
+ setTabsState: (
+ update: FlowsState | ((oldState: FlowsState) => FlowsState)
) => void;
- lastCopiedSelection: { nodes: any; edges: any } | null;
- setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
- setTweak: (tweak: tweakType) => tweakType | void;
- getTweak: tweakType;
saveComponent: (
component: NodeDataType,
override: boolean
) => Promise;
deleteComponent: (key: string) => void;
version: string;
- nodes: Array;
- setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
- setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
- getNode: (id: string) => Node | undefined;
- onNodesChange: OnChange;
- edges: Array;
- setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
- onEdgesChange: OnChange;
+ flows: Array;
};
export type FlowsState = {
diff --git a/src/frontend/src/types/typesContext/index.ts b/src/frontend/src/types/typesContext/index.ts
index cfe206694..e04c40ce7 100644
--- a/src/frontend/src/types/typesContext/index.ts
+++ b/src/frontend/src/types/typesContext/index.ts
@@ -7,8 +7,6 @@ const template: { [char: string]: APIClassType } = {};
const data: { [char: string]: string } = {};
export type typesContextType = {
- reactFlowInstance: ReactFlowInstance | null;
- setReactFlowInstance: (newState: ReactFlowInstance) => void;
types: typeof types;
setTypes: (newState: {}) => void;
templates: typeof template;
diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts
index 9a05f8d2e..0f3af8a55 100644
--- a/src/frontend/src/utils/reactflowUtils.ts
+++ b/src/frontend/src/utils/reactflowUtils.ts
@@ -27,6 +27,7 @@ import {
updateEdgesHandleIdsType,
} from "../types/utils/reactflowUtils";
import { getFieldTitle, toTitleCase } from "./utils";
+const uid = new ShortUniqueId({ length: 5 });
export function cleanEdges(nodes: Node[], edges: Edge[]) {
let newEdges = _.cloneDeep(edges);
@@ -82,7 +83,7 @@ export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {
export function isValidConnection(
{ source, target, sourceHandle, targetHandle }: Connection,
nodes: Node[],
- edges: Edge[],
+ edges: Edge[]
) {
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
@@ -98,16 +99,12 @@ export function isValidConnection(
) {
let targetNode = nodes.find((node) => node.id === target!)?.data?.node;
if (!targetNode) {
- if (
- !edges
- .find((e) => e.targetHandle === targetHandle)
- ) {
+ if (!edges.find((e) => e.targetHandle === targetHandle)) {
return true;
}
} else if (
(!targetNode.template[targetHandleObject.fieldName].list &&
- !edges
- .find((e) => e.targetHandle === targetHandle)) ||
+ !edges.find((e) => e.targetHandle === targetHandle)) ||
targetNode.template[targetHandleObject.fieldName].list
) {
return true;
@@ -153,7 +150,6 @@ export function updateTemplate(
export function updateIds(
newFlow: ReactFlowJsonObject,
- getNodeId: (type: string) => string
) {
let idsMap = {};
@@ -272,6 +268,20 @@ export function validateNodes(nodes: Node[], edges: Edge[]) {
return nodes.flatMap((n: NodeType) => validateNode(n, edges));
}
+export function updateEdges(edges: Edge[]) {
+ if (edges)
+ edges.forEach((edge) => {
+ const targetHandleObject: targetHandleType = scapeJSONParse(
+ edge.targetHandle!
+ );
+ edge.className =
+ (targetHandleObject.type === "Text"
+ ? "stroke-gray-800 "
+ : "stroke-gray-900 ") + " stroke-connection";
+ edge.animated = targetHandleObject.type === "Text";
+ });
+};
+
export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
const existingNames = flows.map((item) => item.name);
let newName = flow.name;
@@ -499,13 +509,26 @@ export function getMiddlePoint(nodes: Node[]) {
return { x: averageX, y: averageY };
}
+export function getNodeId(nodeType: string) {
+ return nodeType + "-" + uid();
+}
+
+export function getHandleId(source: string, sourceHandle: string, target: string, targetHandle: string){
+ return "reactflow__edge-" +
+ source +
+ sourceHandle +
+ "-" +
+ target +
+ targetHandle;
+}
+
export function generateFlow(
selection: OnSelectionChangeParams,
nodes: Node[],
edges: Edge[],
name: string
): generateFlowType {
- const newFlowData = {nodes, edges, viewport: { zoom: 1, x: 0, y: 0 }};
+ const newFlowData = { nodes, edges, viewport: { zoom: 1, x: 0, y: 0 } };
const uid = new ShortUniqueId({ length: 5 });
/* remove edges that are not connected to selected nodes on both ends
in future we can save this edges to when ungrouping reconect to the old nodes
@@ -539,14 +562,10 @@ export function generateFlow(
export function filterFlow(
selection: OnSelectionChangeParams,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
- setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void,
+ setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) {
- setNodes((nodes) =>
- nodes.filter((node) => !selection.nodes.includes(node))
- );
- setEdges((edges) =>
- edges.filter((edge) => !selection.edges.includes(edge))
- );
+ setNodes((nodes) => nodes.filter((node) => !selection.nodes.includes(node)));
+ setEdges((edges) => edges.filter((edge) => !selection.edges.includes(edge)));
}
export function findLastNode({ nodes, edges }: findLastNodeType) {
@@ -572,7 +591,7 @@ export function updateFlowPosition(NewPosition: XYPosition, flow: FlowType) {
export function concatFlows(
flow: FlowType,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
- setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void,
+ setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) {
const { nodes, edges } = flow.data!;
setNodes((old) => [...old, ...nodes]);
@@ -916,16 +935,28 @@ function updateEdgesIds(edges: Edge[], idsMap: { [key: string]: string }) {
});
}
+export function processFlowEdges(flow: FlowType) {
+ if (!flow.data || !flow.data.edges) return;
+ if (checkOldEdgesHandles(flow.data.edges)) {
+ const newEdges = updateEdgesHandleIds(flow.data);
+ flow.data.edges = newEdges;
+ }
+ //update edges colors
+ flow.data.edges.forEach((edge) => {
+ edge.className = "";
+ edge.style = { stroke: "#555" };
+ });
+}
+
export function expandGroupNode(
groupNode: NodeDataType,
- getNodeId: (type: string) => string,
nodes: Node[],
edges: Edge[],
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) {
const { template, flow } = _.cloneDeep(groupNode.node!);
- const idsMap = updateIds(flow!.data!, getNodeId);
+ const idsMap = updateIds(flow!.data!);
updateProxyIdsOnTemplate(template, idsMap);
let flowEdges = edges;
updateEdgesIds(flowEdges, idsMap);
diff --git a/tests/test_endpoints.py b/tests/test_endpoints.py
index 2e2a65b64..338224004 100644
--- a/tests/test_endpoints.py
+++ b/tests/test_endpoints.py
@@ -578,4 +578,4 @@ def test_async_task_processing_vector_store(client, added_vector_store, created_
# Validate that the task completed successfully and the result is as expected
assert "result" in task_status_json, task_status_json
assert "output" in task_status_json["result"], task_status_json["result"]
- assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"]
\ No newline at end of file
+ assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"]