Tai Truong
fix readme
d202ada
from loguru import logger
from langflow.custom.directory_reader import DirectoryReader
from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode
def merge_nested_dicts_with_renaming(dict1, dict2):
for key, value in dict2.items():
if key in dict1 and isinstance(value, dict) and isinstance(dict1.get(key), dict):
for sub_key, sub_value in value.items():
# if sub_key in dict1[key]:
# new_key = get_new_key(dict1[key], sub_key)
# dict1[key][new_key] = sub_value
# else:
dict1[key][sub_key] = sub_value
else:
dict1[key] = value
return dict1
def build_invalid_menu(invalid_components):
"""Build the invalid menu."""
if not invalid_components.get("menu"):
return {}
logger.debug("------------------- INVALID COMPONENTS -------------------")
invalid_menu = {}
for menu_item in invalid_components["menu"]:
menu_name = menu_item["name"]
invalid_menu[menu_name] = build_invalid_menu_items(menu_item)
return invalid_menu
def build_valid_menu(valid_components):
"""Build the valid menu."""
valid_menu = {}
logger.debug("------------------- VALID COMPONENTS -------------------")
for menu_item in valid_components["menu"]:
menu_name = menu_item["name"]
valid_menu[menu_name] = build_menu_items(menu_item)
return valid_menu
def build_and_validate_all_files(reader: DirectoryReader, file_list):
"""Build and validate all files."""
data = reader.build_component_menu_list(file_list)
valid_components = reader.filter_loaded_components(data=data, with_errors=False)
invalid_components = reader.filter_loaded_components(data=data, with_errors=True)
return valid_components, invalid_components
async def abuild_and_validate_all_files(reader: DirectoryReader, file_list):
"""Build and validate all files."""
data = await reader.abuild_component_menu_list(file_list)
valid_components = reader.filter_loaded_components(data=data, with_errors=False)
invalid_components = reader.filter_loaded_components(data=data, with_errors=True)
return valid_components, invalid_components
def load_files_from_path(path: str):
"""Load all files from a given path."""
reader = DirectoryReader(path, compress_code_field=False)
return reader.get_files()
def build_custom_component_list_from_path(path: str):
"""Build a list of custom components for the langchain from a given path."""
file_list = load_files_from_path(path)
reader = DirectoryReader(path, compress_code_field=False)
valid_components, invalid_components = build_and_validate_all_files(reader, file_list)
valid_menu = build_valid_menu(valid_components)
invalid_menu = build_invalid_menu(invalid_components)
return merge_nested_dicts_with_renaming(valid_menu, invalid_menu)
async def abuild_custom_component_list_from_path(path: str):
"""Build a list of custom components for the langchain from a given path."""
file_list = load_files_from_path(path)
reader = DirectoryReader(path, compress_code_field=False)
valid_components, invalid_components = await abuild_and_validate_all_files(reader, file_list)
valid_menu = build_valid_menu(valid_components)
invalid_menu = build_invalid_menu(invalid_components)
return merge_nested_dicts_with_renaming(valid_menu, invalid_menu)
def create_invalid_component_template(component, component_name):
"""Create a template for an invalid component."""
component_code = component["code"]
component_frontend_node = CustomComponentFrontendNode(
description="ERROR - Check your Python Code",
display_name=f"ERROR - {component_name}",
)
component_frontend_node.error = component.get("error", None)
field = component_frontend_node.template.get_field("code")
field.value = component_code
component_frontend_node.template.update_field("code", field)
return component_frontend_node.model_dump(by_alias=True, exclude_none=True)
def log_invalid_component_details(component) -> None:
"""Log details of an invalid component."""
logger.debug(component)
logger.debug(f"Component Path: {component.get('path', None)}")
logger.debug(f"Component Error: {component.get('error', None)}")
def build_invalid_component(component):
"""Build a single invalid component."""
component_name = component["name"]
component_template = create_invalid_component_template(component, component_name)
log_invalid_component_details(component)
return component_name, component_template
def build_invalid_menu_items(menu_item):
"""Build invalid menu items for a given menu."""
menu_items = {}
for component in menu_item["components"]:
try:
component_name, component_template = build_invalid_component(component)
menu_items[component_name] = component_template
logger.debug(f"Added {component_name} to invalid menu.")
except Exception: # noqa: BLE001
logger.exception(f"Error while creating custom component [{component_name}]")
return menu_items
def get_new_key(dictionary, original_key):
counter = 1
new_key = original_key + " (" + str(counter) + ")"
while new_key in dictionary:
counter += 1
new_key = original_key + " (" + str(counter) + ")"
return new_key
def determine_component_name(component):
"""Determine the name of the component."""
# component_output_types = component["output_types"]
# if len(component_output_types) == 1:
# return component_output_types[0]
# else:
# file_name = component.get("file").split(".")[0]
# return "".join(word.capitalize() for word in file_name.split("_")) if "_" in file_name else file_name
return component["name"]
def build_menu_items(menu_item):
"""Build menu items for a given menu."""
menu_items = {}
logger.debug(f"Building menu items for {menu_item['name']}")
logger.debug(f"Loading {len(menu_item['components'])} components")
for component_name, component_template, component in menu_item["components"]:
try:
menu_items[component_name] = component_template
except Exception: # noqa: BLE001
logger.exception(f"Error while building custom component {component['output_types']}")
return menu_items