From f1e097d052f73703675d1ecb1c92c46b7f6d7cfe Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 26 Feb 2024 16:45:32 -0300 Subject: [PATCH 01/19] checkNodes --- src/frontend/src/components/IOview/index.tsx | 11 ----------- src/frontend/src/stores/flowStore.ts | 13 +++++++++++++ src/frontend/src/utils/buildUtils.ts | 10 +++++++++- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/frontend/src/components/IOview/index.tsx b/src/frontend/src/components/IOview/index.tsx index b0eb16547..d745ca1b4 100644 --- a/src/frontend/src/components/IOview/index.tsx +++ b/src/frontend/src/components/IOview/index.tsx @@ -4,7 +4,6 @@ import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants"; import BaseModal from "../../modals/baseModal"; import useAlertStore from "../../stores/alertStore"; import useFlowStore from "../../stores/flowStore"; -import { validateNodes } from "../../utils/reactflowUtils"; import { cn } from "../../utils/utils"; import AccordionComponent from "../AccordionComponent"; import IOInputField from "../IOInputField"; @@ -51,8 +50,6 @@ export default function IOView({ children, open, setOpen }): JSX.Element { async function sendMessage(count = 1): Promise { if (isBuilding) return; const { nodes, edges } = getFlow(); - let nodeValidationErrors = validateNodes(nodes, edges); - if (nodeValidationErrors.length === 0) { setIsBuilding(true); setLockChat(true); setChatValue(""); @@ -70,14 +67,6 @@ export default function IOView({ children, open, setOpen }): JSX.Element { } setLockChat(false); - //set chat message in the flow and run build - //@ts-ignore - } else { - setErrorData({ - title: "Oops! Looks like you missed some required information:", - list: nodeValidationErrors, - }); - } } useEffect(() => { diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index f1e5c85ea..c1b2f7547 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -26,6 +26,7 @@ import { getNodeId, scapeJSONParse, scapedJSONStringfy, + validateNodes, } from "../utils/reactflowUtils"; import { getInputsAndOutputs } from "../utils/storeUtils"; import useAlertStore from "./alertStore"; @@ -377,6 +378,17 @@ const useFlowStore = create((set, get) => ({ const setSuccessData = useAlertStore.getState().setSuccessData; const setErrorData = useAlertStore.getState().setErrorData; const setNoticeData = useAlertStore.getState().setNoticeData; + function validateSubgraph(nodes:string[]){ + const errors = validateNodes(get().nodes.filter(node=>nodes.includes(node.id)), get().edges); + if (errors.length > 0) { + setErrorData({ + title: "Oops! Looks like you missed something", + list: errors, + }); + get().setIsBuilding(false); + throw new Error("Invalid nodes"); + } + } function handleBuildUpdate( vertexBuildData: VertexBuildTypeAPI, status: BuildStatus @@ -422,6 +434,7 @@ const useFlowStore = create((set, get) => ({ onBuildStart: (idList) => { useFlowStore.getState().updateBuildStatus(idList, BuildStatus.BUILDING); }, + validateNodes: validateSubgraph, }); get().revertBuiltStatusFromBuilding(); }, diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index fd87ccd4e..87d431770 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -12,6 +12,7 @@ type BuildVerticesParams = { onBuildComplete?: (allNodesValid: boolean) => void; onBuildError?: (title, list, idList: string[]) => void; onBuildStart?: (idList: string[]) => void; + validateNodes?: (nodes:string[])=>void; }; function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { @@ -40,12 +41,19 @@ export async function buildVertices({ onBuildComplete, onBuildError, onBuildStart, + validateNodes }: BuildVerticesParams) { let orderResponse = await getVerticesOrder(flowId, nodeId); let verticesOrder: Array> = orderResponse.data.ids; let vertices_layers: Array> = []; let stop = false; - + if(validateNodes){ + try{ + validateNodes(verticesOrder.flatMap(id=>id)) + } catch(e){ + return; + } + } if (nodeId) { for (let i = 0; i < verticesOrder.length; i += 1) { const innerArray = verticesOrder[i]; From 694418827dbebc52917e809103a1fdd4146f660b Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 26 Feb 2024 16:52:53 -0300 Subject: [PATCH 02/19] Implement update_edges_from_vertex --- src/backend/langflow/graph/graph/base.py | 109 ++++++++++++++--------- 1 file changed, 69 insertions(+), 40 deletions(-) diff --git a/src/backend/langflow/graph/graph/base.py b/src/backend/langflow/graph/graph/base.py index 59a8966b5..de126374d 100644 --- a/src/backend/langflow/graph/graph/base.py +++ b/src/backend/langflow/graph/graph/base.py @@ -85,7 +85,9 @@ class Graph: def build_parent_child_map(self): parent_child_map = defaultdict(list) for vertex in self.vertices: - parent_child_map[vertex.id] = [child.id for child in self.get_successors(vertex)] + parent_child_map[vertex.id] = [ + child.id for child in self.get_successors(vertex) + ] return parent_child_map def increment_run_count(self): @@ -149,6 +151,16 @@ class Graph: # both graphs have the same vertices and edges # but the data of the vertices might be different + def update_edges_from_vertex(self, vertex: Vertex, other_vertex: Vertex) -> None: + """Updates the edges of a vertex in the Graph.""" + new_edges = [] + for edge in self.edges: + if edge.source_id == other_vertex.id or edge.target_id == other_vertex.id: + continue + new_edges.append(edge) + new_edges += other_vertex.edges + self.edges = new_edges + def vertex_data_is_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool: return vertex.__repr__() == other_vertex.__repr__() @@ -173,10 +185,6 @@ class Graph: # Find vertices that are in self but not in other (removed vertices) removed_vertex_ids = existing_vertex_ids - other_vertex_ids - # Create a set for new edges - edges_to_add = set() - edges_to_remove = set() - # Update existing vertices that have changed for vertex_id in existing_vertex_ids.intersection(other_vertex_ids): self_vertex = self.get_vertex(vertex_id) @@ -184,6 +192,8 @@ class Graph: if not self.vertex_data_is_identical(self_vertex, other_vertex): self_vertex._data = other_vertex._data self_vertex._parse_data() + # Now we update the edges of the vertex + self.update_edges_from_vertex(self_vertex, other_vertex) self_vertex.params = {} self_vertex._build_params() self_vertex.graph = self @@ -195,25 +205,6 @@ class Graph: self_vertex.artifacts = None self_vertex.set_top_level(self.top_level_vertices) self.reset_all_edges_of_vertex(self_vertex) - if not self.vertex_edges_are_identical(self_vertex, other_vertex): - # New edges are the edges of the other vertex and not the self vertex - # If there are more edges in the other vertex than in the self vertex - # then we need to add the new edges to the self vertex - # if there are less edges in the other vertex than in the self vertex - # then we need to remove the edges that are not in the other vertex - - if len(self_vertex.edges) < len(other_vertex.edges): - edges_to_add.update(edge for edge in other_vertex.edges if edge not in self_vertex.edges) - elif len(self_vertex.edges) > len(other_vertex.edges): - edges_to_remove.update(edge for edge in self_vertex.edges if edge not in other_vertex.edges) - - # Add new edges - # to self.edges if they are not already in self.edges - for edge in edges_to_add: - if edge not in self.edges: - self.edges.append(edge) - for edge in edges_to_remove: - self.edges.remove(edge) # Remove vertices for vertex_id in removed_vertex_ids: @@ -290,7 +281,11 @@ class Graph: return self.vertices.remove(vertex) self.vertex_map.pop(vertex_id) - self.edges = [edge for edge in self.edges if edge.source_id != vertex_id and edge.target_id != vertex_id] + self.edges = [ + edge + for edge in self.edges + if edge.source_id != vertex_id and edge.target_id != vertex_id + ] def _build_vertex_params(self) -> None: """Identifies and handles the LLM vertex within the graph.""" @@ -311,7 +306,9 @@ class Graph: return for vertex in self.vertices: if not self._validate_vertex(vertex): - raise ValueError(f"{vertex.vertex_type} is not connected to any other components") + raise ValueError( + f"{vertex.vertex_type} is not connected to any other components" + ) def _validate_vertex(self, vertex: Vertex) -> bool: """Validates a vertex.""" @@ -327,7 +324,11 @@ class Graph: def get_vertex_edges(self, vertex_id: str) -> List[ContractEdge]: """Returns a list of edges for a given vertex.""" - return [edge for edge in self.edges if edge.source_id == vertex_id or edge.target_id == vertex_id] + return [ + edge + for edge in self.edges + if edge.source_id == vertex_id or edge.target_id == vertex_id + ] def get_vertices_with_target(self, vertex_id: str) -> List[Vertex]: """Returns the vertices connected to a vertex.""" @@ -365,7 +366,9 @@ class Graph: def dfs(vertex): if state[vertex] == 1: # We have a cycle - raise ValueError("Graph contains a cycle, cannot perform topological sort") + raise ValueError( + "Graph contains a cycle, cannot perform topological sort" + ) if state[vertex] == 0: state[vertex] = 1 for edge in vertex.edges: @@ -389,11 +392,17 @@ class Graph: def get_predecessors(self, vertex): """Returns the predecessors of a vertex.""" - return [self.get_vertex(source_id) for source_id in self.predecessor_map.get(vertex.id, [])] + return [ + self.get_vertex(source_id) + for source_id in self.predecessor_map.get(vertex.id, []) + ] def get_successors(self, vertex): """Returns the successors of a vertex.""" - return [self.get_vertex(target_id) for target_id in self.successor_map.get(vertex.id, [])] + return [ + self.get_vertex(target_id) + for target_id in self.successor_map.get(vertex.id, []) + ] def get_vertex_neighbors(self, vertex: Vertex) -> Dict[Vertex, int]: """Returns the neighbors of a vertex.""" @@ -432,7 +441,9 @@ class Graph: edges.append(ContractEdge(source, target, edge)) return edges - def _get_vertex_class(self, node_type: str, node_base_type: str, node_id: str) -> Type[Vertex]: + def _get_vertex_class( + self, node_type: str, node_base_type: str, node_id: str + ) -> Type[Vertex]: """Returns the node class based on the node type.""" # First we check for the node_base_type node_name = node_id.split("-")[0] @@ -463,14 +474,18 @@ class Graph: vertex_type: str = vertex_data["type"] # type: ignore vertex_base_type: str = vertex_data["node"]["template"]["_type"] # type: ignore - VertexClass = self._get_vertex_class(vertex_type, vertex_base_type, vertex_data["id"]) + VertexClass = self._get_vertex_class( + vertex_type, vertex_base_type, vertex_data["id"] + ) vertex_instance = VertexClass(vertex, graph=self) vertex_instance.set_top_level(self.top_level_vertices) vertices.append(vertex_instance) return vertices - def get_children_by_vertex_type(self, vertex: Vertex, vertex_type: str) -> List[Vertex]: + def get_children_by_vertex_type( + self, vertex: Vertex, vertex_type: str + ) -> List[Vertex]: """Returns the children of a vertex based on the vertex type.""" children = [] vertex_types = [vertex.data["type"]] @@ -482,7 +497,9 @@ class Graph: def __repr__(self): vertex_ids = [vertex.id for vertex in self.vertices] - edges_repr = "\n".join([f"{edge.source_id} --> {edge.target_id}" for edge in self.edges]) + edges_repr = "\n".join( + [f"{edge.source_id} --> {edge.target_id}" for edge in self.edges] + ) return f"Graph:\nNodes: {vertex_ids}\nConnections:\n{edges_repr}" def sort_up_to_vertex(self, vertex_id: str) -> "Graph": @@ -513,7 +530,9 @@ class Graph: """Performs a layered topological sort of the vertices in the graph.""" # Queue for vertices with no incoming edges - queue = deque(vertex.id for vertex in vertices if self.in_degree_map[vertex.id] == 0) + queue = deque( + vertex.id for vertex in vertices if self.in_degree_map[vertex.id] == 0 + ) layers = [] current_layer = 0 @@ -569,7 +588,9 @@ class Graph: return refined_layers - def sort_chat_inputs_first(self, vertices_layers: List[List[str]]) -> List[List[str]]: + def sort_chat_inputs_first( + self, vertices_layers: List[List[str]] + ) -> List[List[str]]: chat_inputs_first = [] for layer in vertices_layers: for vertex_id in layer: @@ -597,11 +618,15 @@ class Graph: self.increment_run_count() return vertices_layers - def sort_interface_components_first(self, vertices_layers: List[List[str]]) -> List[List[str]]: + def sort_interface_components_first( + self, vertices_layers: List[List[str]] + ) -> List[List[str]]: """Sorts the vertices in the graph so that vertices containing ChatInput or ChatOutput come first.""" def contains_interface_component(vertex): - return any(component.value in vertex for component in InterfaceComponentTypes) + return any( + component.value in vertex for component in InterfaceComponentTypes + ) # Sort each inner list so that vertices containing ChatInput or ChatOutput come first sorted_vertices = [ @@ -620,9 +645,13 @@ class Graph: """Sorts the vertices in the graph so that vertices with the lowest average build time come first.""" if len(vertices_ids) == 1: return vertices_ids - vertices_ids.sort(key=lambda vertex_id: self.get_vertex(vertex_id).avg_build_time) + vertices_ids.sort( + key=lambda vertex_id: self.get_vertex(vertex_id).avg_build_time + ) return vertices_ids - sorted_vertices = [sort_layer_by_avg_build_time(layer) for layer in vertices_layers] + sorted_vertices = [ + sort_layer_by_avg_build_time(layer) for layer in vertices_layers + ] return sorted_vertices From 2118869e1e7c287cc9d24adacba578b94bb83c4a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 26 Feb 2024 17:10:01 -0300 Subject: [PATCH 03/19] Add onGetOrderSuccess and fix error in get vertices order --- src/frontend/src/stores/flowStore.ts | 13 +++++++---- src/frontend/src/utils/buildUtils.ts | 32 ++++++++++++++++++++-------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index c1b2f7547..b8b2508ce 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -9,6 +9,7 @@ import { applyNodeChanges, } from "reactflow"; import { create } from "zustand"; +import { FLOW_BUILD_SUCCESS_ALERT } from "../alerts_constants"; import { BuildStatus } from "../constants/enums"; import { getFlowPool, updateFlowInDatabase } from "../controllers/API"; import { VertexBuildTypeAPI } from "../types/api"; @@ -32,7 +33,6 @@ import { getInputsAndOutputs } from "../utils/storeUtils"; import useAlertStore from "./alertStore"; import { useDarkStore } from "./darkStore"; import useFlowsManagerStore from "./flowsManagerStore"; -import { FLOW_BUILD_SUCCESS_ALERT } from "../alerts_constants"; // this is our useStore hook that we can use in our components to get parts of the store and call actions const useFlowStore = create((set, get) => ({ @@ -378,8 +378,11 @@ const useFlowStore = create((set, get) => ({ const setSuccessData = useAlertStore.getState().setSuccessData; const setErrorData = useAlertStore.getState().setErrorData; const setNoticeData = useAlertStore.getState().setNoticeData; - function validateSubgraph(nodes:string[]){ - const errors = validateNodes(get().nodes.filter(node=>nodes.includes(node.id)), get().edges); + function validateSubgraph(nodes: string[]) { + const errors = validateNodes( + get().nodes.filter((node) => nodes.includes(node.id)), + get().edges + ); if (errors.length > 0) { setErrorData({ title: "Oops! Looks like you missed something", @@ -409,10 +412,12 @@ const useFlowStore = create((set, get) => ({ name: currentFlow!.name, description: currentFlow!.description, }); - setNoticeData({ title: "Running components" }); await buildVertices({ flowId: currentFlow!.id, nodeId, + onGetOrderSuccess: () => { + setNoticeData({ title: "Running components" }); + }, onBuildComplete: () => { if (nodeId) { setSuccessData({ diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index 87d431770..862472ee7 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -1,18 +1,19 @@ import { AxiosError } from "axios"; import { BuildStatus } from "../constants/enums"; import { getVerticesOrder, postBuildVertex } from "../controllers/API"; +import useAlertStore from "../stores/alertStore"; import useFlowStore from "../stores/flowStore"; import { VertexBuildTypeAPI } from "../types/api"; type BuildVerticesParams = { flowId: string; // Assuming FlowType is the type for your flow nodeId?: string | null; // Assuming nodeId is of type string, and it's optional - onProgressUpdate?: (progress: number) => void; // Replace number with the actual type if it's not a number + onGetOrderSuccess?: () => void; onBuildUpdate?: (data: VertexBuildTypeAPI, status: BuildStatus) => void; // Replace any with the actual type if it's not any onBuildComplete?: (allNodesValid: boolean) => void; onBuildError?: (title, list, idList: string[]) => void; onBuildStart?: (idList: string[]) => void; - validateNodes?: (nodes:string[])=>void; + validateNodes?: (nodes: string[]) => void; }; function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { @@ -36,21 +37,34 @@ function getInactiveVertexData(vertexId: string): VertexBuildTypeAPI { export async function buildVertices({ flowId, nodeId = null, - onProgressUpdate, + onGetOrderSuccess, onBuildUpdate, onBuildComplete, onBuildError, onBuildStart, - validateNodes + validateNodes, }: BuildVerticesParams) { - let orderResponse = await getVerticesOrder(flowId, nodeId); + const setErrorData = useAlertStore.getState().setErrorData; + let orderResponse; + try { + orderResponse = await getVerticesOrder(flowId, nodeId); + } catch (error) { + console.log(error); + setErrorData({ + title: "Oops! Looks like you missed something", + list: [error.response?.data?.detail ?? "Unknown Error"], + }); + useFlowStore.getState().setIsBuilding(false); + throw new Error("Invalid nodes"); + } + if (onGetOrderSuccess) onGetOrderSuccess(); let verticesOrder: Array> = orderResponse.data.ids; let vertices_layers: Array> = []; let stop = false; - if(validateNodes){ - try{ - validateNodes(verticesOrder.flatMap(id=>id)) - } catch(e){ + if (validateNodes) { + try { + validateNodes(verticesOrder.flatMap((id) => id)); + } catch (e) { return; } } From e4d4c21f344dc52e95af612cb8ec0c3bea843160 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 26 Feb 2024 17:10:52 -0300 Subject: [PATCH 04/19] Use display_name in exception --- src/backend/langflow/graph/graph/base.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/backend/langflow/graph/graph/base.py b/src/backend/langflow/graph/graph/base.py index de126374d..1ea34fd51 100644 --- a/src/backend/langflow/graph/graph/base.py +++ b/src/backend/langflow/graph/graph/base.py @@ -9,13 +9,8 @@ from langflow.graph.graph.constants import lazy_load_vertex_dict from langflow.graph.graph.utils import process_flow from langflow.graph.schema import InterfaceComponentTypes from langflow.graph.vertex.base import Vertex -from langflow.graph.vertex.types import ( - ChatVertex, - FileToolVertex, - LLMVertex, - RoutingVertex, - ToolkitVertex, -) +from langflow.graph.vertex.types import (ChatVertex, FileToolVertex, LLMVertex, + RoutingVertex, ToolkitVertex) from langflow.interface.tools.constants import FILE_TOOLS from langflow.utils import payload @@ -307,7 +302,7 @@ class Graph: for vertex in self.vertices: if not self._validate_vertex(vertex): raise ValueError( - f"{vertex.vertex_type} is not connected to any other components" + f"{vertex.display_name} is not connected to any other components" ) def _validate_vertex(self, vertex: Vertex) -> bool: From 9864ee824d3443097a04c8f6d778dacf33a69314 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Mon, 26 Feb 2024 17:13:04 -0300 Subject: [PATCH 05/19] Use display_name in other exceptions --- src/backend/langflow/graph/vertex/base.py | 102 ++++++++++++++++----- src/backend/langflow/graph/vertex/types.py | 49 +++++++--- 2 files changed, 115 insertions(+), 36 deletions(-) diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index 25b5f1b30..d744d764e 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -72,11 +72,17 @@ class Vertex: def set_state(self, state: str): self.state = VertexStates[state] - if self.state == VertexStates.INACTIVE and self.graph.in_degree_map[self.id] < 2: + if ( + self.state == VertexStates.INACTIVE + and self.graph.in_degree_map[self.id] < 2 + ): # If the vertex is inactive and has only one in degree # it means that it is not a merge point in the graph self.graph.inactive_vertices.add(self.id) - elif self.state == VertexStates.ACTIVE and self.id in self.graph.inactive_vertices: + elif ( + self.state == VertexStates.ACTIVE + and self.id in self.graph.inactive_vertices + ): self.graph.inactive_vertices.remove(self.id) @property @@ -104,7 +110,9 @@ class Vertex: ): if edge.target_id not in edge_results: edge_results[edge.target_id] = {} - edge_results[edge.target_id][edge.target_param] = await edge.get_result(source=self, target=target) + edge_results[edge.target_id][edge.target_param] = await edge.get_result( + source=self, target=target + ) return edge_results def set_result(self, result: "ResultData") -> None: @@ -114,7 +122,9 @@ class Vertex: # If the Vertex.type is a power component # then we need to return the built object # instead of the result dict - if self.is_interface_component and not isinstance(self._built_object, UnbuiltObject): + if self.is_interface_component and not isinstance( + self._built_object, UnbuiltObject + ): result = self._built_object # if it is not a dict or a string and hasattr model_dump then # return the model_dump @@ -124,7 +134,11 @@ class Vertex: if isinstance(self._built_result, UnbuiltResult): return {} - return self._built_result if isinstance(self._built_result, dict) else {"result": self._built_result} + return ( + self._built_result + if isinstance(self._built_result, dict) + else {"result": self._built_result} + ) def set_artifacts(self) -> None: pass @@ -187,17 +201,29 @@ class Vertex: self.output = self.data["node"]["base_classes"] self.display_name = self.data["node"]["display_name"] self.pinned = self.data["node"].get("pinned", False) - template_dicts = {key: value for key, value in self.data["node"]["template"].items() if isinstance(value, dict)} + template_dicts = { + key: value + for key, value in self.data["node"]["template"].items() + if isinstance(value, dict) + } self.required_inputs = [ - template_dicts[key]["type"] for key, value in template_dicts.items() if value["required"] + template_dicts[key]["type"] + for key, value in template_dicts.items() + if value["required"] ] self.optional_inputs = [ - template_dicts[key]["type"] for key, value in template_dicts.items() if not value["required"] + template_dicts[key]["type"] + for key, value in template_dicts.items() + if not value["required"] ] # Add the template_dicts[key]["input_types"] to the optional_inputs self.optional_inputs.extend( - [input_type for value in template_dicts.values() for input_type in value.get("input_types", [])] + [ + input_type + for value in template_dicts.values() + for input_type in value.get("input_types", []) + ] ) template_dict = self.data["node"]["template"] @@ -240,7 +266,11 @@ class Vertex: if self.graph is None: raise ValueError("Graph not found") - template_dict = {key: value for key, value in self.data["node"]["template"].items() if isinstance(value, dict)} + template_dict = { + key: value + for key, value in self.data["node"]["template"].items() + if isinstance(value, dict) + } params = {} for edge in self.edges: @@ -278,7 +308,7 @@ class Vertex: full_path = storage_service.build_full_path(flow_id, file_name) params[key] = full_path else: - raise ValueError(f"File path not found for {self.vertex_type}") + raise ValueError(f"File path not found for {self.display_name}") elif value.get("type") in DIRECT_TYPES and params.get(key) is None: val = value.get("value") if value.get("type") == "code": @@ -291,7 +321,11 @@ class Vertex: # list of dicts, so we need to convert it to a dict # before passing it to the build method if isinstance(val, list): - params[key] = {k: v for item in value.get("value", []) for k, v in item.items()} + params[key] = { + k: v + for item in value.get("value", []) + for k, v in item.items() + } elif isinstance(val, dict): params[key] = val elif value.get("type") == "int" and val is not None: @@ -327,7 +361,7 @@ class Vertex: """ Initiate the build process. """ - logger.debug(f"Building {self.vertex_type}") + logger.debug(f"Building {self.display_name}") await self._build_each_node_in_params_dict(user_id) await self._get_and_instantiate_class(user_id) self._validate_built_object() @@ -354,7 +388,9 @@ class Vertex: if isinstance(self._built_object, str): self._built_result = self._built_object - result = await generate_result(self._built_object, inputs, self.has_external_output, session_id) + result = await generate_result( + self._built_object, inputs, self.has_external_output, session_id + ) self._built_result = result async def _build_each_node_in_params_dict(self, user_id=None): @@ -382,7 +418,9 @@ class Vertex: """ return all(self._is_node(node) for node in value) - async def get_result(self, requester: Optional["Vertex"] = None, user_id=None, timeout=None) -> Any: + async def get_result( + self, requester: Optional["Vertex"] = None, user_id=None, timeout=None + ) -> Any: # PLEASE REVIEW THIS IF STATEMENT # Check if the Vertex was built already if self._built: @@ -416,7 +454,9 @@ class Vertex: self._extend_params_list_with_result(key, result) self.params[key] = result - async def _build_list_of_nodes_and_update_params(self, key, nodes: List["Vertex"], user_id=None): + async def _build_list_of_nodes_and_update_params( + self, key, nodes: List["Vertex"], user_id=None + ): """ Iterates over a list of nodes, builds each and updates the params dictionary. """ @@ -457,7 +497,7 @@ class Vertex: Gets the class from a dictionary and instantiates it with the params. """ if self.base_type is None: - raise ValueError(f"Base type for node {self.vertex_type} not found") + raise ValueError(f"Base type for node {self.display_name} not found") try: result = await loading.instantiate_class( node_type=self.vertex_type, @@ -468,7 +508,9 @@ class Vertex: self._update_built_object_and_artifacts(result) except Exception as exc: logger.exception(exc) - raise ValueError(f"Error building node {self.display_name}: {str(exc)}") from exc + raise ValueError( + f"Error building node {self.display_name}: {str(exc)}" + ) from exc def _update_built_object_and_artifacts(self, result): """ @@ -484,9 +526,9 @@ class Vertex: Checks if the built object is None and raises a ValueError if so. """ if isinstance(self._built_object, UnbuiltObject): - raise ValueError(f"{self.vertex_type}: {self._built_object_repr()}") + raise ValueError(f"{self.display_name}: {self._built_object_repr()}") elif self._built_object is None: - message = f"{self.vertex_type} returned None." + message = f"{self.display_name} returned None." if self.base_type == "custom_components": message += " Make sure your build method returns a component." @@ -538,16 +580,24 @@ class Vertex: return self._built_object # Get the requester edge - requester_edge = next((edge for edge in self.edges if edge.target_id == requester.id), None) + requester_edge = next( + (edge for edge in self.edges if edge.target_id == requester.id), None + ) # Return the result of the requester edge - return None if requester_edge is None else await requester_edge.get_result(source=self, target=requester) + return ( + None + if requester_edge is None + else await requester_edge.get_result(source=self, target=requester) + ) def add_edge(self, edge: "ContractEdge") -> None: if edge not in self.edges: self.edges.append(edge) def __repr__(self) -> str: - return f"Vertex(display_name={self.display_name}, id={self.id}, data={self.data})" + return ( + f"Vertex(display_name={self.display_name}, id={self.id}, data={self.data})" + ) def __eq__(self, __o: object) -> bool: try: @@ -560,7 +610,11 @@ class Vertex: def _built_object_repr(self): # Add a message with an emoji, stars for sucess, - return "Built sucessfully ✨" if self._built_object is not None else "Failed to build 😵‍💫" + return ( + "Built sucessfully ✨" + if self._built_object is not None + else "Failed to build 😵‍💫" + ) class StatefulVertex(Vertex): diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index cec1f6caf..b7746b6a1 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -122,10 +122,12 @@ class DocumentLoaderVertex(StatefulVertex): # show how many documents are in the list? if not isinstance(self._built_object, UnbuiltObject): - avg_length = sum(len(doc.page_content) for doc in self._built_object if hasattr(doc, "page_content")) / len( - self._built_object - ) - return f"""{self.vertex_type}({len(self._built_object)} documents) + avg_length = sum( + len(doc.page_content) + for doc in self._built_object + if hasattr(doc, "page_content") + ) / len(self._built_object) + return f"""{self.display_name}({len(self._built_object)} documents) \nAvg. Document Length (characters): {int(avg_length)} Documents: {self._built_object[:3]}...""" return f"{self.vertex_type}()" @@ -197,7 +199,9 @@ class TextSplitterVertex(StatefulVertex): # show how many documents are in the list? if not isinstance(self._built_object, UnbuiltObject): - avg_length = sum(len(doc.page_content) for doc in self._built_object) / len(self._built_object) + avg_length = sum(len(doc.page_content) for doc in self._built_object) / len( + self._built_object + ) return f"""{self.vertex_type}({len(self._built_object)} documents) \nAvg. Document Length (characters): {int(avg_length)} \nDocuments: {self._built_object[:3]}...""" @@ -244,18 +248,27 @@ class PromptVertex(StatelessVertex): user_id = kwargs.get("user_id", None) tools = kwargs.get("tools", []) if not self._built or force: - if "input_variables" not in self.params or self.params["input_variables"] is None: + if ( + "input_variables" not in self.params + or self.params["input_variables"] is None + ): self.params["input_variables"] = [] # Check if it is a ZeroShotPrompt and needs a tool if "ShotPrompt" in self.vertex_type: - tools = [tool_node.build(user_id=user_id) for tool_node in tools] if tools is not None else [] + tools = ( + [tool_node.build(user_id=user_id) for tool_node in tools] + if tools is not None + else [] + ) # flatten the list of tools if it is a list of lists # first check if it is a list if tools and isinstance(tools, list) and isinstance(tools[0], list): tools = flatten_list(tools) self.params["tools"] = tools prompt_params = [ - key for key, value in self.params.items() if isinstance(value, str) and key != "format_instructions" + key + for key, value in self.params.items() + if isinstance(value, str) and key != "format_instructions" ] else: prompt_params = ["template"] @@ -265,14 +278,20 @@ class PromptVertex(StatelessVertex): prompt_text = self.params[param] variables = extract_input_variables_from_prompt(prompt_text) self.params["input_variables"].extend(variables) - self.params["input_variables"] = list(set(self.params["input_variables"])) + self.params["input_variables"] = list( + set(self.params["input_variables"]) + ) elif isinstance(self.params, dict): self.params.pop("input_variables", None) await self._build(user_id=user_id) def _built_object_repr(self): - if not self.artifacts or self._built_object is None or not hasattr(self._built_object, "format"): + if ( + not self.artifacts + or self._built_object is None + or not hasattr(self._built_object, "format") + ): return super()._built_object_repr() elif isinstance(self._built_object, UnbuiltObject): return super()._built_object_repr() @@ -284,7 +303,9 @@ class PromptVertex(StatelessVertex): # so the prompt format doesn't break artifacts.pop("handle_keys", None) try: - if not hasattr(self._built_object, "template") and hasattr(self._built_object, "prompt"): + if not hasattr(self._built_object, "template") and hasattr( + self._built_object, "prompt" + ): template = self._built_object.prompt.template else: template = self._built_object.template @@ -292,7 +313,11 @@ class PromptVertex(StatelessVertex): if value: replace_key = "{" + key + "}" template = template.replace(replace_key, value) - return template if isinstance(template, str) else f"{self.vertex_type}({template})" + return ( + template + if isinstance(template, str) + else f"{self.vertex_type}({template})" + ) except KeyError: return str(self._built_object) From 96e5ae65b1ee8dd813a123242e5ad4260af2dbc4 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Mon, 26 Feb 2024 21:23:16 +0100 Subject: [PATCH 06/19] Implemented output text --- src/frontend/src/components/IOOutputView/index.tsx | 5 +++-- src/frontend/src/constants/constants.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/IOOutputView/index.tsx b/src/frontend/src/components/IOOutputView/index.tsx index a443b982f..a31ac2796 100644 --- a/src/frontend/src/components/IOOutputView/index.tsx +++ b/src/frontend/src/components/IOOutputView/index.tsx @@ -11,6 +11,7 @@ export default function IOOutputView({ const setNode = useFlowStore((state) => state.setNode); const flowPool = useFlowStore((state) => state.flowPool); const node = nodes.find((node) => node.id === outputId); + console.log(flowPool[node!.id][flowPool[node!.id].length - 1]) function handleOutputType() { if (!node) return "no node found"; switch (outputType) { @@ -18,9 +19,9 @@ export default function IOOutputView({ return (