diff --git a/src/backend/langflow/api/v1/flows.py b/src/backend/langflow/api/v1/flows.py index 0afec1e71..e58a44e70 100644 --- a/src/backend/langflow/api/v1/flows.py +++ b/src/backend/langflow/api/v1/flows.py @@ -207,8 +207,11 @@ async def upload_file( async def download_file( *, session: Session = Depends(get_session), + settings_service: "SettingsService" = Depends(get_settings_service), current_user: User = Depends(get_current_active_user), ): """Download all flows as a file.""" - flows = read_flows(current_user=current_user) + flows = read_flows( + current_user=current_user, session=session, settings_service=settings_service + ) return FlowListRead(flows=flows) diff --git a/src/backend/langflow/components/data/APIRequest.py b/src/backend/langflow/components/data/APIRequest.py index 0873c2705..b9b3e2c51 100644 --- a/src/backend/langflow/components/data/APIRequest.py +++ b/src/backend/langflow/components/data/APIRequest.py @@ -56,7 +56,9 @@ class APIRequest(CustomComponent): data = body if body else None payload = json.dumps(data) try: - response = await client.request(method, url, headers=headers, content=payload, timeout=timeout) + response = await client.request( + method, url, headers=headers, content=payload, timeout=timeout + ) try: result = response.json() except Exception: @@ -104,8 +106,14 @@ class APIRequest(CustomComponent): bodies = [b.data for b in body] else: bodies = [body.data] + if len(urls) != len(bodies): + # add bodies with None + bodies += [None] * (len(urls) - len(bodies)) async with httpx.AsyncClient() as client: results = await asyncio.gather( - *[self.make_request(client, method, u, headers, rec, timeout) for u, rec in zip(urls, bodies)] + *[ + self.make_request(client, method, u, headers, rec, timeout) + for u, rec in zip(urls, bodies) + ] ) return results diff --git a/src/backend/langflow/graph/vertex/base.py b/src/backend/langflow/graph/vertex/base.py index c0cfb116d..db7602551 100644 --- a/src/backend/langflow/graph/vertex/base.py +++ b/src/backend/langflow/graph/vertex/base.py @@ -60,8 +60,13 @@ class Vertex: self.updated_raw_params = False self.id: str = data["id"] self.is_state = False - self.is_input = any(input_component_name in self.id for input_component_name in INPUT_COMPONENTS) - self.is_output = any(output_component_name in self.id for output_component_name in OUTPUT_COMPONENTS) + self.is_input = any( + input_component_name in self.id for input_component_name in INPUT_COMPONENTS + ) + self.is_output = any( + output_component_name in self.id + for output_component_name in OUTPUT_COMPONENTS + ) self.has_session_id = None self._custom_component = None self.has_external_input = False @@ -101,11 +106,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.inactivated_vertices.add(self.id) - elif self.state == VertexStates.ACTIVE and self.id in self.graph.inactivated_vertices: + elif ( + self.state == VertexStates.ACTIVE + and self.id in self.graph.inactivated_vertices + ): self.graph.inactivated_vertices.remove(self.id) @property @@ -122,7 +133,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 @@ -134,7 +147,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 @@ -166,6 +183,8 @@ class Vertex: "_built": False, "parent_node_id": self.parent_node_id, "parent_is_top_level": self.parent_is_top_level, + "is_input": self.is_input, + "is_output": self.is_output, } def __setstate__(self, state): @@ -175,6 +194,8 @@ class Vertex: self.is_task = state["is_task"] self.id = state["id"] self.frozen = state.get("frozen", False) + self.is_input = state.get("is_input", False) + self.is_output = state.get("is_output", False) self._parse_data() if "_built_object" in state: self._built_object = state["_built_object"] @@ -204,19 +225,31 @@ class Vertex: self.selected_output_type = self.data["node"].get("selected_output_type") self.is_input = self.data["node"].get("is_input") or self.is_input self.is_output = self.data["node"].get("is_output") or self.is_output - 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.has_session_id = "session_id" in template_dicts 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"] @@ -263,7 +296,11 @@ class Vertex: self.updated_raw_params = False return - 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: @@ -284,7 +321,10 @@ class Vertex: # we don't know the key of the dict but we need to set the value # to the vertex that is the source of the edge param_dict = template_dict[param_key]["value"] - params[param_key] = {key: self.graph.get_vertex(edge.source_id) for key in param_dict.keys()} + params[param_key] = { + key: self.graph.get_vertex(edge.source_id) + for key in param_dict.keys() + } else: params[param_key] = self.graph.get_vertex(edge.source_id) @@ -320,7 +360,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: @@ -445,7 +489,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): @@ -465,7 +511,9 @@ class Vertex: elif key not in self.params or self.updated_raw_params: self.params[key] = value - async def _build_dict_and_update_params(self, key, nodes_dict: Dict[str, "Vertex"], user_id=None): + async def _build_dict_and_update_params( + self, key, nodes_dict: Dict[str, "Vertex"], user_id=None + ): """ Iterates over a dictionary of nodes, builds each and updates the params dictionary. """ @@ -488,7 +536,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: @@ -522,7 +572,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. """ @@ -589,7 +641,9 @@ class Vertex: 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): """ @@ -617,7 +671,9 @@ class Vertex: logger.warning(message) elif isinstance(self._built_object, (Iterator, AsyncIterator)): if self.display_name in ["Text Output"]: - raise ValueError(f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead.") + raise ValueError( + f"You are trying to stream to a {self.display_name}. Try using a Chat Output instead." + ) def _reset(self, params_update: Optional[Dict[str, Any]] = None): self._built = False @@ -680,16 +736,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: @@ -710,4 +774,8 @@ 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 😵‍💫" + ) diff --git a/src/backend/langflow/graph/vertex/types.py b/src/backend/langflow/graph/vertex/types.py index 98f0e831f..8e1c194c5 100644 --- a/src/backend/langflow/graph/vertex/types.py +++ b/src/backend/langflow/graph/vertex/types.py @@ -371,7 +371,9 @@ class ChatVertex(Vertex): artifacts = None sender = self.params.get("sender", None) sender_name = self.params.get("sender_name", None) - message = unescape_string(self.params.get(INPUT_FIELD_NAME, None)) + message = self.params.get(INPUT_FIELD_NAME, None) + if isinstance(message, str): + message = unescape_string(message) stream_url = None if isinstance(self._built_object, AIMessage): artifacts = ChatOutputResponse.from_message( diff --git a/src/backend/langflow/services/socket/service.py b/src/backend/langflow/services/socket/service.py index fd2c236d5..81d1fad3c 100644 --- a/src/backend/langflow/services/socket/service.py +++ b/src/backend/langflow/services/socket/service.py @@ -20,11 +20,12 @@ class SocketIOService(Service): def init(self, sio: socketio.AsyncServer): # Registering event handlers self.sio = sio - self.sio.event(self.connect) - self.sio.event(self.disconnect) - self.sio.on("message")(self.message) - self.sio.on("get_vertices")(self.on_get_vertices) - self.sio.on("build_vertex")(self.on_build_vertex) + if self.sio: + self.sio.event(self.connect) + self.sio.event(self.disconnect) + self.sio.on("message")(self.message) + self.sio.on("get_vertices")(self.on_get_vertices) + self.sio.on("build_vertex")(self.on_build_vertex) self.sessions = {} # type: dict[str, dict] async def emit_error(self, sid, error): diff --git a/src/backend/langflow/utils/util.py b/src/backend/langflow/utils/util.py index 7aed2018c..9bc2d2030 100644 --- a/src/backend/langflow/utils/util.py +++ b/src/backend/langflow/utils/util.py @@ -11,7 +11,7 @@ from langflow.template.frontend_node.constants import FORCE_SHOW_FIELDS from langflow.utils import constants -def unescape_string(s): +def unescape_string(s: str): # Replace escaped new line characters with actual new line characters return s.replace("\\n", "\n") @@ -20,8 +20,12 @@ def remove_ansi_escape_codes(text): return re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", text) -def build_template_from_function(name: str, type_to_loader_dict: Dict, add_function: bool = False): - classes = [item.__annotations__["return"].__name__ for item in type_to_loader_dict.values()] +def build_template_from_function( + name: str, type_to_loader_dict: Dict, add_function: bool = False +): + classes = [ + item.__annotations__["return"].__name__ for item in type_to_loader_dict.values() + ] # Raise error if name is not in chains if name not in classes: @@ -42,8 +46,10 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct for name_, value_ in value.__repr_args__(): if name_ == "default_factory": try: - variables[class_field_items]["default"] = get_default_factory( - module=_class.__base__.__module__, function=value_ + variables[class_field_items]["default"] = ( + get_default_factory( + module=_class.__base__.__module__, function=value_ + ) ) except Exception: variables[class_field_items]["default"] = None @@ -51,7 +57,9 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct variables[class_field_items][name_] = value_ variables[class_field_items]["placeholder"] = ( - docs.params[class_field_items] if class_field_items in docs.params else "" + docs.params[class_field_items] + if class_field_items in docs.params + else "" ) # Adding function to base classes to allow # the output to be a function @@ -66,7 +74,9 @@ def build_template_from_function(name: str, type_to_loader_dict: Dict, add_funct } -def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: bool = False): +def build_template_from_class( + name: str, type_to_cls_dict: Dict, add_function: bool = False +): classes = [item.__name__ for item in type_to_cls_dict.values()] # Raise error if name is not in chains @@ -90,9 +100,11 @@ def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: b for name_, value_ in value.__repr_args__(): if name_ == "default_factory": try: - variables[class_field_items]["default"] = get_default_factory( - module=_class.__base__.__module__, - function=value_, + variables[class_field_items]["default"] = ( + get_default_factory( + module=_class.__base__.__module__, + function=value_, + ) ) except Exception: variables[class_field_items]["default"] = None @@ -100,7 +112,9 @@ def build_template_from_class(name: str, type_to_cls_dict: Dict, add_function: b variables[class_field_items][name_] = value_ variables[class_field_items]["placeholder"] = ( - docs.params[class_field_items] if class_field_items in docs.params else "" + docs.params[class_field_items] + if class_field_items in docs.params + else "" ) base_classes = get_base_classes(_class) # Adding function to base classes to allow @@ -132,7 +146,9 @@ def build_template_from_method( # Check if the method exists in this class if not hasattr(_class, method_name): - raise ValueError(f"Method {method_name} not found in class {class_name}") + raise ValueError( + f"Method {method_name} not found in class {class_name}" + ) # Get the method method = getattr(_class, method_name) @@ -151,8 +167,14 @@ def build_template_from_method( "_type": _type, **{ name: { - "default": (param.default if param.default != param.empty else None), - "type": (param.annotation if param.annotation != param.empty else None), + "default": ( + param.default if param.default != param.empty else None + ), + "type": ( + param.annotation + if param.annotation != param.empty + else None + ), "required": param.default == param.empty, } for name, param in params.items() @@ -239,7 +261,9 @@ def sync_to_async(func): return async_wrapper -def format_dict(dictionary: Dict[str, Any], class_name: Optional[str] = None) -> Dict[str, Any]: +def format_dict( + dictionary: Dict[str, Any], class_name: Optional[str] = None +) -> Dict[str, Any]: """ Formats a dictionary by removing certain keys and modifying the values of other keys. @@ -325,7 +349,9 @@ def check_list_type(_type: str, value: Dict[str, Any]) -> str: The modified type string. """ if any(list_type in _type for list_type in ["List", "Sequence", "Set"]): - _type = _type.replace("List[", "").replace("Sequence[", "").replace("Set[", "")[:-1] + _type = ( + _type.replace("List[", "").replace("Sequence[", "").replace("Set[", "")[:-1] + ) value["list"] = True else: value["list"] = False @@ -428,7 +454,9 @@ def set_headers_value(value: Dict[str, Any]) -> None: value["value"] = """{"Authorization": "Bearer "}""" -def add_options_to_field(value: Dict[str, Any], class_name: Optional[str], key: str) -> None: +def add_options_to_field( + value: Dict[str, Any], class_name: Optional[str], key: str +) -> None: """ Adds options to the field based on the class name and key. """