This commit is contained in:
cristhianzl 2024-01-05 11:38:33 -03:00
commit 34592f87e8
30 changed files with 437 additions and 556 deletions

View file

@ -150,10 +150,9 @@ class OllamaLLM(CustomComponent):
"top_k": top_k, "top_k": top_k,
"top_p": top_p, "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: try:
llm = Ollama(**llm_params) llm = Ollama(**llm_params)

View file

@ -15,29 +15,21 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
display_name: str = "Vectara Self Query Retriever for Vectara Vector Store" display_name: str = "Vectara Self Query Retriever for Vectara Vector Store"
description: str = "Implementation of Vectara Self Query Retriever" description: str = "Implementation of Vectara Self Query Retriever"
documentation = ( documentation = "https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
"https://python.langchain.com/docs/integrations/retrievers/self_query/vectara_self_query"
)
beta = True beta = True
field_config = { field_config = {
"code": {"show": True}, "code": {"show": True},
"vectorstore": { "vectorstore": {"display_name": "Vector Store", "info": "Input Vectara Vectore Store"},
"display_name": "Vector Store", "llm": {"display_name": "LLM", "info": "For self query retriever"},
"info": "Input Vectara Vectore Store" "document_content_description": {
}, "display_name": "Document Content Description",
"llm": {
"display_name": "LLM",
"info": "For self query retriever"
},
"document_content_description":{
"display_name": "Document Content Description",
"info": "For self query retriever", "info": "For self query retriever",
}, },
"metadata_field_info": { "metadata_field_info": {
"display_name": "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)", "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( def build(
@ -47,24 +39,19 @@ class VectaraSelfQueryRetriverComponent(CustomComponent):
llm: BaseLanguageModel, llm: BaseLanguageModel,
metadata_field_info: List[str], metadata_field_info: List[str],
) -> BaseRetriever: ) -> BaseRetriever:
metadata_field_obj = [] metadata_field_obj = []
for meta in metadata_field_info: for meta in metadata_field_info:
meta_obj = json.loads(meta) meta_obj = json.loads(meta)
if 'name' not in meta_obj or 'description' not in meta_obj or 'type' not in meta_obj : 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.') raise Exception("Incorrect metadata field info format.")
attribute_info = AttributeInfo( attribute_info = AttributeInfo(
name = meta_obj['name'], name=meta_obj["name"],
description = meta_obj['description'], description=meta_obj["description"],
type = meta_obj['type'], type=meta_obj["type"],
) )
metadata_field_obj.append(attribute_info) metadata_field_obj.append(attribute_info)
return SelfQueryRetriever.from_llm( return SelfQueryRetriever.from_llm(
llm, llm, vectorstore, document_content_description, metadata_field_obj, verbose=True
vectorstore, )
document_content_description,
metadata_field_obj,
verbose=True
)

View file

@ -16,7 +16,6 @@ import {
FETCH_ERROR_MESSAGE, FETCH_ERROR_MESSAGE,
} from "./constants/constants"; } from "./constants/constants";
import { alertContext } from "./contexts/alertContext"; import { alertContext } from "./contexts/alertContext";
import { FlowsContext } from "./contexts/flowsContext";
import { locationContext } from "./contexts/locationContext"; import { locationContext } from "./contexts/locationContext";
import { typesContext } from "./contexts/typesContext"; import { typesContext } from "./contexts/typesContext";
import Router from "./routes"; import Router from "./routes";
@ -30,7 +29,6 @@ export default function App() {
setShowSideBar(true); setShowSideBar(true);
setIsStackedOpen(true); setIsStackedOpen(true);
}, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]); }, [location.pathname, setCurrent, setIsStackedOpen, setShowSideBar]);
const { hardReset } = useContext(FlowsContext);
const { const {
errorData, errorData,
@ -136,10 +134,7 @@ export default function App() {
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<ErrorBoundary <ErrorBoundary
onReset={() => { onReset={() => {
window.localStorage.removeItem("tabsData"); // any reset function
window.localStorage.clear();
hardReset();
window.location.href = window.location.href;
}} }}
FallbackComponent={CrashErrorComponent} FallbackComponent={CrashErrorComponent}
> >

View file

@ -31,6 +31,7 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import { postCustomComponentUpdate } from "../../../../controllers/API"; import { postCustomComponentUpdate } from "../../../../controllers/API";
import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api"; import { APIClassType } from "../../../../types/api";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import { NodeDataType } from "../../../../types/flow"; import { NodeDataType } from "../../../../types/flow";
@ -70,13 +71,8 @@ export default function ParameterComponent({
const { setErrorData, modalContextOpen } = useContext(alertContext); const { setErrorData, modalContextOpen } = useContext(alertContext);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const [position, setPosition] = useState(0); const [position, setPosition] = useState(0);
const { const { tabId, flows } = useContext(FlowsContext);
tabId, const { nodes, edges, setNode } = useFlow();
flows,
nodes,
edges,
setNode,
} = useContext(FlowsContext);
const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null; const flow = flows.find((flow) => flow.id === tabId)?.data?.nodes ?? null;
@ -133,8 +129,8 @@ export default function ParameterComponent({
let newNode = cloneDeep(oldNode); let newNode = cloneDeep(oldNode);
newNode.data = { newNode.data = {
...newNode.data ...newNode.data,
} };
newNode.data.node.template[name].value = newValue; newNode.data.node.template[name].value = newValue;

View file

@ -7,7 +7,6 @@ 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 { useSSE } from "../../contexts/SSEContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext"; import { typesContext } from "../../contexts/typesContext";
import { undoRedoContext } from "../../contexts/undoRedoContext"; import { undoRedoContext } from "../../contexts/undoRedoContext";
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent"; import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
@ -17,6 +16,7 @@ import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
import { classNames, cn, getFieldTitle } from "../../utils/utils"; import { classNames, cn, getFieldTitle } from "../../utils/utils";
import ParameterComponent from "./components/parameterComponent"; import ParameterComponent from "./components/parameterComponent";
import useFlow from "../../stores/flowManagerStore";
export default function GenericNode({ export default function GenericNode({
data, data,
@ -30,7 +30,7 @@ export default function GenericNode({
yPos: number; yPos: number;
}): JSX.Element { }): JSX.Element {
const { types } = useContext(typesContext); const { types } = useContext(typesContext);
const { deleteNode, setNode } = useContext(FlowsContext); const { deleteNode, setNode } = useFlow();
const name = nodeIconsLucide[data.type] ? data.type : types[data.type]; const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
const [inputName, setInputName] = useState(false); const [inputName, setInputName] = useState(false);
const [nodeName, setNodeName] = useState(data.node!.display_name); const [nodeName, setNodeName] = useState(data.node!.display_name);

View file

@ -3,7 +3,6 @@ import { useContext, useState } from "react";
import Loading from "../../../components/ui/loading"; import Loading from "../../../components/ui/loading";
import { useSSE } from "../../../contexts/SSEContext"; import { useSSE } from "../../../contexts/SSEContext";
import { alertContext } from "../../../contexts/alertContext"; import { alertContext } from "../../../contexts/alertContext";
import { typesContext } from "../../../contexts/typesContext";
import { postBuildInit } from "../../../controllers/API"; import { postBuildInit } from "../../../controllers/API";
import { FlowType } from "../../../types/flow"; import { FlowType } from "../../../types/flow";
@ -13,6 +12,7 @@ import { FlowsState } 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 useFlow from "../../../stores/flowManagerStore";
export default function BuildTrigger({ export default function BuildTrigger({
open, open,
@ -25,7 +25,8 @@ export default function BuildTrigger({
isBuilt: boolean; isBuilt: boolean;
}): JSX.Element { }): JSX.Element {
const { updateSSEData, isBuilding, setIsBuilding, sseData } = useSSE(); 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 { setErrorData, setSuccessData } = useContext(alertContext);
const [isIconTouched, setIsIconTouched] = useState(false); const [isIconTouched, setIsIconTouched] = useState(false);
const eventClick = isBuilding ? "pointer-events-none" : ""; const eventClick = isBuilding ? "pointer-events-none" : "";
@ -36,10 +37,7 @@ export default function BuildTrigger({
if (isBuilding) { if (isBuilding) {
return; return;
} }
const errors = validateNodes( const errors = validateNodes(nodes, edges);
nodes,
edges
);
if (errors.length > 0) { if (errors.length > 0) {
setErrorData({ setErrorData({
title: "Oops! Looks like you missed something", title: "Oops! Looks like you missed something",

View file

@ -9,11 +9,14 @@ import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API"; import { getBuildStatus } from "../../controllers/API";
import FormModal from "../../modals/formModal"; import FormModal from "../../modals/formModal";
import { NodeType } from "../../types/flow"; import { NodeType } from "../../types/flow";
import useFlow from "../../stores/flowManagerStore";
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 [canOpen, setCanOpen] = useState(false);
const { tabsState, isBuilt, setIsBuilt, isPending } = useContext(FlowsContext); const { isBuilt, setIsBuilt, isPending } = useFlow();
const { tabsState } =
useContext(FlowsContext);
useEffect(() => { useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {

View file

@ -28,7 +28,6 @@ import {
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants"; import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import { codeTabsPropsType } from "../../types/components"; import { codeTabsPropsType } from "../../types/components";
import { import {
@ -41,6 +40,7 @@ import { classNames } from "../../utils/utils";
import DictComponent from "../dictComponent"; import DictComponent from "../dictComponent";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import KeypairListComponent from "../keypairListComponent"; import KeypairListComponent from "../keypairListComponent";
import useFlow from "../../stores/flowManagerStore";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,
@ -53,9 +53,9 @@ export default function CodeTabsComponent({
const [isCopied, setIsCopied] = useState<Boolean>(false); const [isCopied, setIsCopied] = useState<Boolean>(false);
const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null); const [data, setData] = useState(flow ? flow["data"]!["nodes"] : null);
const [openAccordion, setOpenAccordion] = useState<string[]>([]); const [openAccordion, setOpenAccordion] = useState<string[]>([]);
const dark = useDarkStore((state) => state.dark); const {dark} = useDarkStore();
const { setNodes } = useContext(FlowsContext); const { setNodes } = useFlow();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
useEffect(() => { useEffect(() => {

View file

@ -8,7 +8,6 @@ import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import { useStoreStore } from "../../stores/storeStore";
import { gradients } from "../../utils/styleUtils"; import { gradients } from "../../utils/styleUtils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
@ -30,12 +29,7 @@ export default function Header(): JSX.Element {
const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext); const { logout, autoLogin, isAdmin, userData } = useContext(AuthContext);
const navigate = useNavigate(); const navigate = useNavigate();
const hasStore = useStoreStore((state) => state.hasStore); const { dark, setDark, stars, gradientIndex } = useDarkStore();
const dark = useDarkStore((state) => state.dark);
const setDark = useDarkStore((state) => state.updateDark);
const stars = useDarkStore((state) => state.stars);
const gradientIndex = useDarkStore((state) => state.gradientIndex);
useEffect(() => { useEffect(() => {
if (dark) { if (dark) {

View file

@ -12,7 +12,7 @@ export default function PageLayout({
description: string; description: string;
children: React.ReactNode; children: React.ReactNode;
button?: React.ReactNode; button?: React.ReactNode;
betaIcon: boolean; betaIcon?: boolean;
}) { }) {
return ( return (
<div className="flex h-screen w-full flex-col"> <div className="flex h-screen w-full flex-col">

View file

@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useDarkStore } from "../../stores/darkStore";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import { Badge } from "../ui/badge"; import { Badge } from "../ui/badge";
@ -24,7 +23,6 @@ export function TagsSelector({
: selectedTags.filter((_, i) => i !== index); : selectedTags.filter((_, i) => i !== index);
setSelectedTags(newArray); setSelectedTags(newArray);
}; };
const dark = useDarkStore((state) => state.dark);
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
const fadeContainerRef = useRef<HTMLDivElement>(null); const fadeContainerRef = useRef<HTMLDivElement>(null);

View file

@ -31,7 +31,7 @@ import {
updateFlowInDatabase, updateFlowInDatabase,
uploadFlowsToDatabase, uploadFlowsToDatabase,
} from "../controllers/API"; } from "../controllers/API";
import { APIClassType, APITemplateType } from "../types/api"; import { APIClassType } from "../types/api";
import { tweakType } from "../types/components"; import { tweakType } from "../types/components";
import { import {
FlowType, FlowType,
@ -46,12 +46,13 @@ import {
checkOldEdgesHandles, checkOldEdgesHandles,
cleanEdges, cleanEdges,
createFlowComponent, createFlowComponent,
processFlowEdges,
removeFileNameFromComponents, removeFileNameFromComponents,
scapeJSONParse, scapeJSONParse,
scapedJSONStringfy, scapedJSONStringfy,
updateEdges,
updateEdgesHandleIds, updateEdgesHandleIds,
updateIds, updateIds,
updateTemplate,
} from "../utils/reactflowUtils"; } from "../utils/reactflowUtils";
import { import {
createRandomKey, createRandomKey,
@ -61,10 +62,12 @@ import {
import { alertContext } from "./alertContext"; import { alertContext } from "./alertContext";
import { AuthContext } from "./authContext"; import { AuthContext } from "./authContext";
import { typesContext } from "./typesContext"; import { typesContext } from "./typesContext";
import useFlow from "../stores/flowManagerStore";
const uid = new ShortUniqueId({ length: 5 }); const uid = new ShortUniqueId({ length: 5 });
const FlowsContextInitialValue: FlowsContextType = { const FlowsContextInitialValue: FlowsContextType = {
//Remove tab id and get current id from url
tabId: "", tabId: "",
setTabId: (index: string) => {}, setTabId: (index: string) => {},
isLoading: true, isLoading: true,
@ -75,41 +78,16 @@ const FlowsContextInitialValue: FlowsContextType = {
flowData?: FlowType, flowData?: FlowType,
override?: boolean override?: boolean
) => "", ) => "",
deleteNode: () => {},
deleteEdge: () => {},
incrementNodeId: () => uid(),
downloadFlow: (flow: FlowType) => {}, downloadFlow: (flow: FlowType) => {},
downloadFlows: () => {}, downloadFlows: () => {},
uploadFlows: () => {}, uploadFlows: () => {},
uploadFlow: async () => "", uploadFlow: async () => "",
isBuilt: false,
setIsBuilt: (state: boolean) => {},
hardReset: () => {},
saveFlow: async (flow?: FlowType, silent?: boolean) => {}, saveFlow: async (flow?: FlowType, silent?: boolean) => {},
lastCopiedSelection: null,
setLastCopiedSelection: (selection: any) => {},
isPending: false,
setPending: (pending: boolean) => {},
tabsState: {}, tabsState: {},
setTabsState: () => {}, 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) => "", saveComponent: async (component: NodeDataType, override: boolean) => "",
deleteComponent: (key: string) => {}, deleteComponent: (key: string) => {},
version: "", version: "",
nodes: [],
setNodes: () => {},
setNode: () => {},
getNode: () => undefined,
onNodesChange: () => {},
edges: [],
setEdges: () => {},
onEdgesChange: () => {},
}; };
export const FlowsContext = createContext<FlowsContextType>( export const FlowsContext = createContext<FlowsContextType>(
@ -123,107 +101,10 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const [tabId, setTabId] = useState(""); const [tabId, setTabId] = useState("");
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [flows, setFlows] = useState<Array<FlowType>>([]); const [flows, setFlows] = useState<Array<FlowType>>([]);
const [id, setId] = useState(uid()); const { setData } = useContext(typesContext);
const { reactFlowInstance, setData } = useContext(typesContext);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const [tabsState, setTabsState] = useState<FlowsState>({}); const [tabsState, setTabsState] = useState<FlowsState>({});
const [getTweak, setTweak] = useState<tweakType>([]);
const [nodes, setNodesInternal, onNodesChangeInternal] = useNodesState([]); const {nodes, edges, paste, setPending, reactFlowInstance} = useFlow();
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;
}
function refreshFlows() { function refreshFlows() {
setIsLoading(true); setIsLoading(true);
@ -231,7 +112,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
if (DbData) { if (DbData) {
try { try {
processFlows(DbData, false); processFlows(DbData, false);
updateStateWithDbData(DbData); setFlows(DbData);
setIsLoading(false); setIsLoading(false);
} catch (e) {} } 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 * Downloads the current flow as a JSON file
*/ */
@ -353,11 +209,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
link.click(); 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. * 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. * 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)); 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<NodeDataType>[] = 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 ( const addFlow = async (
newProject: Boolean, newProject: Boolean,
@ -634,26 +380,12 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
//add animation to text type edges //add animation to text type edges
updateEdges(data.edges); updateEdges(data.edges);
// updateNodes(data.nodes, 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; 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 = ( const createNewFlow = (
flowData: ReactFlowJsonObject | null, flowData: ReactFlowJsonObject | null,
flow: FlowType flow: FlowType
@ -697,7 +429,11 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
const saveTimeoutId = useRef<NodeJS.Timeout | null>(null); const saveTimeoutId = useRef<NodeJS.Timeout | null>(null);
const saveCurrentFlow = (nodes: Node[], edges: Edge[], viewport: Viewport) => { const saveCurrentFlow = (
nodes: Node[],
edges: Edge[],
viewport: Viewport
) => {
// Clear the previous timeout if it exists. // Clear the previous timeout if it exists.
if (saveTimeoutId.current) { if (saveTimeoutId.current) {
clearTimeout(saveTimeoutId.current); clearTimeout(saveTimeoutId.current);
@ -710,8 +446,7 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
saveFlow({ ...currentFlow, data: { nodes, edges, viewport } }, true); saveFlow({ ...currentFlow, data: { nodes, edges, viewport } }, true);
} }
}, 300); // Delay of 300ms. }, 300); // Delay of 300ms.
} };
async function saveFlow(flow?: FlowType, silent?: boolean) { async function saveFlow(flow?: FlowType, silent?: boolean) {
let newFlow; let newFlow;
@ -765,7 +500,6 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
} }
} }
const [isBuilt, setIsBuilt] = useState(false);
// Initialize state variable for the version // Initialize state variable for the version
const [version, setVersion] = useState(""); const [version, setVersion] = useState("");
useEffect(() => { useEffect(() => {
@ -774,63 +508,26 @@ export function FlowsProvider({ children }: { children: ReactNode }) {
}); });
}, []); }, []);
function deleteNode(idx: string | Array<string>) {
setNodes((oldNodes) =>
oldNodes.filter((node) =>
typeof idx === "string" ? node.id !== idx : !idx.includes(node.id)
)
);
}
function deleteEdge(idx: string | Array<string>) {
setEdges((oldEdges) =>
oldEdges.filter((edge) =>
typeof idx === "string" ? edge.id !== idx : !idx.includes(edge.id)
)
);
}
return ( return (
<FlowsContext.Provider <FlowsContext.Provider
value={{ value={{
version, version,
flows,
saveFlow, saveFlow,
isBuilt,
setIsBuilt,
lastCopiedSelection,
setLastCopiedSelection,
hardReset,
tabId, tabId,
setTabId, setTabId,
flows,
incrementNodeId,
removeFlow, removeFlow,
addFlow, addFlow,
downloadFlow, downloadFlow,
downloadFlows, downloadFlows,
uploadFlows, uploadFlows,
uploadFlow, uploadFlow,
getNodeId,
deleteNode,
deleteEdge,
isPending,
setPending,
tabsState, tabsState,
setTabsState, setTabsState,
paste,
getTweak,
setTweak,
isLoading, isLoading,
saveComponent, saveComponent,
deleteComponent, deleteComponent,
nodes,
setNodes,
setNode,
getNode,
onNodesChange,
edges,
setEdges,
onEdgesChange,
}} }}
> >
{children} {children}

View file

@ -16,8 +16,6 @@ import { AuthContext } from "./authContext";
//context to share types adn functions from nodes to flow //context to share types adn functions from nodes to flow
const initialValue: typesContextType = { const initialValue: typesContextType = {
reactFlowInstance: null,
setReactFlowInstance: (newState: ReactFlowInstance) => {},
types: {}, types: {},
setTypes: () => {}, setTypes: () => {},
templates: {}, templates: {},
@ -34,8 +32,6 @@ export const typesContext = createContext<typesContextType>(initialValue);
export function TypesProvider({ children }: { children: ReactNode }) { export function TypesProvider({ children }: { children: ReactNode }) {
const [types, setTypes] = useState({}); const [types, setTypes] = useState({});
const [reactFlowInstance, setReactFlowInstance] =
useState<ReactFlowInstance | null>(null);
const [templates, setTemplates] = useState({}); const [templates, setTemplates] = useState({});
const [data, setData] = useState({}); const [data, setData] = useState({});
const [fetchError, setFetchError] = useState(false); const [fetchError, setFetchError] = useState(false);
@ -95,14 +91,11 @@ export function TypesProvider({ children }: { children: ReactNode }) {
} }
} }
return ( return (
<typesContext.Provider <typesContext.Provider
value={{ value={{
types, types,
setTypes, setTypes,
reactFlowInstance,
setReactFlowInstance,
setTemplates, setTemplates,
templates, templates,
data, data,

View file

@ -6,7 +6,6 @@ import {
useEffect, useEffect,
useState, useState,
} from "react"; } from "react";
import { useReactFlow } from "reactflow";
import { import {
HistoryItem, HistoryItem,
UseUndoRedoOptions, UseUndoRedoOptions,
@ -14,6 +13,7 @@ import {
} from "../types/typesContext"; } from "../types/typesContext";
import { isWrappedWithClass } from "../utils/utils"; import { isWrappedWithClass } from "../utils/utils";
import { FlowsContext } from "./flowsContext"; import { FlowsContext } from "./flowsContext";
import useFlow from "../stores/flowManagerStore";
const initialValue = { const initialValue = {
undo: () => {}, undo: () => {},
@ -29,7 +29,10 @@ const defaultOptions: UseUndoRedoOptions = {
export const undoRedoContext = createContext<undoRedoContextType>(initialValue); export const undoRedoContext = createContext<undoRedoContextType>(initialValue);
export function UndoRedoProvider({ children }) { 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<HistoryItem[][]>(flows.map(() => [])); const [past, setPast] = useState<HistoryItem[][]>(flows.map(() => []));
const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => [])); const [future, setFuture] = useState<HistoryItem[][]>(flows.map(() => []));

View file

@ -48,7 +48,8 @@ const ApiModal = forwardRef(
const [activeTab, setActiveTab] = useState("0"); const [activeTab, setActiveTab] = useState("0");
const tweak = useRef<tweakType>([]); const tweak = useRef<tweakType>([]);
const tweaksList = useRef<string[]>([]); const tweaksList = useRef<string[]>([]);
const { setTweak, getTweak, tabsState } = useContext(FlowsContext); const { tabsState } = useContext(FlowsContext);
const [getTweak, setTweak] = useState<tweakType>([]);
const pythonApiCode = getPythonApiCode( const pythonApiCode = getPythonApiCode(
flow, flow,
autoLogin, autoLogin,

View file

@ -29,10 +29,7 @@ import {
limitScrollFieldsModal, limitScrollFieldsModal,
} from "../../constants/constants"; } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { FlowsState } from "../../types/tabs";
import { import {
convertObjToArray, convertObjToArray,
convertValuesToNumbers, convertValuesToNumbers,
@ -41,6 +38,7 @@ import {
} from "../../utils/reactflowUtils"; } from "../../utils/reactflowUtils";
import { classNames } from "../../utils/utils"; import { classNames } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
import useFlow from "../../stores/flowManagerStore";
const EditNodeModal = forwardRef( const EditNodeModal = forwardRef(
( (
@ -59,7 +57,7 @@ const EditNodeModal = forwardRef(
) => { ) => {
const [myData, setMyData] = useState(data); const [myData, setMyData] = useState(data);
const { setPending, edges, setNode } = useContext(FlowsContext); const { setPending, edges, setNode } = useFlow();
const { setModalContextOpen } = useContext(alertContext); const { setModalContextOpen } = useContext(alertContext);
function changeAdvanced(n) { function changeAdvanced(n) {

View file

@ -26,7 +26,7 @@ export default function CodeAreaModal({
readonly = false, readonly = false,
}: codeAreaModalPropsType): JSX.Element { }: codeAreaModalPropsType): JSX.Element {
const [code, setCode] = useState(value); const [code, setCode] = useState(value);
const dark = useDarkStore((state) => state.dark); const {dark} = useDarkStore();
const [height, setHeight] = useState<string | null>(null); const [height, setHeight] = useState<string | null>(null);
const { setErrorData, setSuccessData } = useContext(alertContext); const { setErrorData, setSuccessData } = useContext(alertContext);

View file

@ -6,7 +6,6 @@ import { Checkbox } from "../../components/ui/checkbox";
import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants"; import { EXPORT_DIALOG_SUBTITLE } from "../../constants/constants";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { typesContext } from "../../contexts/typesContext";
import { removeApiKeys } from "../../utils/reactflowUtils"; import { removeApiKeys } from "../../utils/reactflowUtils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";

View file

@ -1,6 +1,5 @@
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { alertContext } from "../../contexts/alertContext"; import { alertContext } from "../../contexts/alertContext";
import { typesContext } from "../../contexts/typesContext";
import { sendAllProps } from "../../types/api"; import { sendAllProps } from "../../types/api";
import { ChatMessageType } from "../../types/chat"; import { ChatMessageType } from "../../types/chat";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
@ -26,6 +25,7 @@ import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import { FlowsContext } from "../../contexts/flowsContext"; import { FlowsContext } from "../../contexts/flowsContext";
import { getBuildStatus } from "../../controllers/API"; import { getBuildStatus } from "../../controllers/API";
import useFlow from "../../stores/flowManagerStore";
import { FlowsState } from "../../types/tabs"; import { FlowsState } from "../../types/tabs";
import { validateNodes } from "../../utils/reactflowUtils"; import { validateNodes } from "../../utils/reactflowUtils";
@ -38,7 +38,8 @@ export default function FormModal({
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
flow: FlowType; flow: FlowType;
}): JSX.Element { }): JSX.Element {
const { tabsState, setTabsState, nodes, edges } = useContext(FlowsContext); const { tabsState, setTabsState } = useContext(FlowsContext);
const { nodes, edges } = useFlow();
const [chatValue, setChatValue] = useState(() => { const [chatValue, setChatValue] = useState(() => {
try { try {
const { formKeysData } = tabsState[flow.id]; const { formKeysData } = tabsState[flow.id];
@ -383,10 +384,7 @@ export default function FormModal({
}, [open]); }, [open]);
function sendMessage(): void { function sendMessage(): void {
let nodeValidationErrors = validateNodes( let nodeValidationErrors = validateNodes(nodes, edges);
nodes,
edges
);
if (nodeValidationErrors.length === 0) { if (nodeValidationErrors.length === 0) {
setLockChat(true); setLockChat(true);
let inputs = tabsState[id.current].formKeysData.input_keys; let inputs = tabsState[id.current].formKeysData.input_keys;

View file

@ -17,7 +17,6 @@ import ReactFlow, {
SelectionDragHandler, SelectionDragHandler,
addEdge, addEdge,
updateEdge, updateEdge,
useReactFlow,
} from "reactflow"; } from "reactflow";
import GenericNode from "../../../../CustomNodes/GenericNode"; import GenericNode from "../../../../CustomNodes/GenericNode";
import Chat from "../../../../components/chatComponent"; import Chat from "../../../../components/chatComponent";
@ -27,14 +26,14 @@ import { FlowsContext } from "../../../../contexts/flowsContext";
import { locationContext } from "../../../../contexts/locationContext"; import { locationContext } from "../../../../contexts/locationContext";
import { typesContext } from "../../../../contexts/typesContext"; import { typesContext } from "../../../../contexts/typesContext";
import { undoRedoContext } from "../../../../contexts/undoRedoContext"; import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import useFlow from "../../../../stores/flowManagerStore";
import { APIClassType } from "../../../../types/api"; import { APIClassType } from "../../../../types/api";
import { FlowType, NodeType, targetHandleType } from "../../../../types/flow"; import { FlowType, NodeType } from "../../../../types/flow";
import { FlowsState } from "../../../../types/tabs";
import { import {
generateFlow, generateFlow,
generateNodeFromFlow, generateNodeFromFlow,
getNodeId,
isValidConnection, isValidConnection,
scapeJSONParse,
validateSelection, validateSelection,
} from "../../../../utils/reactflowUtils"; } from "../../../../utils/reactflowUtils";
import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils"; import { cn, getRandomName, isWrappedWithClass } from "../../../../utils/utils";
@ -53,26 +52,33 @@ export default function Page({
flow: FlowType; flow: FlowType;
view?: boolean; view?: boolean;
}): JSX.Element { }): JSX.Element {
let { let { uploadFlow, saveFlow } = useContext(FlowsContext);
uploadFlow, const { types, templates, setFilterEdge } = useContext(typesContext);
getNodeId,
paste,
lastCopiedSelection,
setLastCopiedSelection,
deleteNode,
deleteEdge,
} = useContext(FlowsContext);
const {
types,
reactFlowInstance,
setReactFlowInstance,
templates,
setFilterEdge,
} = useContext(typesContext);
const reactFlowWrapper = useRef<HTMLDivElement>(null); const reactFlowWrapper = useRef<HTMLDivElement>(null);
const [lastCopiedSelection, setLastCopiedSelection] = useState<{
nodes: any;
edges: any;
} | null>(null);
const { takeSnapshot } = useContext(undoRedoContext); 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 position = useRef({ x: 0, y: 0 });
const [lastSelection, setLastSelection] = const [lastSelection, setLastSelection] =
@ -136,11 +142,7 @@ export default function Page({
document.removeEventListener("keydown", onKeyDown); document.removeEventListener("keydown", onKeyDown);
document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mousemove", handleMouseMove);
}; };
}, [ }, [lastCopiedSelection, lastSelection, takeSnapshot]);
lastCopiedSelection,
lastSelection,
takeSnapshot,
]);
const [selectionMenuVisible, setSelectionMenuVisible] = useState(false); const [selectionMenuVisible, setSelectionMenuVisible] = useState(false);
@ -155,10 +157,12 @@ export default function Page({
useEffect(() => { useEffect(() => {
setLoading(true); setLoading(true);
if(reactFlowInstance){ if (reactFlowInstance) {
reactFlowInstance.setNodes(flow?.data?.nodes ?? []); reactFlowInstance.setNodes(flow?.data?.nodes ?? []);
reactFlowInstance.setEdges(flow?.data?.edges ?? []); 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 // Clear the previous timeout
@ -177,30 +181,10 @@ export default function Page({
}; };
}, [flow, reactFlowInstance]); }, [flow, reactFlowInstance]);
const onConnect = useCallback( const onConnectMod = useCallback(
(params: Connection) => { (params: Connection) => {
takeSnapshot(); takeSnapshot();
setEdges((eds) => onConnect(params);
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
)
);
}, },
[setEdges, takeSnapshot, addEdge] [setEdges, takeSnapshot, addEdge]
); );
@ -237,10 +221,6 @@ export default function Page({
if (event.dataTransfer.types.some((types) => types === "nodedata")) { if (event.dataTransfer.types.some((types) => types === "nodedata")) {
takeSnapshot(); 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 // Extract the data from the drag event and parse it as a JSON object
let data: { type: string; node?: APIClassType } = JSON.parse( let data: { type: string; node?: APIClassType } = JSON.parse(
event.dataTransfer.getData("nodedata") event.dataTransfer.getData("nodedata")
@ -370,10 +350,9 @@ export default function Page({
}, []); }, []);
const onMove = useCallback(() => { const onMove = useCallback(() => {
if(!isPending) if (!isPending) setPending(true);
setPending(true);
}, [setPending]); }, [setPending]);
return ( return (
<div className="flex h-full overflow-hidden"> <div className="flex h-full overflow-hidden">
{!view && <ExtraSidebar />} {!view && <ExtraSidebar />}
@ -399,7 +378,7 @@ export default function Page({
edges={edges} edges={edges}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onConnect={onConnect} onConnect={onConnectMod}
disableKeyboardA11y={true} disableKeyboardA11y={true}
onInit={setReactFlowInstance} onInit={setReactFlowInstance}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
@ -479,9 +458,7 @@ export default function Page({
}} }}
/> />
</ReactFlow> </ReactFlow>
{!view && ( {!view && <Chat flow={flow} />}
<Chat flow={flow} />
)}
</div> </div>
) : ( ) : (
<></> <></>

View file

@ -10,6 +10,7 @@ import { typesContext } from "../../../../contexts/typesContext";
import ApiModal from "../../../../modals/ApiModal"; import ApiModal from "../../../../modals/ApiModal";
import ExportModal from "../../../../modals/exportModal"; import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal"; import ShareModal from "../../../../modals/shareModal";
import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore"; import { useStoreStore } from "../../../../stores/storeStore";
import { APIClassType, APIObjectType } from "../../../../types/api"; import { APIClassType, APIObjectType } from "../../../../types/api";
import { import {
@ -28,13 +29,11 @@ import SidebarDraggableComponent from "./sideBarDraggableComponent";
export default function ExtraSidebar(): JSX.Element { export default function ExtraSidebar(): JSX.Element {
const { data, templates, getFilterEdge, setFilterEdge } = const { data, templates, getFilterEdge, setFilterEdge } =
useContext(typesContext); useContext(typesContext);
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt, isPending } = const { flows, tabId, uploadFlow, saveFlow } = useContext(FlowsContext);
useContext(FlowsContext);
const hasStore = useStoreStore((state) => state.hasStore); const { hasStore, hasApiKey, validApiKey } = useStoreStore();
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
const { isBuilt, isPending } = useFlow();
const { setErrorData } = useContext(alertContext); const { setErrorData } = useContext(alertContext);
const [dataFilter, setFilterData] = useState(data); const [dataFilter, setFilterData] = useState(data);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
@ -292,12 +291,9 @@ export default function ExtraSidebar(): JSX.Element {
{flow && flow.data && ( {flow && flow.data && (
<ShadTooltip content="Save" side="top"> <ShadTooltip content="Save" side="top">
<button <button
disabled={flow?.data?.nodes.length === 0}
className={ className={
"extra-side-bar-buttons " + "extra-side-bar-buttons " +
(isPending && flow!.data!.nodes?.length > 0 (isPending ? "" : "button-disable")
? ""
: "button-disable")
} }
onClick={(event) => { onClick={(event) => {
saveFlow(); saveFlow();
@ -307,9 +303,7 @@ export default function ExtraSidebar(): JSX.Element {
name="Save" name="Save"
className={ className={
"side-bar-button-size" + "side-bar-button-size" +
(isPending && flow!.data!.nodes?.length > 0 (isPending ? " " : " extra-side-bar-save-disable")
? " "
: " extra-side-bar-save-disable")
} }
/> />
</button> </button>

View file

@ -12,6 +12,7 @@ import { APIClassType } from "../../../../../types/api";
import { import {
createFlowComponent, createFlowComponent,
downloadNode, downloadNode,
getNodeId,
} from "../../../../../utils/reactflowUtils"; } from "../../../../../utils/reactflowUtils";
import { removeCountFromString } from "../../../../../utils/utils"; import { removeCountFromString } from "../../../../../utils/utils";
@ -35,7 +36,7 @@ export default function SidebarDraggableComponent({
official: boolean; official: boolean;
}) { }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const { getNodeId, deleteComponent, version } = useContext(FlowsContext); const { deleteComponent, version } = useContext(FlowsContext);
const { autoLogin, userData } = useContext(AuthContext); const { autoLogin, userData } = useContext(AuthContext);
const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 }); const [cursorPos, setCursorPos] = useState({ x: 0, y: 0 });
const popoverRef = useRef<HTMLDivElement>(null); const popoverRef = useRef<HTMLDivElement>(null);

View file

@ -13,6 +13,7 @@ import { undoRedoContext } from "../../../../contexts/undoRedoContext";
import ConfirmationModal from "../../../../modals/ConfirmationModal"; import ConfirmationModal from "../../../../modals/ConfirmationModal";
import EditNodeModal from "../../../../modals/EditNodeModal"; import EditNodeModal from "../../../../modals/EditNodeModal";
import ShareModal from "../../../../modals/shareModal"; import ShareModal from "../../../../modals/shareModal";
import useFlow from "../../../../stores/flowManagerStore";
import { useStoreStore } from "../../../../stores/storeStore"; import { useStoreStore } from "../../../../stores/storeStore";
import { nodeToolbarPropsType } from "../../../../types/components"; import { nodeToolbarPropsType } from "../../../../types/components";
import { FlowType } from "../../../../types/flow"; import { FlowType } from "../../../../types/flow";
@ -49,11 +50,8 @@ export default function NodeToolbarComponent({
data.node.template[templateField].type === "NestedDict") data.node.template[templateField].type === "NestedDict")
).length ).length
); );
const { getNodeId } = useContext(FlowsContext);
const hasStore = useStoreStore((state) => state.hasStore); const { hasStore, hasApiKey, validApiKey } = useStoreStore();
const hasApiKey = useStoreStore((state) => state.hasApiKey);
const validApiKey = useStoreStore((state) => state.validApiKey);
function canMinimize() { function canMinimize() {
let countHandles: number = 0; let countHandles: number = 0;
@ -66,16 +64,9 @@ export default function NodeToolbarComponent({
const isMinimal = canMinimize(); const isMinimal = canMinimize();
const isGroup = data.node?.flow ? true : false; const isGroup = data.node?.flow ? true : false;
const { const { paste, nodes, edges, setNodes, setEdges } = useFlow();
paste,
saveComponent, const { saveComponent, flows, version } = useContext(FlowsContext);
version,
flows,
nodes,
edges,
setNodes,
setEdges,
} = useContext(FlowsContext);
const { takeSnapshot } = useContext(undoRedoContext); const { takeSnapshot } = useContext(undoRedoContext);
const [showModalAdvanced, setShowModalAdvanced] = useState(false); const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false); const [showconfirmShare, setShowconfirmShare] = useState(false);
@ -123,7 +114,7 @@ export default function NodeToolbarComponent({
case "ungroup": case "ungroup":
takeSnapshot(); takeSnapshot();
updateFlowPosition(position, data.node?.flow!); updateFlowPosition(position, data.node?.flow!);
expandGroupNode(data, getNodeId, nodes, edges, setNodes, setEdges); expandGroupNode(data, nodes, edges, setNodes, setEdges);
break; break;
case "override": case "override":
setShowOverrideModal(true); setShowOverrideModal(true);

View file

@ -8,9 +8,9 @@ type State = {
}; };
type Action = { type Action = {
updateDark: (dark: State["dark"]) => void; setDark: (dark: State["dark"]) => void;
updateStars: (starts: State["stars"]) => void; setStars: (starts: State["stars"]) => void;
updateGradientIndex: (gradientIndex: State["gradientIndex"]) => void; setGradientIndex: (gradientIndex: State["gradientIndex"]) => void;
}; };
function gradientIndexInitialState() { function gradientIndexInitialState() {
@ -23,9 +23,9 @@ export const useDarkStore = create<State & Action>((set) => ({
dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false, dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false,
stars: 0, stars: 0,
gradientIndex: gradientIndexInitialState(), gradientIndex: gradientIndexInitialState(),
updateDark: (dark) => set(() => ({ dark: dark })), setDark: (dark) => set(() => ({ dark: dark })),
updateStars: (starts) => set(() => ({ stars: starts })), setStars: (starts) => set(() => ({ stars: starts })),
updateGradientIndex: (gradientIndex) => setGradientIndex: (gradientIndex) =>
set(() => ({ gradientIndex: gradientIndex })), set(() => ({ gradientIndex: gradientIndex })),
})); }));

View file

@ -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<string>) => void;
deleteEdge: (edgeId: string | Array<string>) => 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<RFState>((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<NodeDataType>[] = 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;

View file

@ -1,6 +1,6 @@
import { FlowType } from "../flow"; import { FlowType } from "../flow";
export type ChatType = { flow: FlowType; }; export type ChatType = { flow: FlowType };
export type ChatMessageType = { export type ChatMessageType = {
message: string | Object; message: string | Object;
template?: string; template?: string;

View file

@ -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 { tweakType } from "../components";
import { FlowType, NodeDataType } from "../flow"; import { FlowType, NodeDataType } from "../flow";
import { Dispatch, SetStateAction } from "react";
type OnChange<ChangesType> = (changes: ChangesType[]) => void; type OnChange<ChangesType> = (changes: ChangesType[]) => void;
export type FlowsContextType = { export type FlowsContextType = {
//keep
saveFlow: (flow?: FlowType, silent?: boolean) => Promise<void>; saveFlow: (flow?: FlowType, silent?: boolean) => Promise<void>;
tabId: string; tabId: string;
//keep
isLoading: boolean; isLoading: boolean;
setTabId: (index: string) => void; setTabId: (index: string) => void;
flows: Array<FlowType>; //keep
deleteNode: (idx: string | Array<string>) => void;
deleteEdge: (idx: string | Array<string>) => void;
removeFlow: (id: string) => void; removeFlow: (id: string) => void;
//keep
addFlow: ( addFlow: (
newProject: boolean, newProject: boolean,
flow?: FlowType, flow?: FlowType,
override?: boolean, override?: boolean,
position?: XYPosition position?: XYPosition
) => Promise<String | undefined>; ) => Promise<String | undefined>;
incrementNodeId: () => string;
downloadFlow: ( downloadFlow: (
flow: FlowType, flow: FlowType,
flowName: string, flowName: string,
flowDescription?: string flowDescription?: string
) => void; ) => void;
//keep
downloadFlows: () => void; downloadFlows: () => void;
//keep
uploadFlows: () => void; uploadFlows: () => void;
isBuilt: boolean;
setIsBuilt: (state: boolean) => void;
uploadFlow: ({ uploadFlow: ({
newProject, newProject,
file, file,
@ -41,34 +40,17 @@ export type FlowsContextType = {
isComponent?: boolean; isComponent?: boolean;
position?: XYPosition; position?: XYPosition;
}) => Promise<String | never>; }) => Promise<String | never>;
hardReset: () => void;
getNodeId: (nodeType: string) => string;
isPending: boolean;
setPending: (pending: boolean) => void;
tabsState: FlowsState; tabsState: FlowsState;
setTabsState: (update: FlowsState | ((oldState: FlowsState) => FlowsState)) => void; setTabsState: (
paste: ( update: FlowsState | ((oldState: FlowsState) => FlowsState)
selection: { nodes: any; edges: any },
position: { x: number; y: number; paneX?: number; paneY?: number }
) => void; ) => void;
lastCopiedSelection: { nodes: any; edges: any } | null;
setLastCopiedSelection: (selection: { nodes: any; edges: any }) => void;
setTweak: (tweak: tweakType) => tweakType | void;
getTweak: tweakType;
saveComponent: ( saveComponent: (
component: NodeDataType, component: NodeDataType,
override: boolean override: boolean
) => Promise<String | undefined>; ) => Promise<String | undefined>;
deleteComponent: (key: string) => void; deleteComponent: (key: string) => void;
version: string; version: string;
nodes: Array<Node>; flows: Array<FlowType>;
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void;
setNode: (id: string, update: Node | ((oldState: Node) => Node)) => void;
getNode: (id: string) => Node | undefined;
onNodesChange: OnChange<NodeChange>;
edges: Array<Edge>;
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void;
onEdgesChange: OnChange<EdgeChange>;
}; };
export type FlowsState = { export type FlowsState = {

View file

@ -7,8 +7,6 @@ const template: { [char: string]: APIClassType } = {};
const data: { [char: string]: string } = {}; const data: { [char: string]: string } = {};
export type typesContextType = { export type typesContextType = {
reactFlowInstance: ReactFlowInstance | null;
setReactFlowInstance: (newState: ReactFlowInstance) => void;
types: typeof types; types: typeof types;
setTypes: (newState: {}) => void; setTypes: (newState: {}) => void;
templates: typeof template; templates: typeof template;

View file

@ -27,6 +27,7 @@ import {
updateEdgesHandleIdsType, updateEdgesHandleIdsType,
} from "../types/utils/reactflowUtils"; } from "../types/utils/reactflowUtils";
import { getFieldTitle, toTitleCase } from "./utils"; import { getFieldTitle, toTitleCase } from "./utils";
const uid = new ShortUniqueId({ length: 5 });
export function cleanEdges(nodes: Node[], edges: Edge[]) { export function cleanEdges(nodes: Node[], edges: Edge[]) {
let newEdges = _.cloneDeep(edges); let newEdges = _.cloneDeep(edges);
@ -82,7 +83,7 @@ export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) {
export function isValidConnection( export function isValidConnection(
{ source, target, sourceHandle, targetHandle }: Connection, { source, target, sourceHandle, targetHandle }: Connection,
nodes: Node[], nodes: Node[],
edges: Edge[], edges: Edge[]
) { ) {
const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!); const targetHandleObject: targetHandleType = scapeJSONParse(targetHandle!);
const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!); const sourceHandleObject: sourceHandleType = scapeJSONParse(sourceHandle!);
@ -98,16 +99,12 @@ export function isValidConnection(
) { ) {
let targetNode = nodes.find((node) => node.id === target!)?.data?.node; let targetNode = nodes.find((node) => node.id === target!)?.data?.node;
if (!targetNode) { if (!targetNode) {
if ( if (!edges.find((e) => e.targetHandle === targetHandle)) {
!edges
.find((e) => e.targetHandle === targetHandle)
) {
return true; return true;
} }
} else if ( } else if (
(!targetNode.template[targetHandleObject.fieldName].list && (!targetNode.template[targetHandleObject.fieldName].list &&
!edges !edges.find((e) => e.targetHandle === targetHandle)) ||
.find((e) => e.targetHandle === targetHandle)) ||
targetNode.template[targetHandleObject.fieldName].list targetNode.template[targetHandleObject.fieldName].list
) { ) {
return true; return true;
@ -153,7 +150,6 @@ export function updateTemplate(
export function updateIds( export function updateIds(
newFlow: ReactFlowJsonObject, newFlow: ReactFlowJsonObject,
getNodeId: (type: string) => string
) { ) {
let idsMap = {}; let idsMap = {};
@ -272,6 +268,20 @@ export function validateNodes(nodes: Node[], edges: Edge[]) {
return nodes.flatMap((n: NodeType) => validateNode(n, edges)); 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[]) { export function addVersionToDuplicates(flow: FlowType, flows: FlowType[]) {
const existingNames = flows.map((item) => item.name); const existingNames = flows.map((item) => item.name);
let newName = flow.name; let newName = flow.name;
@ -499,13 +509,26 @@ export function getMiddlePoint(nodes: Node[]) {
return { x: averageX, y: averageY }; 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( export function generateFlow(
selection: OnSelectionChangeParams, selection: OnSelectionChangeParams,
nodes: Node[], nodes: Node[],
edges: Edge[], edges: Edge[],
name: string name: string
): generateFlowType { ): 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 }); const uid = new ShortUniqueId({ length: 5 });
/* remove edges that are not connected to selected nodes on both ends /* 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 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( export function filterFlow(
selection: OnSelectionChangeParams, selection: OnSelectionChangeParams,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void, setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) { ) {
setNodes((nodes) => setNodes((nodes) => nodes.filter((node) => !selection.nodes.includes(node)));
nodes.filter((node) => !selection.nodes.includes(node)) setEdges((edges) => edges.filter((edge) => !selection.edges.includes(edge)));
);
setEdges((edges) =>
edges.filter((edge) => !selection.edges.includes(edge))
);
} }
export function findLastNode({ nodes, edges }: findLastNodeType) { export function findLastNode({ nodes, edges }: findLastNodeType) {
@ -572,7 +591,7 @@ export function updateFlowPosition(NewPosition: XYPosition, flow: FlowType) {
export function concatFlows( export function concatFlows(
flow: FlowType, flow: FlowType,
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, 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!; const { nodes, edges } = flow.data!;
setNodes((old) => [...old, ...nodes]); 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( export function expandGroupNode(
groupNode: NodeDataType, groupNode: NodeDataType,
getNodeId: (type: string) => string,
nodes: Node[], nodes: Node[],
edges: Edge[], edges: Edge[],
setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void, setNodes: (update: Node[] | ((oldState: Node[]) => Node[])) => void,
setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void setEdges: (update: Edge[] | ((oldState: Edge[]) => Edge[])) => void
) { ) {
const { template, flow } = _.cloneDeep(groupNode.node!); const { template, flow } = _.cloneDeep(groupNode.node!);
const idsMap = updateIds(flow!.data!, getNodeId); const idsMap = updateIds(flow!.data!);
updateProxyIdsOnTemplate(template, idsMap); updateProxyIdsOnTemplate(template, idsMap);
let flowEdges = edges; let flowEdges = edges;
updateEdgesIds(flowEdges, idsMap); updateEdgesIds(flowEdges, idsMap);

View file

@ -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 # Validate that the task completed successfully and the result is as expected
assert "result" in task_status_json, task_status_json assert "result" in task_status_json, task_status_json
assert "output" in task_status_json["result"], task_status_json["result"] assert "output" in task_status_json["result"], task_status_json["result"]
assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"] assert "Langflow" in task_status_json["result"]["output"], task_status_json["result"]