🐛 fix(endpoints.py): handle CustomComponentPathValueError and raise HTTPException with error details

🐛 fix(chat/manager.py): rename exception variable from e to exc for clarity
🐛 fix(load_custom_component_from_path.py): handle case when file_path does not exist and return None
🐛 fix(load_custom_component_from_path.py): raise CustomComponentPathValueError if directory path is invalid
🐛 fix(load_custom_component_from_path.py): handle case when file_content is None and return error message
This commit is contained in:
gustavoschaedler 2023-07-20 23:35:02 +01:00
commit 7b22e0780f
3 changed files with 39 additions and 19 deletions

View file

@ -10,6 +10,10 @@ from fastapi import APIRouter, Depends, HTTPException, UploadFile
from langflow.interface.custom.custom_component import CustomComponent from langflow.interface.custom.custom_component import CustomComponent
from langflow.interface.custom.load_custom_component_from_path import (
CustomComponentPathValueError,
)
from langflow.api.v1.schemas import ( from langflow.api.v1.schemas import (
ProcessResponse, ProcessResponse,
UploadFileResponse, UploadFileResponse,
@ -36,7 +40,15 @@ def get_all():
@router.get("/load_custom_component_from_path") @router.get("/load_custom_component_from_path")
def get_load_custom_component_from_path(path: str): def get_load_custom_component_from_path(path: str):
return build_langchain_custom_component_list_from_path(path) try:
data = build_langchain_custom_component_list_from_path(path)
except CustomComponentPathValueError as err:
raise HTTPException(
status_code=400,
detail={"error": type(err).__name__, "traceback": str(err)},
) from err
return data
@router.get("/load_custom_component_from_path_TEST") @router.get("/load_custom_component_from_path_TEST")
@ -52,8 +64,6 @@ def get_load_custom_component_from_path_test(path: str):
# For backwards compatibility we will keep the old endpoint # For backwards compatibility we will keep the old endpoint
@router.post("/predict/{flow_id}", response_model=ProcessResponse) @router.post("/predict/{flow_id}", response_model=ProcessResponse)
@router.post("/process/{flow_id}", response_model=ProcessResponse) @router.post("/process/{flow_id}", response_model=ProcessResponse)
async def process_flow( async def process_flow(

View file

@ -203,7 +203,7 @@ class ChatManager:
await self.close_connection( await self.close_connection(
client_id=client_id, client_id=client_id,
code=status.WS_1011_INTERNAL_ERROR, code=status.WS_1011_INTERNAL_ERROR,
reason=str(e)[:120], reason=str(exc)[:120],
) )
finally: finally:
try: try:

View file

@ -3,6 +3,10 @@ import ast
import zlib import zlib
class CustomComponentPathValueError(ValueError):
pass
class StringCompressor: class StringCompressor:
def __init__(self, input_string): def __init__(self, input_string):
"""Initialize StringCompressor with a string to compress.""" """Initialize StringCompressor with a string to compress."""
@ -30,6 +34,8 @@ class StringCompressor:
class DirectoryReader: class DirectoryReader:
base_path = "/custom_component_files"
def __init__(self, directory_path, compress_code_field=False): def __init__(self, directory_path, compress_code_field=False):
""" """
Initialize DirectoryReader with a directory path Initialize DirectoryReader with a directory path
@ -38,6 +44,15 @@ class DirectoryReader:
self.directory_path = directory_path self.directory_path = directory_path
self.compress_code_field = compress_code_field self.compress_code_field = compress_code_field
def get_safe_path(self):
"""Check if the path is valid and return it, or None if it's not."""
return self.directory_path if self.is_valid_path() else None
def is_valid_path(self) -> bool:
"""Check if the directory path is valid by comparing it to the base path."""
fullpath = os.path.normpath(os.path.join(self.directory_path))
return fullpath.startswith(self.base_path)
def is_empty_file(self, file_content): def is_empty_file(self, file_content):
""" """
Check if the file content is empty. Check if the file content is empty.
@ -64,27 +79,21 @@ class DirectoryReader:
""" """
Read and return the content of a file. Read and return the content of a file.
""" """
if not os.path.isfile(file_path):
return None
with open(file_path, "r") as file: with open(file_path, "r") as file:
return file.read() return file.read()
def compress_string(self, content: str):
"""
Compress a string and return the compressed data.
"""
return StringCompressor(content).compress_string()
def decompress_string(self, content: str):
"""
Decompress a string and return the original string.
"""
return StringCompressor(content).decompress_string()
def get_files(self): def get_files(self):
""" """
Walk through the directory path and return a list of all .py files. Walk through the directory path and return a list of all .py files.
""" """
if not (safe_path := self.get_safe_path()):
raise CustomComponentPathValueError(
f"The path needs to start with '{self.base_path}'."
)
file_list = [] file_list = []
for root, _, files in os.walk(self.directory_path): for root, _, files in os.walk(safe_path):
file_list.extend( file_list.extend(
os.path.join(root, filename) os.path.join(root, filename)
for filename in files for filename in files
@ -107,6 +116,8 @@ class DirectoryReader:
returning the result and content/error message. returning the result and content/error message.
""" """
file_content = self.read_file_content(file_path) file_content = self.read_file_content(file_path)
if file_content is None:
return False, f"Could not read {file_path}"
if self.is_empty_file(file_content): if self.is_empty_file(file_content):
return False, "Empty file" return False, "Empty file"
@ -116,8 +127,7 @@ class DirectoryReader:
return False, "Missing build function" return False, "Missing build function"
else: else:
if self.compress_code_field: if self.compress_code_field:
file_content = str(self.compress_string(file_content)) file_content = str(StringCompressor(file_content).compress_string())
return True, file_content return True, file_content
def build_component_menu_list(self, file_paths): def build_component_menu_list(self, file_paths):