From 883f852b73f2dc068d5ecaece8972fdb1d17b9b8 Mon Sep 17 00:00:00 2001 From: gustavoschaedler Date: Tue, 25 Jul 2023 21:58:09 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(utils.py):=20move=20merge=5F?= =?UTF-8?q?nested=5Fdicts=20function=20to=20the=20top=20of=20the=20file=20?= =?UTF-8?q?for=20better=20organization=20=E2=9C=A8=20feat(utils.py):=20add?= =?UTF-8?q?=20merge=5Fnested=5Fdicts=20function=20to=20merge=20nested=20di?= =?UTF-8?q?ctionaries=20recursively=20=F0=9F=90=9B=20fix(endpoints.py):=20?= =?UTF-8?q?import=20merge=5Fnested=5Fdicts=20function=20from=20the=20corre?= =?UTF-8?q?ct=20module=20=E2=9C=A8=20feat(endpoints.py):=20use=20merge=5Fn?= =?UTF-8?q?ested=5Fdicts=20function=20to=20merge=20dictionaries=20in=20bui?= =?UTF-8?q?ld=5Flangchain=5Fcustom=5Fcomponent=5Flist=5Ffrom=5Fpath=20func?= =?UTF-8?q?tion=20=F0=9F=90=9B=20fix(types.py):=20import=20merge=5Fnested?= =?UTF-8?q?=5Fdicts=20function=20from=20the=20correct=20module=20=E2=9C=A8?= =?UTF-8?q?=20feat(types.py):=20use=20merge=5Fnested=5Fdicts=20function=20?= =?UTF-8?q?to=20merge=20dictionaries=20in=20build=5Flangchain=5Fcustom=5Fc?= =?UTF-8?q?omponent=5Flist=5Ffrom=5Fpath=20function=20=F0=9F=90=9B=20fix(t?= =?UTF-8?q?ypes.py):=20import=20merge=5Fnested=5Fdicts=20function=20from?= =?UTF-8?q?=20the=20correct=20module=20=E2=9C=A8=20feat(types.py):=20use?= =?UTF-8?q?=20merge=5Fnested=5Fdicts=20function=20to=20merge=20valid=20and?= =?UTF-8?q?=20invalid=20menus=20in=20build=5Flangchain=5Fcustom=5Fcomponen?= =?UTF-8?q?t=5Flist=5Ffrom=5Fpath=20function=20=F0=9F=90=9B=20fix(tools.py?= =?UTF-8?q?):=20import=20Optional=20from=20typing=20module=20=E2=9C=A8=20f?= =?UTF-8?q?eat(tools.py):=20add=20CustomComponentEmptyNode=20class=20to=20?= =?UTF-8?q?represent=20an=20empty=20custom=20component=20template?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/langflow/api/utils.py | 9 ++++ src/backend/langflow/api/v1/endpoints.py | 12 +---- src/backend/langflow/interface/types.py | 47 +++++++++++++++---- .../langflow/template/frontend_node/tools.py | 28 ++++++++++- 4 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/backend/langflow/api/utils.py b/src/backend/langflow/api/utils.py index 2384a4089..91fa93ea4 100644 --- a/src/backend/langflow/api/utils.py +++ b/src/backend/langflow/api/utils.py @@ -57,3 +57,12 @@ def build_input_keys_response(langchain_object, artifacts): input_keys_response["template"] = langchain_object.prompt.template return input_keys_response + + +def merge_nested_dicts(dict1, dict2): + for key, value in dict2.items(): + if isinstance(value, dict) and isinstance(dict1.get(key), dict): + dict1[key] = merge_nested_dicts(dict1[key], value) + else: + dict1[key] = value + return dict1 diff --git a/src/backend/langflow/api/v1/endpoints.py b/src/backend/langflow/api/v1/endpoints.py index 8e3f66805..f60640d4e 100644 --- a/src/backend/langflow/api/v1/endpoints.py +++ b/src/backend/langflow/api/v1/endpoints.py @@ -21,6 +21,8 @@ from langflow.api.v1.schemas import ( CustomComponentCode, ) +from langflow.api.utils import merge_nested_dicts + from langflow.interface.types import ( build_langchain_types_dict, build_langchain_template_custom_component, @@ -34,16 +36,6 @@ from sqlmodel import Session router = APIRouter(tags=["Base"]) -# TODO: Move to correct local -def merge_nested_dicts(dict1, dict2): - for key, value in dict2.items(): - if isinstance(value, dict) and isinstance(dict1.get(key), dict): - dict1[key] = merge_nested_dicts(dict1[key], value) - else: - dict1[key] = value - return dict1 - - @router.get("/all") def get_all(): native_components = build_langchain_types_dict() diff --git a/src/backend/langflow/interface/types.py b/src/backend/langflow/interface/types.py index eaaa47f2b..ea85013fd 100644 --- a/src/backend/langflow/interface/types.py +++ b/src/backend/langflow/interface/types.py @@ -18,16 +18,21 @@ from langflow.interface.custom.base import custom_component_creator from langflow.interface.custom.custom_component import CustomComponent from langflow.template.field.base import TemplateField -from langflow.template.frontend_node.tools import CustomComponentNode +from langflow.template.frontend_node.tools import ( + CustomComponentNode, + CustomComponentEmptyNode, +) from langflow.interface.retrievers.base import retriever_creator from langflow.interface.custom.directory_reader import DirectoryReader from langflow.utils.logger import logger +from langflow.utils.util import get_base_classes +from langflow.api.utils import merge_nested_dicts + import re import warnings import traceback from fastapi import HTTPException -from langflow.utils.util import get_base_classes # Used to get the base_classes list @@ -250,15 +255,14 @@ def build_langchain_custom_component_list_from_path(path: str): # Build and validate all files data = reader.build_component_menu_list(file_list) - valid_components = reader.filter_loaded_components(data, False) - # TODO: Handle those invalid components - reader.filter_loaded_components(data, True) + valid_components = reader.filter_loaded_components(data=data, with_errors=False) + invalid_components = reader.filter_loaded_components(data=data, with_errors=True) - menu = {} + valid_menu = {} for menu_item in valid_components["menu"]: menu_name = menu_item["name"] - menu[menu_name] = {} + valid_menu[menu_name] = {} for component in menu_item["components"]: try: @@ -271,8 +275,33 @@ def build_langchain_custom_component_list_from_path(path: str): component_extractor ) - menu[menu_name][component_name] = component_template + valid_menu[menu_name][component_name] = component_template except Exception as exc: logger.error(f"Error while building custom component: {exc}") - return menu + invalid_menu = {} + for menu_item in invalid_components["menu"]: + menu_name = menu_item["name"] + invalid_menu[menu_name] = {} + + for component in menu_item["components"]: + try: + component_name = component["name"] + component_code = component["code"] + + component_template = ( + CustomComponentNode( + description="ERROR - Check your Python Code", + display_name=f"ERROR - {component_name}", + ) + .to_dict() + .get(type(CustomComponent()).__name__) + ) + + component_template.get("template").get("code")["value"] = component_code + + invalid_menu[menu_name][component_name] = component_template + except Exception as exc: + logger.error(f"Error while creating custom component: {exc}") + + return merge_nested_dicts(valid_menu, invalid_menu) diff --git a/src/backend/langflow/template/frontend_node/tools.py b/src/backend/langflow/template/frontend_node/tools.py index d23033b35..c7ed716c1 100644 --- a/src/backend/langflow/template/frontend_node/tools.py +++ b/src/backend/langflow/template/frontend_node/tools.py @@ -5,6 +5,7 @@ from langflow.template.template.base import Template from langflow.utils.constants import ( DEFAULT_PYTHON_FUNCTION, ) +from typing import Optional class ToolNode(FrontendNode): @@ -160,7 +161,32 @@ class CustomComponentNode(FrontendNode): ) ], ) - description: str = "Python Class to be executed." + description: str = "Dynamic Python code to be executed." + base_classes: list[str] = [] + + def to_dict(self): + return super().to_dict() + + +class CustomComponentEmptyNode(FrontendNode): + name: str = "CustomComponent" + template: Template = Template( + type_name="CustomComponent", + fields=[ + TemplateField( + field_type="code", + required=True, + placeholder="", + is_list=False, + show=True, + value="", + name="code", + advanced=False, + dynamic=True, + ) + ], + ) + description: str = "Dynamic Python code to be executed." base_classes: list[str] = [] def to_dict(self):