From 23874b81ed2d24765daf5e7994ffcb0865e7bb92 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 16 Jul 2024 20:48:20 -0300 Subject: [PATCH] feat: add new freeze function to component (#2624) * feat: Add new API endpoint for retrieving vertex order This commit adds a new API endpoint, , which allows for retrieving the order of vertices in a graph. It includes the necessary functions and types to make the API request and process the response. This feature enhances the functionality of the application by providing the ability to retrieve and work with vertex order data. * feat: Add missing semicolon in constants.ts The commit adds a missing semicolon in the file in the directory. This fix ensures that the code is syntactically correct and prevents any potential issues that may arise from the missing semicolon. * feat: Refactor usePostRetrieveVertexOrder to improve code readability and maintainability * feat: Add FreezeAllSvg component for displaying a freeze all icon This commit adds a new component called FreezeAllSvg, which is responsible for rendering an SVG icon representing the freeze all feature. The component accepts props for customizing the size and color of the icon. This addition enhances the visual representation of the freeze all functionality in the application. * feat: Add freezeMultipleNodes method to FlowStoreType This commit adds the `freezeMultipleNodes` method to the `FlowStoreType` interface in the `index.ts` file. The method takes an array of node IDs as input and freezes the corresponding nodes in the flow. This addition enhances the functionality of the flow store by providing the ability to freeze multiple nodes at once. * feat: Update freezeMultipleNodes to use updateFreezeStatus and updateNodeInternals Refactor the NodeToolbarComponent to use the updated freezeMultipleNodes method in the FlowStore. Instead of directly freezing the nodes, the method now calls updateFreezeStatus to update the freeze status of the nodes and updateNodeInternals to update the node internals. This change improves the functionality and maintainability of the code. * feat: Update FreezeAllSvg component to use currentColor for fill The commit updates the FreezeAllSvg component to use the CSS `currentColor` value for the fill property. This change ensures that the fill color of the SVG icon will match the current text color, providing a consistent and customizable visual representation of the freeze all feature. * feat: Add freezeAll shortcut and component This commit adds the `freezeAll` shortcut and component to the application. The `freezeAll` shortcut allows users to freeze all nodes in the flow at once, while the `FreezeAllSvg` component is responsible for rendering an SVG icon representing the freeze all feature. These additions enhance the functionality and user experience of the application by providing a convenient way to freeze multiple nodes and a visual representation of the freeze all feature. * [autofix.ci] apply automated fixes * feat: Refactor get_vertex method to include silent parameter This commit refactors the `get_vertex` method in the `Graph` class to include a `silent` parameter. The `silent` parameter allows the method to be called without raising an exception if the vertex is not found. This change improves the flexibility and usability of the method, as it can now be used in scenarios where the absence of a vertex is expected. * feat: check if parent vertex is frozen and cache children * feat: change ungroup_node function to add frozen status to nodes This commit refactors the `ungroup_node` function in the `utils.py` file to add the `frozen` status to each node in the list of nodes. The `frozen` status is obtained from the `group_node_data` parameter and is assigned to each node in the loop. This change improves the functionality and maintainability of the code by ensuring that the frozen status is correctly applied to the nodes during the ungrouping process. * Refactor code to check if parent vertex is frozen before caching children * feat: Rename "Freeze All" to "Freeze Path" in shortcuts and components This commit updates the codebase to rename the "Freeze All" feature to "Freeze Path" in the shortcuts and components. The changes include modifying the constant name, updating the shortcut key, and adjusting the component names and labels. This renaming improves the clarity and accuracy of the feature, aligning it with its intended functionality of freezing a specific path in the flow. * [autofix.ci] apply automated fixes * fix: refactor get_vertex method to handle silent parameter * fix(base.py): remove unnecessary silent parameter in get_vertex method and remove conditional check for silent parameter in get_vertex method to simplify code and improve readability * Added new freezeAll icon --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: Lucas Oliveira --- src/backend/base/langflow/graph/graph/base.py | 5 +- .../base/langflow/graph/graph/utils.py | 16 ++- src/frontend/src/constants/constants.ts | 4 + .../src/controllers/API/helpers/constants.ts | 1 + .../controllers/API/queries/vertex/index.tsx | 1 + .../vertex/use-post-retrieve-vertex-order.tsx | 68 ++++++++++ .../src/icons/freezeAll/freezeAll.jsx | 46 +++++++ src/frontend/src/icons/freezeAll/index.tsx | 10 ++ .../components/nodeToolbarComponent/index.tsx | 123 +++++++++++------- src/frontend/src/stores/flowStore.ts | 11 ++ src/frontend/src/stores/shortcuts.ts | 1 + src/frontend/src/types/store/index.ts | 1 + src/frontend/src/types/zustand/flow/index.ts | 1 + src/frontend/src/utils/styleUtils.ts | 2 + 14 files changed, 235 insertions(+), 55 deletions(-) create mode 100644 src/frontend/src/controllers/API/queries/vertex/index.tsx create mode 100644 src/frontend/src/controllers/API/queries/vertex/use-post-retrieve-vertex-order.tsx create mode 100644 src/frontend/src/icons/freezeAll/freezeAll.jsx create mode 100644 src/frontend/src/icons/freezeAll/index.tsx diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 9d46908cf..348559c0f 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -818,7 +818,7 @@ class Graph: # All vertices that do not have edges are invalid return len(self.get_vertex_edges(vertex.id)) > 0 - def get_vertex(self, vertex_id: str) -> Vertex: + def get_vertex(self, vertex_id: str, silent: bool = False) -> Vertex: """Returns a vertex by id.""" try: return self.vertex_map[vertex_id] @@ -869,7 +869,8 @@ class Graph: self.run_manager.add_to_vertices_being_run(vertex_id) try: params = "" - if vertex.frozen: + parent_vertex = self.get_vertex(vertex.parent_node_id) if vertex.parent_node_id else None + if vertex.frozen or (parent_vertex and parent_vertex.frozen): # Check the cache for the vertex cached_result = await chat_service.get_cache(key=vertex.id) if isinstance(cached_result, CacheMiss): diff --git a/src/backend/base/langflow/graph/graph/utils.py b/src/backend/base/langflow/graph/graph/utils.py index 9a46f828f..0addab0b6 100644 --- a/src/backend/base/langflow/graph/graph/utils.py +++ b/src/backend/base/langflow/graph/graph/utils.py @@ -1,7 +1,6 @@ -from typing import List, Dict import copy from collections import deque - +from typing import Dict, List PRIORITY_LIST_OF_INPUTS = ["webhook", "chat"] @@ -38,14 +37,25 @@ def add_parent_node_id(nodes, parent_node_id): node["parent_node_id"] = parent_node_id +def add_frozen(nodes, frozen): + """ + This function receives a list of nodes and adds a frozen to each node. + """ + for node in nodes: + node["frozen"] = frozen + + def ungroup_node(group_node_data, base_flow): - template, flow = ( + template, flow, frozen = ( group_node_data["node"]["template"], group_node_data["node"]["flow"], + group_node_data["node"].get("frozen", False), ) parent_node_id = group_node_data["id"] + g_nodes = flow["data"]["nodes"] add_parent_node_id(g_nodes, parent_node_id) + add_frozen(g_nodes, frozen) g_edges = flow["data"]["edges"] # Redirect edges to the correct proxy node diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 9642859f0..9ca0122f4 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -815,6 +815,10 @@ export const defaultShortcuts = [ name: "Freeze", shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + F`, }, + { + name: "Freeze Path", + shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + Shift + F`, + }, { name: "Flow Share", shortcut: `${IS_MAC ? "Cmd" : "Ctrl"} + B`, diff --git a/src/frontend/src/controllers/API/helpers/constants.ts b/src/frontend/src/controllers/API/helpers/constants.ts index 02e5a67ff..0276e73c6 100644 --- a/src/frontend/src/controllers/API/helpers/constants.ts +++ b/src/frontend/src/controllers/API/helpers/constants.ts @@ -7,6 +7,7 @@ export const URLs = { VERSION: `version`, MESSAGES: `monitor/messages`, STORE: `store`, + BUILD: `build`, } as const; export function getURL(key: keyof typeof URLs, params: any = {}) { diff --git a/src/frontend/src/controllers/API/queries/vertex/index.tsx b/src/frontend/src/controllers/API/queries/vertex/index.tsx new file mode 100644 index 000000000..e8b098070 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/vertex/index.tsx @@ -0,0 +1 @@ +export * from "./use-post-retrieve-vertex-order"; diff --git a/src/frontend/src/controllers/API/queries/vertex/use-post-retrieve-vertex-order.tsx b/src/frontend/src/controllers/API/queries/vertex/use-post-retrieve-vertex-order.tsx new file mode 100644 index 000000000..2b90daea2 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/vertex/use-post-retrieve-vertex-order.tsx @@ -0,0 +1,68 @@ +import { useMutationFunctionType } from "@/types/api"; +import { AxiosRequestConfig } from "axios"; +import { ReactFlowJsonObject } from "reactflow"; +import { api } from "../../api"; +import { getURL } from "../../helpers/constants"; +import { UseRequestProcessor } from "../../services/request-processor"; + +interface retrieveGetVerticesOrder { + flowId: string; + data?: ReactFlowJsonObject; + stopNodeId?: string; + startNodeId?: string; +} + +interface retrieveGetVerticesOrderResponse { + ids: string[]; + rund_id: string; + vertices_to_run: string[]; +} + +// add types for error handling and success +export const usePostRetrieveVertexOrder: useMutationFunctionType< + retrieveGetVerticesOrder, + retrieveGetVerticesOrderResponse +> = (options) => { + const { mutate } = UseRequestProcessor(); + + const postRetrieveVertexOrder = async ({ + flowId, + data: flow, + startNodeId, + stopNodeId, + }: retrieveGetVerticesOrder): Promise => { + // nodeId is optional and is a query parameter + // if nodeId is not provided, the API will return all vertices + const config: AxiosRequestConfig = {}; + if (stopNodeId) { + config["params"] = { stop_component_id: decodeURIComponent(stopNodeId) }; + } else if (startNodeId) { + config["params"] = { + start_component_id: decodeURIComponent(startNodeId), + }; + } + const data = { + data: {}, + }; + if (flow && flow.nodes && flow.edges) { + const { nodes, edges } = flow; + data["data"]["nodes"] = nodes; + data["data"]["edges"] = edges; + } + const response = await api.post( + `${getURL("BUILD")}/${flowId}/vertices`, + data, + config, + ); + + return response.data; + }; + + const mutation = mutate( + ["usePostRetrieveVertexOrder"], + postRetrieveVertexOrder, + options, + ); + + return mutation; +}; diff --git a/src/frontend/src/icons/freezeAll/freezeAll.jsx b/src/frontend/src/icons/freezeAll/freezeAll.jsx new file mode 100644 index 000000000..4c2b22bc3 --- /dev/null +++ b/src/frontend/src/icons/freezeAll/freezeAll.jsx @@ -0,0 +1,46 @@ +const FreezeAllSvg = (props) => { + return ( + + snowflake-svg + + + + + + + + + + ); +}; + +export default FreezeAllSvg; diff --git a/src/frontend/src/icons/freezeAll/index.tsx b/src/frontend/src/icons/freezeAll/index.tsx new file mode 100644 index 000000000..62f5b53de --- /dev/null +++ b/src/frontend/src/icons/freezeAll/index.tsx @@ -0,0 +1,10 @@ +import React, { forwardRef } from "react"; +import SvgFreezeAll from "./freezeAll"; +("./freezeAll.jsx"); + +export const freezeAllIcon = forwardRef< + SVGSVGElement, + React.PropsWithChildren<{}> +>((props, ref) => { + return ; +}); diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index d27ad4ef7..684298f77 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -1,3 +1,4 @@ +import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex"; import _, { cloneDeep } from "lodash"; import { useEffect, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; @@ -71,12 +72,14 @@ export default function NodeToolbarComponent({ data.node.template[templateField]?.type === "dict" || data.node.template[templateField]?.type === "NestedDict"), ).length; + const updateFreezeStatus = useFlowStore((state) => state.updateFreezeStatus); const hasStore = useStoreStore((state) => state.hasStore); const hasApiKey = useStoreStore((state) => state.hasApiKey); const validApiKey = useStoreStore((state) => state.validApiKey); const shortcuts = useShortcutsStore((state) => state.shortcuts); const unselectAll = useFlowStore((state) => state.unselectAll); + const currentFlow = useFlowsManagerStore((state) => state.currentFlow); function handleMinimizeWShortcut(e: KeyboardEvent) { if (isWrappedWithClass(e, "noflow")) return; e.preventDefault(); @@ -153,7 +156,6 @@ export default function NodeToolbarComponent({ function handleFreeze(e: KeyboardEvent) { if (isWrappedWithClass(e, "noflow")) return; e.preventDefault(); - if (data.node?.flow) return; setNode(data.id, (old) => ({ ...old, data: { @@ -166,6 +168,12 @@ export default function NodeToolbarComponent({ })); } + function handleFreezeAll(e: KeyboardEvent) { + if (isWrappedWithClass(e, "noflow")) return; + e.preventDefault(); + FreezeAllVertices({ flowId: currentFlow!.id, stopNodeId: data.id }); + } + const advanced = useShortcutsStore((state) => state.advanced); const minimize = useShortcutsStore((state) => state.minimize); const component = useShortcutsStore((state) => state.component); @@ -175,6 +183,7 @@ export default function NodeToolbarComponent({ const group = useShortcutsStore((state) => state.group); const download = useShortcutsStore((state) => state.download); const freeze = useShortcutsStore((state) => state.freeze); + const freezeAll = useShortcutsStore((state) => state.FreezePath); useHotkeys(minimize, handleMinimizeWShortcut, { preventDefault }); useHotkeys(group, handleGroupWShortcut, { preventDefault }); @@ -185,6 +194,7 @@ export default function NodeToolbarComponent({ useHotkeys(docs, handleDocsWShortcut, { preventDefault }); useHotkeys(download, handleDownloadWShortcut, { preventDefault }); useHotkeys(freeze, handleFreeze); + useHotkeys(freezeAll, handleFreezeAll); const isMinimal = numberOfHandles <= 1 && numberOfOutputHandles <= 1; const isGroup = data.node?.flow ? true : false; @@ -200,6 +210,14 @@ export default function NodeToolbarComponent({ const getNodePosition = useFlowStore((state) => state.getNodePosition); const flows = useFlowsManagerStore((state) => state.flows); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); + const { mutate: FreezeAllVertices } = usePostRetrieveVertexOrder({ + onSuccess: ({ vertices_to_run }) => { + updateFreezeStatus(vertices_to_run, !data.node?.frozen); + vertices_to_run.forEach((vertex) => { + updateNodeInternals(vertex); + }); + }, + }); // useEffect(() => { // if (openWDoubleClick) setShowModalAdvanced(true); @@ -244,7 +262,6 @@ export default function NodeToolbarComponent({ saveComponent(cloneDeep(data), false); break; case "freeze": - if (data.node?.flow) return; setNode(data.id, (old) => ({ ...old, data: { @@ -256,6 +273,9 @@ export default function NodeToolbarComponent({ }, })); break; + case "freezeAll": + FreezeAllVertices({ flowId: currentFlow!.id, stopNodeId: data.id }); + break; case "code": setOpenModal(!openModal); break; @@ -490,44 +510,37 @@ export default function NodeToolbarComponent({ */} - {!data.node?.flow && ( - name.split(" ")[0].toLowerCase() === "freeze", - )!, + name.toLowerCase() === "freeze path", + )!, + )} + side="top" + > + - - )} + /> + + {/*