Merge branch 'zustand/io/migration' into state_theories
This commit is contained in:
commit
dc8ad28418
79 changed files with 1598 additions and 1613 deletions
|
|
@ -56,6 +56,13 @@ LANGFLOW_REMOVE_API_KEYS=
|
||||||
# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600)
|
# LANGFLOW_REDIS_CACHE_EXPIRE (default: 3600)
|
||||||
LANGFLOW_CACHE_TYPE=
|
LANGFLOW_CACHE_TYPE=
|
||||||
|
|
||||||
|
|
||||||
|
# Set AUTO_LOGIN to false if you want to disable auto login
|
||||||
|
# and use the login form to login. LANGFLOW_SUPERUSER and LANGFLOW_SUPERUSER_PASSWORD
|
||||||
|
# must be set if AUTO_LOGIN is set to false
|
||||||
|
# Values: true, false
|
||||||
|
LANGFLOW_AUTO_LOGIN=
|
||||||
|
|
||||||
# Superuser username
|
# Superuser username
|
||||||
# Example: LANGFLOW_SUPERUSER=admin
|
# Example: LANGFLOW_SUPERUSER=admin
|
||||||
LANGFLOW_SUPERUSER=
|
LANGFLOW_SUPERUSER=
|
||||||
|
|
|
||||||
|
|
@ -68,8 +68,6 @@ INVALID_CHARACTERS = {
|
||||||
")",
|
")",
|
||||||
"[",
|
"[",
|
||||||
"]",
|
"]",
|
||||||
"{",
|
|
||||||
"}",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INVALID_NAMES = {
|
INVALID_NAMES = {
|
||||||
|
|
@ -88,73 +86,110 @@ def validate_prompt(template: str):
|
||||||
# Check if there are invalid characters in the input_variables
|
# Check if there are invalid characters in the input_variables
|
||||||
input_variables = check_input_variables(input_variables)
|
input_variables = check_input_variables(input_variables)
|
||||||
if any(var in INVALID_NAMES for var in input_variables):
|
if any(var in INVALID_NAMES for var in input_variables):
|
||||||
raise ValueError(f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. ")
|
raise ValueError(
|
||||||
|
f"Invalid input variables. None of the variables can be named {', '.join(input_variables)}. "
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
PromptTemplate(template=template, input_variables=input_variables)
|
PromptTemplate(template=template, input_variables=input_variables)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise ValueError(str(exc)) from exc
|
raise ValueError(f"Invalid prompt: {exc}") from exc
|
||||||
|
|
||||||
return input_variables
|
return input_variables
|
||||||
|
|
||||||
|
|
||||||
def check_input_variables(input_variables: list):
|
def is_json_like(var):
|
||||||
|
if var.startswith("{{") and var.endswith("}}"):
|
||||||
|
# If it is a double brance variable
|
||||||
|
# we don't want to validate any of its content
|
||||||
|
return True
|
||||||
|
# the above doesn't work on all cases because the json string can be multiline
|
||||||
|
# or indented which can add \n or spaces at the start or end of the string
|
||||||
|
# test_case_3 new_var == '\n{{\n "test": "hello",\n "text": "world"\n}}\n'
|
||||||
|
# what we can do is to remove the \n and spaces from the start and end of the string
|
||||||
|
# and then check if the string starts with {{ and ends with }}
|
||||||
|
var = var.strip()
|
||||||
|
var = var.replace("\n", "")
|
||||||
|
var = var.replace(" ", "")
|
||||||
|
# Now it should be a valid json string
|
||||||
|
return var.startswith("{{") and var.endswith("}}")
|
||||||
|
|
||||||
|
|
||||||
|
def fix_variable(var, invalid_chars, wrong_variables):
|
||||||
|
if not var:
|
||||||
|
return var, invalid_chars, wrong_variables
|
||||||
|
new_var = var
|
||||||
|
|
||||||
|
# Handle variables starting with a number
|
||||||
|
if var[0].isdigit():
|
||||||
|
invalid_chars.append(var[0])
|
||||||
|
new_var, invalid_chars, wrong_variables = fix_variable(
|
||||||
|
var[1:], invalid_chars, wrong_variables
|
||||||
|
)
|
||||||
|
|
||||||
|
# Temporarily replace {{ and }} to avoid treating them as invalid
|
||||||
|
new_var = new_var.replace("{{", "ᴛᴇᴍᴘᴏᴘᴇɴ").replace("}}", "ᴛᴇᴍᴘᴄʟᴏsᴇ")
|
||||||
|
|
||||||
|
# Remove invalid characters
|
||||||
|
for char in new_var:
|
||||||
|
if char in INVALID_CHARACTERS:
|
||||||
|
invalid_chars.append(char)
|
||||||
|
new_var = new_var.replace(char, "")
|
||||||
|
if var not in wrong_variables: # Avoid duplicating entries
|
||||||
|
wrong_variables.append(var)
|
||||||
|
|
||||||
|
# Restore {{ and }}
|
||||||
|
new_var = new_var.replace("ᴛᴇᴍᴘᴏᴘᴇɴ", "{{").replace("ᴛᴇᴍᴘᴄʟᴏsᴇ", "}}")
|
||||||
|
|
||||||
|
return new_var, invalid_chars, wrong_variables
|
||||||
|
|
||||||
|
|
||||||
|
def check_variable(var, invalid_chars, wrong_variables, empty_variables):
|
||||||
|
if any(char in invalid_chars for char in var):
|
||||||
|
wrong_variables.append(var)
|
||||||
|
elif var == "":
|
||||||
|
empty_variables.append(var)
|
||||||
|
return wrong_variables, empty_variables
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_errors(
|
||||||
|
input_variables, fixed_variables, wrong_variables, empty_variables
|
||||||
|
):
|
||||||
|
if any(var for var in input_variables if var not in fixed_variables):
|
||||||
|
error_message = (
|
||||||
|
f"Error: Input variables contain invalid characters or formats. \n"
|
||||||
|
f"Invalid variables: {', '.join(wrong_variables)}.\n"
|
||||||
|
f"Empty variables: {', '.join(empty_variables)}. \n"
|
||||||
|
f"Fixed variables: {', '.join(fixed_variables)}."
|
||||||
|
)
|
||||||
|
raise ValueError(error_message)
|
||||||
|
|
||||||
|
|
||||||
|
def check_input_variables(input_variables):
|
||||||
invalid_chars = []
|
invalid_chars = []
|
||||||
fixed_variables = []
|
fixed_variables = []
|
||||||
wrong_variables = []
|
wrong_variables = []
|
||||||
empty_variables = []
|
empty_variables = []
|
||||||
for variable in input_variables:
|
variables_to_check = []
|
||||||
new_var = variable
|
|
||||||
|
|
||||||
# if variable is empty, then we should add that to the wrong variables
|
for var in input_variables:
|
||||||
if not variable:
|
# First, let's check if the variable is a JSON string
|
||||||
empty_variables.append(variable)
|
# because if it is, it won't be considered a variable
|
||||||
|
# and we don't need to validate it
|
||||||
|
if is_json_like(var):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# if variable starts with a number we should add that to the invalid chars
|
new_var, wrong_variables, empty_variables = fix_variable(
|
||||||
# and wrong variables
|
var, invalid_chars, wrong_variables
|
||||||
if variable[0].isdigit():
|
|
||||||
invalid_chars.append(variable[0])
|
|
||||||
new_var = new_var.replace(variable[0], "")
|
|
||||||
wrong_variables.append(variable)
|
|
||||||
else:
|
|
||||||
for char in INVALID_CHARACTERS:
|
|
||||||
if char in variable:
|
|
||||||
invalid_chars.append(char)
|
|
||||||
new_var = new_var.replace(char, "")
|
|
||||||
wrong_variables.append(variable)
|
|
||||||
fixed_variables.append(new_var)
|
|
||||||
# If any of the input_variables is not in the fixed_variables, then it means that
|
|
||||||
# there are invalid characters in the input_variables
|
|
||||||
|
|
||||||
if any(var not in fixed_variables for var in input_variables):
|
|
||||||
error_message = build_error_message(
|
|
||||||
input_variables,
|
|
||||||
invalid_chars,
|
|
||||||
wrong_variables,
|
|
||||||
fixed_variables,
|
|
||||||
empty_variables,
|
|
||||||
)
|
)
|
||||||
raise ValueError(error_message)
|
wrong_variables, empty_variables = check_variable(
|
||||||
return input_variables
|
var, INVALID_CHARACTERS, wrong_variables, empty_variables
|
||||||
|
)
|
||||||
|
fixed_variables.append(new_var)
|
||||||
|
variables_to_check.append(var)
|
||||||
|
|
||||||
|
check_for_errors(
|
||||||
|
variables_to_check, fixed_variables, wrong_variables, empty_variables
|
||||||
|
)
|
||||||
|
|
||||||
def build_error_message(input_variables, invalid_chars, wrong_variables, fixed_variables, empty_variables):
|
return fixed_variables
|
||||||
input_variables_str = ", ".join([f"'{var}'" for var in input_variables])
|
|
||||||
error_string = f"Invalid input variables: {input_variables_str}. "
|
|
||||||
|
|
||||||
if wrong_variables and invalid_chars:
|
|
||||||
# fix the wrong variables replacing invalid chars and find them in the fixed variables
|
|
||||||
error_string_vars = "You can fix them by replacing the invalid characters: "
|
|
||||||
wvars = wrong_variables.copy()
|
|
||||||
for i, wrong_var in enumerate(wvars):
|
|
||||||
for char in invalid_chars:
|
|
||||||
wrong_var = wrong_var.replace(char, "")
|
|
||||||
if wrong_var in fixed_variables:
|
|
||||||
error_string_vars += f"'{wrong_variables[i]}' -> '{wrong_var}'"
|
|
||||||
error_string += error_string_vars
|
|
||||||
elif empty_variables:
|
|
||||||
error_string += f" There are {len(empty_variables)} empty variable{'s' if len(empty_variables) > 1 else ''}."
|
|
||||||
elif len(set(fixed_variables)) != len(fixed_variables):
|
|
||||||
error_string += "There are duplicate variables."
|
|
||||||
return error_string
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from langchain_core.documents import Document
|
from typing import List
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
|
from langflow.schema import Record
|
||||||
from langflow.utils.constants import LOADERS_INFO
|
from langflow.utils.constants import LOADERS_INFO
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,7 +11,9 @@ class FileLoaderComponent(CustomComponent):
|
||||||
beta = True
|
beta = True
|
||||||
|
|
||||||
def build_config(self):
|
def build_config(self):
|
||||||
loader_options = ["Automatic"] + [loader_info["name"] for loader_info in LOADERS_INFO]
|
loader_options = ["Automatic"] + [
|
||||||
|
loader_info["name"] for loader_info in LOADERS_INFO
|
||||||
|
]
|
||||||
|
|
||||||
file_types = []
|
file_types = []
|
||||||
suffixes = []
|
suffixes = []
|
||||||
|
|
@ -74,7 +77,7 @@ class FileLoaderComponent(CustomComponent):
|
||||||
"code": {"show": False},
|
"code": {"show": False},
|
||||||
}
|
}
|
||||||
|
|
||||||
def build(self, file_path: str, loader: str) -> Document:
|
def build(self, file_path: str, loader: str) -> List[Record]:
|
||||||
file_type = file_path.split(".")[-1]
|
file_type = file_path.split(".")[-1]
|
||||||
|
|
||||||
# Map the loader to the correct loader class
|
# Map the loader to the correct loader class
|
||||||
|
|
@ -102,7 +105,9 @@ class FileLoaderComponent(CustomComponent):
|
||||||
if isinstance(selected_loader_info, dict):
|
if isinstance(selected_loader_info, dict):
|
||||||
loader_import: str = selected_loader_info["import"]
|
loader_import: str = selected_loader_info["import"]
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Loader info for {loader} is not a dict\nLoader info:\n{selected_loader_info}")
|
raise ValueError(
|
||||||
|
f"Loader info for {loader} is not a dict\nLoader info:\n{selected_loader_info}"
|
||||||
|
)
|
||||||
module_name, class_name = loader_import.rsplit(".", 1)
|
module_name, class_name = loader_import.rsplit(".", 1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -110,7 +115,10 @@ class FileLoaderComponent(CustomComponent):
|
||||||
loader_module = __import__(module_name, fromlist=[class_name])
|
loader_module = __import__(module_name, fromlist=[class_name])
|
||||||
loader_instance = getattr(loader_module, class_name)
|
loader_instance = getattr(loader_module, class_name)
|
||||||
except ImportError as e:
|
except ImportError as e:
|
||||||
raise ValueError(f"Loader {loader} could not be imported\nLoader info:\n{selected_loader_info}") from e
|
raise ValueError(
|
||||||
|
f"Loader {loader} could not be imported\nLoader info:\n{selected_loader_info}"
|
||||||
|
) from e
|
||||||
|
|
||||||
result = loader_instance(file_path=file_path)
|
result = loader_instance(file_path=file_path)
|
||||||
return result.load()
|
docs = result.load()
|
||||||
|
return self.to_records(docs)
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,17 @@ class GatherRecordsComponent(CustomComponent):
|
||||||
|
|
||||||
glob = "**/*" if recursive else "*"
|
glob = "**/*" if recursive else "*"
|
||||||
paths = walk_level(path_obj, depth) if depth else path_obj.glob(glob)
|
paths = walk_level(path_obj, depth) if depth else path_obj.glob(glob)
|
||||||
file_paths = [Text(p) for p in paths if p.is_file() and match_types(p) and is_not_hidden(p)]
|
file_paths = [
|
||||||
|
Text(p)
|
||||||
|
for p in paths
|
||||||
|
if p.is_file() and match_types(p) and is_not_hidden(p)
|
||||||
|
]
|
||||||
|
|
||||||
return file_paths
|
return file_paths
|
||||||
|
|
||||||
def parse_file_to_record(self, file_path: str, silent_errors: bool) -> Optional[Record]:
|
def parse_file_to_record(
|
||||||
|
self, file_path: str, silent_errors: bool
|
||||||
|
) -> Optional[Record]:
|
||||||
# Use the partition function to load the file
|
# Use the partition function to load the file
|
||||||
from unstructured.partition.auto import partition # type: ignore
|
from unstructured.partition.auto import partition # type: ignore
|
||||||
|
|
||||||
|
|
@ -100,9 +106,14 @@ class GatherRecordsComponent(CustomComponent):
|
||||||
use_multithreading: bool,
|
use_multithreading: bool,
|
||||||
) -> List[Optional[Record]]:
|
) -> List[Optional[Record]]:
|
||||||
if use_multithreading:
|
if use_multithreading:
|
||||||
records = self.parallel_load_records(file_paths, silent_errors, max_concurrency)
|
records = self.parallel_load_records(
|
||||||
|
file_paths, silent_errors, max_concurrency
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
records = [self.parse_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
records = [
|
||||||
|
self.parse_file_to_record(file_path, silent_errors)
|
||||||
|
for file_path in file_paths
|
||||||
|
]
|
||||||
records = list(filter(None, records))
|
records = list(filter(None, records))
|
||||||
return records
|
return records
|
||||||
|
|
||||||
|
|
@ -131,13 +142,20 @@ class GatherRecordsComponent(CustomComponent):
|
||||||
if types is None:
|
if types is None:
|
||||||
types = []
|
types = []
|
||||||
resolved_path = self.resolve_path(path)
|
resolved_path = self.resolve_path(path)
|
||||||
file_paths = self.retrieve_file_paths(resolved_path, types, load_hidden, recursive, depth)
|
file_paths = self.retrieve_file_paths(
|
||||||
|
resolved_path, types, load_hidden, recursive, depth
|
||||||
|
)
|
||||||
loaded_records = []
|
loaded_records = []
|
||||||
|
|
||||||
if use_multithreading:
|
if use_multithreading:
|
||||||
loaded_records = self.parallel_load_records(file_paths, silent_errors, max_concurrency)
|
loaded_records = self.parallel_load_records(
|
||||||
|
file_paths, silent_errors, max_concurrency
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
loaded_records = [self.parse_file_to_record(file_path, silent_errors) for file_path in file_paths]
|
loaded_records = [
|
||||||
|
self.parse_file_to_record(file_path, silent_errors)
|
||||||
|
for file_path in file_paths
|
||||||
|
]
|
||||||
loaded_records = list(filter(None, loaded_records))
|
loaded_records = list(filter(None, loaded_records))
|
||||||
self.status = loaded_records
|
self.status = loaded_records
|
||||||
return loaded_records
|
return loaded_records
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ class ChatOutput(ChatComponent):
|
||||||
|
|
||||||
def build(
|
def build(
|
||||||
self,
|
self,
|
||||||
sender: Optional[str] = "User",
|
sender: Optional[str] = "Machine",
|
||||||
sender_name: Optional[str] = "User",
|
sender_name: Optional[str] = "AI",
|
||||||
input_value: Optional[str] = None,
|
input_value: Optional[str] = None,
|
||||||
session_id: Optional[str] = None,
|
session_id: Optional[str] = None,
|
||||||
return_record: Optional[bool] = False,
|
return_record: Optional[bool] = False,
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,12 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow.components.io.base.text import TextComponent
|
||||||
from langflow.field_typing import Text
|
from langflow.field_typing import Text
|
||||||
|
|
||||||
|
|
||||||
class TextInput(CustomComponent):
|
class TextInput(TextComponent):
|
||||||
display_name = "Text Input"
|
display_name = "Text Input"
|
||||||
description = "Used to pass text input to the next component."
|
description = "Used to pass text input to the next component."
|
||||||
|
|
||||||
field_config = {
|
|
||||||
"input_value": {"display_name": "Value", "multiline": True},
|
|
||||||
}
|
|
||||||
|
|
||||||
def build(self, input_value: Optional[str] = "") -> Text:
|
def build(self, input_value: Optional[str] = "") -> Text:
|
||||||
self.status = input_value
|
return super().build(input_value=input_value)
|
||||||
if not input_value:
|
|
||||||
input_value = ""
|
|
||||||
return input_value
|
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow.components.io.base.text import TextComponent
|
||||||
from langflow.field_typing import Text
|
from langflow.field_typing import Text
|
||||||
|
|
||||||
|
|
||||||
class TextOutput(CustomComponent):
|
class TextOutput(TextComponent):
|
||||||
display_name = "Text Output"
|
display_name = "Text Output"
|
||||||
description = "Used to pass text output to the next component."
|
description = "Used to pass text output to the next component."
|
||||||
|
|
||||||
field_config = {
|
field_config = {
|
||||||
"value": {"display_name": "Value"},
|
"input_value": {"display_name": "Value"},
|
||||||
}
|
}
|
||||||
|
|
||||||
def build(self, value: Optional[str] = "") -> Text:
|
def build(self, input_value: Optional[Text] = "") -> Text:
|
||||||
self.status = value
|
return super().build(input_value=input_value)
|
||||||
if not value:
|
|
||||||
value = ""
|
|
||||||
return value
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,9 @@ class ChatComponent(CustomComponent):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
if not session_id or not sender or not sender_name:
|
if not session_id or not sender or not sender_name:
|
||||||
raise ValueError("All of session_id, sender, and sender_name must be provided.")
|
raise ValueError(
|
||||||
|
"All of session_id, sender, and sender_name must be provided."
|
||||||
|
)
|
||||||
if isinstance(message, Record):
|
if isinstance(message, Record):
|
||||||
record = message
|
record = message
|
||||||
record.data.update(
|
record.data.update(
|
||||||
|
|
|
||||||
19
src/backend/langflow/components/io/base/text.py
Normal file
19
src/backend/langflow/components/io/base/text.py
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from langflow import CustomComponent
|
||||||
|
from langflow.field_typing import Text
|
||||||
|
|
||||||
|
|
||||||
|
class TextComponent(CustomComponent):
|
||||||
|
display_name = "Text Component"
|
||||||
|
description = "Used to pass text to the next component."
|
||||||
|
|
||||||
|
field_config = {
|
||||||
|
"input_value": {"display_name": "Value", "multiline": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
def build(self, input_value: Optional[str] = "") -> Text:
|
||||||
|
self.status = input_value
|
||||||
|
if not input_value:
|
||||||
|
input_value = ""
|
||||||
|
return input_value
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import Text
|
from langflow.field_typing import Text
|
||||||
|
from langflow.helpers.record import records_to_text
|
||||||
from langflow.schema import Record
|
from langflow.schema import Record
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -27,7 +28,6 @@ class RecordsAsTextComponent(CustomComponent):
|
||||||
if isinstance(records, Record):
|
if isinstance(records, Record):
|
||||||
records = [records]
|
records = [records]
|
||||||
|
|
||||||
formated_records = [template.format(text=record.text, data=record.data, **record.data) for record in records]
|
result_string = records_to_text(template, records)
|
||||||
result_string = "\n".join(formated_records)
|
|
||||||
self.status = result_string
|
self.status = result_string
|
||||||
return result_string
|
return result_string
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import Text
|
|
||||||
from langflow.schema import Record
|
from langflow.schema import Record
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,8 @@ class ChromaSearchComponent(LCVectorStoreComponent):
|
||||||
|
|
||||||
if chroma_server_host is not None:
|
if chroma_server_host is not None:
|
||||||
chroma_settings = chromadb.config.Settings(
|
chroma_settings = chromadb.config.Settings(
|
||||||
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins or None,
|
chroma_server_cors_allow_origins=chroma_server_cors_allow_origins
|
||||||
|
or None,
|
||||||
chroma_server_host=chroma_server_host,
|
chroma_server_host=chroma_server_host,
|
||||||
chroma_server_port=chroma_server_port or None,
|
chroma_server_port=chroma_server_port or None,
|
||||||
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
chroma_server_grpc_port=chroma_server_grpc_port or None,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@ class FAISSSearchComponent(LCVectorStoreComponent):
|
||||||
if not folder_path:
|
if not folder_path:
|
||||||
raise ValueError("Folder path is required to save the FAISS index.")
|
raise ValueError("Folder path is required to save the FAISS index.")
|
||||||
path = self.resolve_path(folder_path)
|
path = self.resolve_path(folder_path)
|
||||||
vector_store = FAISS.load_local(folder_path=Text(path), embeddings=embedding, index_name=index_name)
|
vector_store = FAISS.load_local(
|
||||||
|
folder_path=Text(path), embeddings=embedding, index_name=index_name
|
||||||
|
)
|
||||||
if not vector_store:
|
if not vector_store:
|
||||||
raise ValueError("Failed to load the FAISS index.")
|
raise ValueError("Failed to load the FAISS index.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,9 @@ class SupabaseSearchComponent(LCVectorStoreComponent):
|
||||||
supabase_url: str = "",
|
supabase_url: str = "",
|
||||||
table_name: str = "",
|
table_name: str = "",
|
||||||
) -> List[Record]:
|
) -> List[Record]:
|
||||||
supabase: Client = create_client(supabase_url, supabase_key=supabase_service_key)
|
supabase: Client = create_client(
|
||||||
|
supabase_url, supabase_key=supabase_service_key
|
||||||
|
)
|
||||||
vector_store = SupabaseVectorStore(
|
vector_store = SupabaseVectorStore(
|
||||||
client=supabase,
|
client=supabase,
|
||||||
embedding=embedding,
|
embedding=embedding,
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ from langflow.schema import Record
|
||||||
class VectaraSearchComponent(VectaraComponent, LCVectorStoreComponent):
|
class VectaraSearchComponent(VectaraComponent, LCVectorStoreComponent):
|
||||||
display_name: str = "Vectara Search"
|
display_name: str = "Vectara Search"
|
||||||
description: str = "Search a Vectara Vector Store for similar documents."
|
description: str = "Search a Vectara Vector Store for similar documents."
|
||||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/vectara"
|
documentation = (
|
||||||
|
"https://python.langchain.com/docs/integrations/vectorstores/vectara"
|
||||||
|
)
|
||||||
beta = True
|
beta = True
|
||||||
icon = "Vectara"
|
icon = "Vectara"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,9 @@ from langflow.schema import Record
|
||||||
class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreComponent):
|
class WeaviateSearchVectorStore(WeaviateVectorStoreComponent, LCVectorStoreComponent):
|
||||||
display_name: str = "Weaviate Search"
|
display_name: str = "Weaviate Search"
|
||||||
description: str = "Search a Weaviate Vector Store for similar documents."
|
description: str = "Search a Weaviate Vector Store for similar documents."
|
||||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/weaviate"
|
documentation = (
|
||||||
|
"https://python.langchain.com/docs/integrations/vectorstores/weaviate"
|
||||||
|
)
|
||||||
beta = True
|
beta = True
|
||||||
icon = "Weaviate"
|
icon = "Weaviate"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ from langchain_core.vectorstores import VectorStore
|
||||||
|
|
||||||
from langflow import CustomComponent
|
from langflow import CustomComponent
|
||||||
from langflow.field_typing import Text
|
from langflow.field_typing import Text
|
||||||
from langflow.schema import Record, docs_to_records
|
from langflow.helpers.record import docs_to_records
|
||||||
|
from langflow.schema import Record
|
||||||
|
|
||||||
|
|
||||||
class LCVectorStoreComponent(CustomComponent):
|
class LCVectorStoreComponent(CustomComponent):
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@ class PGVectorSearchComponent(PGVectorComponent, LCVectorStoreComponent):
|
||||||
|
|
||||||
display_name: str = "PGVector Search"
|
display_name: str = "PGVector Search"
|
||||||
description: str = "Search a PGVector Store for similar documents."
|
description: str = "Search a PGVector Store for similar documents."
|
||||||
documentation = "https://python.langchain.com/docs/integrations/vectorstores/pgvector"
|
documentation = (
|
||||||
|
"https://python.langchain.com/docs/integrations/vectorstores/pgvector"
|
||||||
|
)
|
||||||
|
|
||||||
def build_config(self):
|
def build_config(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
from typing import TYPE_CHECKING, Any, List, Optional
|
from typing import TYPE_CHECKING, Any, List, Optional
|
||||||
|
|
||||||
from loguru import logger
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
from langflow.graph.edge.utils import build_clean_params
|
from langflow.graph.edge.utils import build_clean_params
|
||||||
from langflow.graph.schema import INPUT_FIELD_NAME
|
from langflow.graph.schema import INPUT_FIELD_NAME
|
||||||
from langflow.services.deps import get_monitor_service
|
from langflow.services.deps import get_monitor_service
|
||||||
from langflow.services.monitor.utils import log_message
|
from langflow.services.monitor.utils import log_message
|
||||||
|
from loguru import logger
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.graph.vertex.base import Vertex
|
from langflow.graph.vertex.base import Vertex
|
||||||
|
|
||||||
|
|
||||||
class SourceHandle(BaseModel):
|
class SourceHandle(BaseModel):
|
||||||
baseClasses: List[str] = Field(..., description="List of base classes for the source handle.")
|
baseClasses: List[str] = Field(
|
||||||
|
..., description="List of base classes for the source handle."
|
||||||
|
)
|
||||||
dataType: str = Field(..., description="Data type for the source handle.")
|
dataType: str = Field(..., description="Data type for the source handle.")
|
||||||
id: str = Field(..., description="Unique identifier for the source handle.")
|
id: str = Field(..., description="Unique identifier for the source handle.")
|
||||||
|
|
||||||
|
|
@ -21,7 +22,9 @@ class SourceHandle(BaseModel):
|
||||||
class TargetHandle(BaseModel):
|
class TargetHandle(BaseModel):
|
||||||
fieldName: str = Field(..., description="Field name for the target handle.")
|
fieldName: str = Field(..., description="Field name for the target handle.")
|
||||||
id: str = Field(..., description="Unique identifier for the target handle.")
|
id: str = Field(..., description="Unique identifier for the target handle.")
|
||||||
inputTypes: Optional[List[str]] = Field(None, description="List of input types for the target handle.")
|
inputTypes: Optional[List[str]] = Field(
|
||||||
|
None, description="List of input types for the target handle."
|
||||||
|
)
|
||||||
type: str = Field(..., description="Type of the target handle.")
|
type: str = Field(..., description="Type of the target handle.")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -50,16 +53,24 @@ class Edge:
|
||||||
|
|
||||||
def validate_handles(self, source, target) -> None:
|
def validate_handles(self, source, target) -> None:
|
||||||
if self.target_handle.inputTypes is None:
|
if self.target_handle.inputTypes is None:
|
||||||
self.valid_handles = self.target_handle.type in self.source_handle.baseClasses
|
self.valid_handles = (
|
||||||
|
self.target_handle.type in self.source_handle.baseClasses
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.valid_handles = (
|
self.valid_handles = (
|
||||||
any(baseClass in self.target_handle.inputTypes for baseClass in self.source_handle.baseClasses)
|
any(
|
||||||
|
baseClass in self.target_handle.inputTypes
|
||||||
|
for baseClass in self.source_handle.baseClasses
|
||||||
|
)
|
||||||
or self.target_handle.type in self.source_handle.baseClasses
|
or self.target_handle.type in self.source_handle.baseClasses
|
||||||
)
|
)
|
||||||
if not self.valid_handles:
|
if not self.valid_handles:
|
||||||
logger.debug(self.source_handle)
|
logger.debug(self.source_handle)
|
||||||
logger.debug(self.target_handle)
|
logger.debug(self.target_handle)
|
||||||
raise ValueError(f"Edge between {source.vertex_type} and {target.vertex_type} " f"has invalid handles")
|
raise ValueError(
|
||||||
|
f"Edge between {source.vertex_type} and {target.vertex_type} "
|
||||||
|
f"has invalid handles"
|
||||||
|
)
|
||||||
|
|
||||||
def __setstate__(self, state):
|
def __setstate__(self, state):
|
||||||
self.source_id = state["source_id"]
|
self.source_id = state["source_id"]
|
||||||
|
|
@ -76,7 +87,11 @@ class Edge:
|
||||||
# Both lists contain strings and sometimes a string contains the value we are
|
# Both lists contain strings and sometimes a string contains the value we are
|
||||||
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
# looking for e.g. comgin_out=["Chain"] and target_reqs=["LLMChain"]
|
||||||
# so we need to check if any of the strings in source_types is in target_reqs
|
# so we need to check if any of the strings in source_types is in target_reqs
|
||||||
self.valid = any(output in target_req for output in self.source_types for target_req in self.target_reqs)
|
self.valid = any(
|
||||||
|
output in target_req
|
||||||
|
for output in self.source_types
|
||||||
|
for target_req in self.target_reqs
|
||||||
|
)
|
||||||
# Get what type of input the target node is expecting
|
# Get what type of input the target node is expecting
|
||||||
|
|
||||||
self.matched_type = next(
|
self.matched_type = next(
|
||||||
|
|
@ -87,7 +102,10 @@ class Edge:
|
||||||
if no_matched_type:
|
if no_matched_type:
|
||||||
logger.debug(self.source_types)
|
logger.debug(self.source_types)
|
||||||
logger.debug(self.target_reqs)
|
logger.debug(self.target_reqs)
|
||||||
raise ValueError(f"Edge between {source.vertex_type} and {target.vertex_type} " f"has no matched type")
|
raise ValueError(
|
||||||
|
f"Edge between {source.vertex_type} and {target.vertex_type} "
|
||||||
|
f"has no matched type"
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return (
|
return (
|
||||||
|
|
@ -98,8 +116,12 @@ class Edge:
|
||||||
def __hash__(self) -> int:
|
def __hash__(self) -> int:
|
||||||
return hash(self.__repr__())
|
return hash(self.__repr__())
|
||||||
|
|
||||||
def __eq__(self, __value: object) -> bool:
|
def __eq__(self, __o: object) -> bool:
|
||||||
return self.__repr__() == __value.__repr__() if isinstance(__value, Edge) else False
|
# Create a better way to compare edges
|
||||||
|
return (
|
||||||
|
self._source_handle == __o._source_handle
|
||||||
|
and self._target_handle == __o._target_handle
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ContractEdge(Edge):
|
class ContractEdge(Edge):
|
||||||
|
|
@ -156,7 +178,9 @@ class ContractEdge(Edge):
|
||||||
return f"{self.source_id} -[{self.target_param}]-> {self.target_id}"
|
return f"{self.source_id} -[{self.target_param}]-> {self.target_id}"
|
||||||
|
|
||||||
|
|
||||||
def log_transaction(edge: ContractEdge, source: "Vertex", target: "Vertex", status, error=None):
|
def log_transaction(
|
||||||
|
edge: ContractEdge, source: "Vertex", target: "Vertex", status, error=None
|
||||||
|
):
|
||||||
try:
|
try:
|
||||||
monitor_service = get_monitor_service()
|
monitor_service = get_monitor_service()
|
||||||
clean_params = build_clean_params(target)
|
clean_params = build_clean_params(target)
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,6 @@ from collections import defaultdict, deque
|
||||||
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Type, Union
|
from typing import TYPE_CHECKING, Dict, Generator, List, Optional, Type, Union
|
||||||
|
|
||||||
from langchain.chains.base import Chain
|
from langchain.chains.base import Chain
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from langflow.graph.edge.base import ContractEdge
|
from langflow.graph.edge.base import ContractEdge
|
||||||
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
from langflow.graph.graph.constants import lazy_load_vertex_dict
|
||||||
from langflow.graph.graph.state_manager import GraphStateManager
|
from langflow.graph.graph.state_manager import GraphStateManager
|
||||||
|
|
@ -22,6 +20,7 @@ from langflow.graph.vertex.types import (
|
||||||
from langflow.interface.tools.constants import FILE_TOOLS
|
from langflow.interface.tools.constants import FILE_TOOLS
|
||||||
from langflow.schema import Record
|
from langflow.schema import Record
|
||||||
from langflow.utils import payload
|
from langflow.utils import payload
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from langflow.graph.schema import ResultData
|
from langflow.graph.schema import ResultData
|
||||||
|
|
@ -278,7 +277,7 @@ class Graph:
|
||||||
self.edges = new_edges
|
self.edges = new_edges
|
||||||
|
|
||||||
def vertex_data_is_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool:
|
def vertex_data_is_identical(self, vertex: Vertex, other_vertex: Vertex) -> bool:
|
||||||
data_is_equivalent = vertex.__repr__() == other_vertex.__repr__()
|
data_is_equivalent = vertex == other_vertex
|
||||||
if not data_is_equivalent:
|
if not data_is_equivalent:
|
||||||
return False
|
return False
|
||||||
return self.vertex_edges_are_identical(vertex, other_vertex)
|
return self.vertex_edges_are_identical(vertex, other_vertex)
|
||||||
|
|
@ -304,40 +303,52 @@ class Graph:
|
||||||
# Find vertices that are in self but not in other (removed vertices)
|
# Find vertices that are in self but not in other (removed vertices)
|
||||||
removed_vertex_ids = existing_vertex_ids - other_vertex_ids
|
removed_vertex_ids = existing_vertex_ids - other_vertex_ids
|
||||||
|
|
||||||
# Update existing vertices that have changed
|
# Remove vertices that are not in the other graph
|
||||||
for vertex_id in existing_vertex_ids.intersection(other_vertex_ids):
|
|
||||||
self_vertex = self.get_vertex(vertex_id)
|
|
||||||
other_vertex = other.get_vertex(vertex_id)
|
|
||||||
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
|
|
||||||
# If the vertex is pinned, we don't want
|
|
||||||
# to reset the results nor the _built attribute
|
|
||||||
if not self_vertex.pinned:
|
|
||||||
self_vertex._built = False
|
|
||||||
self_vertex.result = None
|
|
||||||
self_vertex.artifacts = {}
|
|
||||||
self_vertex.set_top_level(self.top_level_vertices)
|
|
||||||
self.reset_all_edges_of_vertex(self_vertex)
|
|
||||||
|
|
||||||
# Remove vertices
|
|
||||||
for vertex_id in removed_vertex_ids:
|
for vertex_id in removed_vertex_ids:
|
||||||
self.remove_vertex(vertex_id)
|
self.remove_vertex(vertex_id)
|
||||||
|
|
||||||
# Add new vertices
|
# Add new vertices
|
||||||
for vertex_id in new_vertex_ids:
|
for vertex_id in new_vertex_ids:
|
||||||
new_vertex = other.get_vertex(vertex_id)
|
new_vertex = other.get_vertex(vertex_id)
|
||||||
|
new_vertex.graph = self
|
||||||
self._add_vertex(new_vertex)
|
self._add_vertex(new_vertex)
|
||||||
|
|
||||||
|
# Update existing vertices that have changed
|
||||||
|
for vertex_id in existing_vertex_ids.intersection(other_vertex_ids):
|
||||||
|
self_vertex = self.get_vertex(vertex_id)
|
||||||
|
other_vertex = other.get_vertex(vertex_id)
|
||||||
|
# If the vertices are not identical, update the vertex
|
||||||
|
if not self.vertex_data_is_identical(self_vertex, other_vertex):
|
||||||
|
self.update_vertex_from_another(self_vertex, other_vertex)
|
||||||
|
|
||||||
self.build_graph_maps()
|
self.build_graph_maps()
|
||||||
self.increment_update_count()
|
self.increment_update_count()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def update_vertex_from_another(self, vertex: Vertex, other_vertex: Vertex) -> None:
|
||||||
|
"""
|
||||||
|
Updates a vertex from another vertex.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
vertex (Vertex): The vertex to be updated.
|
||||||
|
other_vertex (Vertex): The vertex to update from.
|
||||||
|
"""
|
||||||
|
vertex._data = other_vertex._data
|
||||||
|
vertex._parse_data()
|
||||||
|
# Now we update the edges of the vertex
|
||||||
|
self.update_edges_from_vertex(vertex, other_vertex)
|
||||||
|
vertex.params = {}
|
||||||
|
vertex._build_params()
|
||||||
|
vertex.graph = self
|
||||||
|
# If the vertex is pinned, we don't want
|
||||||
|
# to reset the results nor the _built attribute
|
||||||
|
if not vertex.pinned:
|
||||||
|
vertex._built = False
|
||||||
|
vertex.result = None
|
||||||
|
vertex.artifacts = {}
|
||||||
|
vertex.set_top_level(self.top_level_vertices)
|
||||||
|
self.reset_all_edges_of_vertex(vertex)
|
||||||
|
|
||||||
def reset_all_edges_of_vertex(self, vertex: Vertex) -> None:
|
def reset_all_edges_of_vertex(self, vertex: Vertex) -> None:
|
||||||
"""Resets all the edges of a vertex."""
|
"""Resets all the edges of a vertex."""
|
||||||
for edge in vertex.edges:
|
for edge in vertex.edges:
|
||||||
|
|
@ -666,18 +677,42 @@ class Graph:
|
||||||
"""Cuts the graph up to a given vertex and sorts the resulting subgraph."""
|
"""Cuts the graph up to a given vertex and sorts the resulting subgraph."""
|
||||||
# Initial setup
|
# Initial setup
|
||||||
visited = set() # To keep track of visited vertices
|
visited = set() # To keep track of visited vertices
|
||||||
|
excluded = set() # To keep track of vertices that should be excluded
|
||||||
stack = [vertex_id] # Use a list as a stack for DFS
|
stack = [vertex_id] # Use a list as a stack for DFS
|
||||||
|
|
||||||
|
def get_successors(vertex, recursive=True):
|
||||||
|
# Recursively get the successors of the current vertex
|
||||||
|
successors = vertex.successors
|
||||||
|
if not successors:
|
||||||
|
return []
|
||||||
|
successors_result = []
|
||||||
|
for successor in successors:
|
||||||
|
# Just return a list of successors
|
||||||
|
if recursive:
|
||||||
|
next_successors = get_successors(successor)
|
||||||
|
successors_result.extend(next_successors)
|
||||||
|
successors_result.append(successor)
|
||||||
|
return successors_result
|
||||||
|
|
||||||
# DFS to collect all vertices that can reach the specified vertex
|
# DFS to collect all vertices that can reach the specified vertex
|
||||||
while stack:
|
while stack:
|
||||||
current_id = stack.pop()
|
current_id = stack.pop()
|
||||||
if current_id not in visited:
|
if current_id not in visited and current_id not in excluded:
|
||||||
visited.add(current_id)
|
visited.add(current_id)
|
||||||
current_vertex = self.get_vertex(current_id)
|
current_vertex = self.get_vertex(current_id)
|
||||||
# Assuming get_predecessors is a method that returns all vertices with edges to current_vertex
|
# Assuming get_predecessors is a method that returns all vertices with edges to current_vertex
|
||||||
for predecessor in current_vertex.predecessors:
|
for predecessor in current_vertex.predecessors:
|
||||||
stack.append(predecessor.id)
|
stack.append(predecessor.id)
|
||||||
|
|
||||||
|
if current_id == vertex_id:
|
||||||
|
# We should add to visited all the vertices that are successors of the current vertex
|
||||||
|
# and their successors and so on
|
||||||
|
for successor in current_vertex.successors:
|
||||||
|
excluded.add(successor.id)
|
||||||
|
all_successors = get_successors(successor)
|
||||||
|
for successor in all_successors:
|
||||||
|
excluded.add(successor.id)
|
||||||
|
|
||||||
# Filter the original graph's vertices and edges to keep only those in `visited`
|
# Filter the original graph's vertices and edges to keep only those in `visited`
|
||||||
vertices_to_keep = [self.get_vertex(vid) for vid in visited]
|
vertices_to_keep = [self.get_vertex(vid) for vid in visited]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,17 @@ import ast
|
||||||
import inspect
|
import inspect
|
||||||
import types
|
import types
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import TYPE_CHECKING, Any, Callable, Coroutine, Dict, List, Optional
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
AsyncIterator,
|
||||||
|
Callable,
|
||||||
|
Coroutine,
|
||||||
|
Dict,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
)
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
|
@ -503,9 +513,12 @@ class Vertex:
|
||||||
self.params[key] = []
|
self.params[key] = []
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
built = await node.get_result(requester=self, user_id=user_id)
|
built = await node.get_result(requester=self, user_id=user_id)
|
||||||
|
# Weird check to see if the params[key] is a list
|
||||||
|
# because sometimes it is a Record and breaks the code
|
||||||
|
if not isinstance(self.params[key], list):
|
||||||
|
self.params[key] = [self.params[key]]
|
||||||
|
|
||||||
if isinstance(built, list):
|
if isinstance(built, list):
|
||||||
if key not in self.params:
|
|
||||||
self.params[key] = []
|
|
||||||
self.params[key].extend(built)
|
self.params[key].extend(built)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
|
@ -517,6 +530,7 @@ class Vertex:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Params {key} ({self.params[key]}) is not a list and cannot be extended with {built}"
|
f"Params {key} ({self.params[key]}) is not a list and cannot be extended with {built}"
|
||||||
|
f"Error building node {self.display_name}: {str(e)}"
|
||||||
) from e
|
) from e
|
||||||
|
|
||||||
def _handle_func(self, key, result):
|
def _handle_func(self, key, result):
|
||||||
|
|
@ -587,6 +601,14 @@ class Vertex:
|
||||||
message += " Make sure your build method returns a component."
|
message += " Make sure your build method returns a component."
|
||||||
|
|
||||||
logger.warning(message)
|
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"{self.display_name}: You are trying to stream to a non-streamable component."
|
||||||
|
)
|
||||||
|
|
||||||
def _reset(self, params_update: Optional[Dict[str, Any]] = None):
|
def _reset(self, params_update: Optional[Dict[str, Any]] = None):
|
||||||
self._built = False
|
self._built = False
|
||||||
|
|
@ -662,7 +684,15 @@ class Vertex:
|
||||||
|
|
||||||
def __eq__(self, __o: object) -> bool:
|
def __eq__(self, __o: object) -> bool:
|
||||||
try:
|
try:
|
||||||
return self.id == __o.id if isinstance(__o, Vertex) else False
|
if not isinstance(__o, Vertex):
|
||||||
|
return False
|
||||||
|
# We should create a more robust comparison
|
||||||
|
# for the Vertex class
|
||||||
|
ids_are_equal = self.id == __o.id
|
||||||
|
# self._data is a dict and we need to compare them
|
||||||
|
# to check if they are equal
|
||||||
|
data_are_equal = self.data == __o.data
|
||||||
|
return ids_are_equal and data_are_equal
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
|
||||||
3
src/backend/langflow/helpers/__init__.py
Normal file
3
src/backend/langflow/helpers/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .record import docs_to_records, records_to_text
|
||||||
|
|
||||||
|
__all__ = ["docs_to_records", "records_to_text"]
|
||||||
37
src/backend/langflow/helpers/record.py
Normal file
37
src/backend/langflow/helpers/record.py
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
from langchain_core.documents import Document
|
||||||
|
|
||||||
|
from langflow.schema import Record
|
||||||
|
|
||||||
|
|
||||||
|
def docs_to_records(documents: list[Document]) -> list[Record]:
|
||||||
|
"""
|
||||||
|
Converts a list of Documents to a list of Records.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
documents (list[Document]): The list of Documents to convert.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Record]: The converted list of Records.
|
||||||
|
"""
|
||||||
|
return [Record.from_document(document) for document in documents]
|
||||||
|
|
||||||
|
|
||||||
|
def records_to_text(template: str, records: list[Record]) -> list[str]:
|
||||||
|
"""
|
||||||
|
Converts a list of Records to a list of texts.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
records (list[Record]): The list of Records to convert.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[str]: The converted list of texts.
|
||||||
|
"""
|
||||||
|
if isinstance(records, Record):
|
||||||
|
records = [records]
|
||||||
|
# Check if there are any format strings in the template
|
||||||
|
|
||||||
|
formated_records = [
|
||||||
|
template.format(text=record.text, data=record.data, **record.data)
|
||||||
|
for record in records
|
||||||
|
]
|
||||||
|
return "\n".join(formated_records)
|
||||||
|
|
@ -15,6 +15,7 @@ from uuid import UUID
|
||||||
import yaml
|
import yaml
|
||||||
from cachetools import TTLCache, cachedmethod
|
from cachetools import TTLCache, cachedmethod
|
||||||
from langchain_core.documents import Document
|
from langchain_core.documents import Document
|
||||||
|
from pydantic import BaseModel
|
||||||
from sqlmodel import select
|
from sqlmodel import select
|
||||||
|
|
||||||
from langflow.interface.custom.code_parser.utils import (
|
from langflow.interface.custom.code_parser.utils import (
|
||||||
|
|
@ -144,29 +145,48 @@ class CustomComponent(Component):
|
||||||
self, data: Any, text_key: str = "text", data_key: str = "data"
|
self, data: Any, text_key: str = "text", data_key: str = "data"
|
||||||
) -> List[Record]:
|
) -> List[Record]:
|
||||||
"""
|
"""
|
||||||
Convert data into a list of records.
|
Converts input data into a list of Record objects.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
data (Any): The input data to be converted.
|
data (Any): The input data to be converted. It can be a single item or a sequence of items.
|
||||||
text_key (str, optional): The key to extract the text from a dictionary item. Defaults to "text".
|
If the input data is a Langchain Document, text_key and data_key are ignored.
|
||||||
data_key (str, optional): The key to extract the data from a dictionary item. Defaults to "data".
|
|
||||||
|
text_key (str, optional): The key to access the text value in each item. Defaults to "text".
|
||||||
|
data_key (str, optional): The key to access the data value in each item. Defaults to "data".
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[dict]: A list of records, where each record is a dictionary with 'text' and 'data' keys.
|
List[Record]: A list of Record objects.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If the input data is not of a valid type or if the specified keys are not found in the data.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
records = []
|
records = []
|
||||||
if not isinstance(data, Sequence):
|
if not isinstance(data, Sequence):
|
||||||
data = [data]
|
data = [data]
|
||||||
for item in data:
|
for item in data:
|
||||||
if isinstance(item, str):
|
if isinstance(item, Document):
|
||||||
records.append(Record(text=item))
|
item = {"text": item.page_content, "data": item.metadata}
|
||||||
|
elif isinstance(item, BaseModel):
|
||||||
|
model_dump = item.model_dump()
|
||||||
|
if text_key not in model_dump:
|
||||||
|
raise ValueError(f"Key '{text_key}' not found in BaseModel item.")
|
||||||
|
if data_key not in model_dump:
|
||||||
|
raise ValueError(f"Key '{data_key}' not found in BaseModel item.")
|
||||||
|
item = {"text": model_dump[text_key], "data": model_dump[data_key]}
|
||||||
|
elif isinstance(item, str):
|
||||||
|
item = {"text": item, "data": {}}
|
||||||
elif isinstance(item, dict):
|
elif isinstance(item, dict):
|
||||||
records.append(Record(text=item.get(text_key), data=item.get(data_key)))
|
if text_key not in item:
|
||||||
elif isinstance(item, Document):
|
raise ValueError(f"Key '{text_key}' not found in dictionary item.")
|
||||||
records.append(Record(text=item.page_content, data=item.metadata))
|
if data_key not in item:
|
||||||
|
raise ValueError(f"Key '{data_key}' not found in dictionary item.")
|
||||||
|
item = {"text": item[text_key], "data": item[data_key]}
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid data type: {type(item)}")
|
raise ValueError(f"Invalid data type: {type(item)}")
|
||||||
|
|
||||||
|
records.append(Record(**item))
|
||||||
|
|
||||||
return records
|
return records
|
||||||
|
|
||||||
def create_references_from_records(
|
def create_references_from_records(
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@ from typing import Any, Dict, List, Optional, Union
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
from langflow.field_typing.range_spec import RangeSpec
|
from langflow.field_typing.range_spec import RangeSpec
|
||||||
from langflow.interface.custom.attributes import ATTR_FUNC_MAPPING
|
from langflow.interface.custom.attributes import ATTR_FUNC_MAPPING
|
||||||
from langflow.interface.custom.code_parser.utils import extract_inner_type
|
from langflow.interface.custom.code_parser.utils import extract_inner_type
|
||||||
|
|
@ -25,6 +23,7 @@ from langflow.template.frontend_node.custom_components import (
|
||||||
)
|
)
|
||||||
from langflow.utils import validate
|
from langflow.utils import validate
|
||||||
from langflow.utils.util import get_base_classes
|
from langflow.utils.util import get_base_classes
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
|
||||||
def add_output_types(
|
def add_output_types(
|
||||||
|
|
@ -86,6 +85,8 @@ def add_base_classes(
|
||||||
)
|
)
|
||||||
|
|
||||||
base_classes = get_base_classes(return_type_instance)
|
base_classes = get_base_classes(return_type_instance)
|
||||||
|
if return_type_instance == str:
|
||||||
|
base_classes.append("Text")
|
||||||
|
|
||||||
for base_class in base_classes:
|
for base_class in base_classes:
|
||||||
frontend_node.add_base_class(base_class)
|
frontend_node.add_base_class(base_class)
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
import base64
|
import base64
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from io import BytesIO
|
|
||||||
import re
|
import re
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
from langchain.base_language import BaseLanguageModel
|
from langchain.base_language import BaseLanguageModel
|
||||||
from PIL.Image import Image
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
from PIL.Image import Image
|
||||||
|
|
||||||
from langflow.services.chat.config import ChatConfig
|
from langflow.services.chat.config import ChatConfig
|
||||||
from langflow.services.deps import get_settings_service
|
from langflow.services.deps import get_settings_service
|
||||||
|
|
||||||
|
|
@ -43,7 +43,9 @@ def try_setting_streaming_options(langchain_object):
|
||||||
llm = None
|
llm = None
|
||||||
if hasattr(langchain_object, "llm"):
|
if hasattr(langchain_object, "llm"):
|
||||||
llm = langchain_object.llm
|
llm = langchain_object.llm
|
||||||
elif hasattr(langchain_object, "llm_chain") and hasattr(langchain_object.llm_chain, "llm"):
|
elif hasattr(langchain_object, "llm_chain") and hasattr(
|
||||||
|
langchain_object.llm_chain, "llm"
|
||||||
|
):
|
||||||
llm = langchain_object.llm_chain.llm
|
llm = langchain_object.llm_chain.llm
|
||||||
|
|
||||||
if isinstance(llm, BaseLanguageModel):
|
if isinstance(llm, BaseLanguageModel):
|
||||||
|
|
@ -56,8 +58,37 @@ def try_setting_streaming_options(langchain_object):
|
||||||
|
|
||||||
|
|
||||||
def extract_input_variables_from_prompt(prompt: str) -> list[str]:
|
def extract_input_variables_from_prompt(prompt: str) -> list[str]:
|
||||||
"""Extract input variables from prompt."""
|
variables = []
|
||||||
return re.findall(r"{(.*?)}", prompt)
|
remaining_text = prompt
|
||||||
|
|
||||||
|
# Pattern to match single {var} and double {{var}} braces.
|
||||||
|
pattern = r"\{\{(.*?)\}\}|\{([^{}]+)\}"
|
||||||
|
|
||||||
|
while True:
|
||||||
|
match = re.search(pattern, remaining_text)
|
||||||
|
if not match:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Extract the variable name from either the single or double brace match
|
||||||
|
if match.group(1): # Match found in double braces
|
||||||
|
variable_name = (
|
||||||
|
"{{" + match.group(1) + "}}"
|
||||||
|
) # Re-add single braces for JSON strings
|
||||||
|
else: # Match found in single braces
|
||||||
|
variable_name = match.group(2)
|
||||||
|
if variable_name is not None:
|
||||||
|
# This means there is a match
|
||||||
|
# but there is nothing inside the braces
|
||||||
|
variables.append(variable_name)
|
||||||
|
|
||||||
|
# Remove the matched text from the remaining_text
|
||||||
|
start, end = match.span()
|
||||||
|
remaining_text = remaining_text[:start] + remaining_text[end:]
|
||||||
|
|
||||||
|
# Proceed to the next match until no more matches are found
|
||||||
|
# No need to compare remaining "{}" instances because we are re-adding braces for JSON compatibility
|
||||||
|
|
||||||
|
return variables
|
||||||
|
|
||||||
|
|
||||||
def setup_llm_caching():
|
def setup_llm_caching():
|
||||||
|
|
@ -73,11 +104,14 @@ def setup_llm_caching():
|
||||||
|
|
||||||
def set_langchain_cache(settings):
|
def set_langchain_cache(settings):
|
||||||
from langchain.globals import set_llm_cache
|
from langchain.globals import set_llm_cache
|
||||||
|
|
||||||
from langflow.interface.importing.utils import import_class
|
from langflow.interface.importing.utils import import_class
|
||||||
|
|
||||||
if cache_type := os.getenv("LANGFLOW_LANGCHAIN_CACHE"):
|
if cache_type := os.getenv("LANGFLOW_LANGCHAIN_CACHE"):
|
||||||
try:
|
try:
|
||||||
cache_class = import_class(f"langchain.cache.{cache_type or settings.LANGCHAIN_CACHE}")
|
cache_class = import_class(
|
||||||
|
f"langchain.cache.{cache_type or settings.LANGCHAIN_CACHE}"
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug(f"Setting up LLM caching with {cache_class.__name__}")
|
logger.debug(f"Setting up LLM caching with {cache_class.__name__}")
|
||||||
set_llm_cache(cache_class())
|
set_llm_cache(cache_class())
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
from langflow.schema import Record
|
from langflow.schema import Record
|
||||||
from langflow.services.deps import get_monitor_service
|
from langflow.services.deps import get_monitor_service
|
||||||
from langflow.services.monitor.schema import MessageModel
|
from langflow.services.monitor.schema import MessageModel
|
||||||
from loguru import logger
|
|
||||||
|
|
||||||
|
|
||||||
def get_messages(
|
def get_messages(
|
||||||
|
|
|
||||||
3
src/backend/langflow/schema/__init__.py
Normal file
3
src/backend/langflow/schema/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
from .schema import Record
|
||||||
|
|
||||||
|
__all__ = ["Record"]
|
||||||
|
|
@ -57,14 +57,3 @@ class Record(BaseModel):
|
||||||
return self.text
|
return self.text
|
||||||
|
|
||||||
|
|
||||||
def docs_to_records(documents: list[Document]) -> list[Record]:
|
|
||||||
"""
|
|
||||||
Converts a list of Documents to a list of Records.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
documents (list[Document]): The list of Documents to convert.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
list[Record]: The converted list of Records.
|
|
||||||
"""
|
|
||||||
return [Record.from_document(document) for document in documents]
|
|
||||||
|
|
@ -36,7 +36,10 @@ class DatabaseService(Service):
|
||||||
def _create_engine(self) -> "Engine":
|
def _create_engine(self) -> "Engine":
|
||||||
"""Create the engine for the database."""
|
"""Create the engine for the database."""
|
||||||
settings_service = get_settings_service()
|
settings_service = get_settings_service()
|
||||||
if settings_service.settings.DATABASE_URL and settings_service.settings.DATABASE_URL.startswith("sqlite"):
|
if (
|
||||||
|
settings_service.settings.DATABASE_URL
|
||||||
|
and settings_service.settings.DATABASE_URL.startswith("sqlite")
|
||||||
|
):
|
||||||
connect_args = {"check_same_thread": False}
|
connect_args = {"check_same_thread": False}
|
||||||
else:
|
else:
|
||||||
connect_args = {}
|
connect_args = {}
|
||||||
|
|
@ -48,7 +51,9 @@ class DatabaseService(Service):
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
if exc_type is not None: # If an exception has been raised
|
if exc_type is not None: # If an exception has been raised
|
||||||
logger.error(f"Session rollback because of exception: {exc_type.__name__} {exc_value}")
|
logger.error(
|
||||||
|
f"Session rollback because of exception: {exc_type.__name__} {exc_value}"
|
||||||
|
)
|
||||||
self._session.rollback()
|
self._session.rollback()
|
||||||
else:
|
else:
|
||||||
self._session.commit()
|
self._session.commit()
|
||||||
|
|
@ -65,7 +70,9 @@ class DatabaseService(Service):
|
||||||
settings_service = get_settings_service()
|
settings_service = get_settings_service()
|
||||||
if settings_service.auth_settings.AUTO_LOGIN:
|
if settings_service.auth_settings.AUTO_LOGIN:
|
||||||
with Session(self.engine) as session:
|
with Session(self.engine) as session:
|
||||||
flows = session.exec(select(models.Flow).where(models.Flow.user_id is None)).all()
|
flows = session.exec(
|
||||||
|
select(models.Flow).where(models.Flow.user_id is None)
|
||||||
|
).all()
|
||||||
if flows:
|
if flows:
|
||||||
logger.debug("Migrating flows to default superuser")
|
logger.debug("Migrating flows to default superuser")
|
||||||
username = settings_service.auth_settings.SUPERUSER
|
username = settings_service.auth_settings.SUPERUSER
|
||||||
|
|
@ -95,7 +102,9 @@ class DatabaseService(Service):
|
||||||
expected_columns = list(model.model_fields.keys())
|
expected_columns = list(model.model_fields.keys())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
available_columns = [col["name"] for col in inspector.get_columns(table)]
|
available_columns = [
|
||||||
|
col["name"] for col in inspector.get_columns(table)
|
||||||
|
]
|
||||||
except sa.exc.NoSuchTableError:
|
except sa.exc.NoSuchTableError:
|
||||||
logger.error(f"Missing table: {table}")
|
logger.error(f"Missing table: {table}")
|
||||||
return False
|
return False
|
||||||
|
|
@ -152,14 +161,16 @@ class DatabaseService(Service):
|
||||||
try:
|
try:
|
||||||
command.check(alembic_cfg)
|
command.check(alembic_cfg)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
if isinstance(exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)):
|
if isinstance(
|
||||||
|
exc, (util.exc.CommandError, util.exc.AutogenerateDiffsDetected)
|
||||||
|
):
|
||||||
command.upgrade(alembic_cfg, "head")
|
command.upgrade(alembic_cfg, "head")
|
||||||
time.sleep(3)
|
time.sleep(3)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
command.check(alembic_cfg)
|
command.check(alembic_cfg)
|
||||||
except util.exc.AutogenerateDiffsDetected as e:
|
except util.exc.AutogenerateDiffsDetected as e:
|
||||||
logger.error("AutogenerateDiffsDetected: {exc}")
|
logger.error(f"AutogenerateDiffsDetected: {exc}")
|
||||||
if not fix:
|
if not fix:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Something went wrong running migrations. Please, run `langflow migration --fix`"
|
"Something went wrong running migrations. Please, run `langflow migration --fix`"
|
||||||
|
|
@ -188,7 +199,10 @@ class DatabaseService(Service):
|
||||||
# We will check that all models are in the database
|
# We will check that all models are in the database
|
||||||
# and that the database is up to date with all columns
|
# and that the database is up to date with all columns
|
||||||
sql_models = [models.Flow, models.User, models.ApiKey]
|
sql_models = [models.Flow, models.User, models.ApiKey]
|
||||||
return [TableResults(sql_model.__tablename__, self.check_table(sql_model)) for sql_model in sql_models]
|
return [
|
||||||
|
TableResults(sql_model.__tablename__, self.check_table(sql_model))
|
||||||
|
for sql_model in sql_models
|
||||||
|
]
|
||||||
|
|
||||||
def check_table(self, model):
|
def check_table(self, model):
|
||||||
results = []
|
results = []
|
||||||
|
|
@ -197,7 +211,9 @@ class DatabaseService(Service):
|
||||||
expected_columns = list(model.__fields__.keys())
|
expected_columns = list(model.__fields__.keys())
|
||||||
available_columns = []
|
available_columns = []
|
||||||
try:
|
try:
|
||||||
available_columns = [col["name"] for col in inspector.get_columns(table_name)]
|
available_columns = [
|
||||||
|
col["name"] for col in inspector.get_columns(table_name)
|
||||||
|
]
|
||||||
results.append(Result(name=table_name, type="table", success=True))
|
results.append(Result(name=table_name, type="table", success=True))
|
||||||
except sa.exc.NoSuchTableError:
|
except sa.exc.NoSuchTableError:
|
||||||
logger.error(f"Missing table: {table_name}")
|
logger.error(f"Missing table: {table_name}")
|
||||||
|
|
@ -228,7 +244,9 @@ class DatabaseService(Service):
|
||||||
try:
|
try:
|
||||||
table.create(self.engine, checkfirst=True)
|
table.create(self.engine, checkfirst=True)
|
||||||
except OperationalError as oe:
|
except OperationalError as oe:
|
||||||
logger.warning(f"Table {table} already exists, skipping. Exception: {oe}")
|
logger.warning(
|
||||||
|
f"Table {table} already exists, skipping. Exception: {oe}"
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.error(f"Error creating table {table}: {exc}")
|
logger.error(f"Error creating table {table}: {exc}")
|
||||||
raise RuntimeError(f"Error creating table {table}") from exc
|
raise RuntimeError(f"Error creating table {table}") from exc
|
||||||
|
|
@ -240,7 +258,9 @@ class DatabaseService(Service):
|
||||||
if table not in table_names:
|
if table not in table_names:
|
||||||
logger.error("Something went wrong creating the database and tables.")
|
logger.error("Something went wrong creating the database and tables.")
|
||||||
logger.error("Please check your database settings.")
|
logger.error("Please check your database settings.")
|
||||||
raise RuntimeError("Something went wrong creating the database and tables.")
|
raise RuntimeError(
|
||||||
|
"Something went wrong creating the database and tables."
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Database and tables created successfully")
|
logger.debug("Database and tables created successfully")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,9 @@ if TYPE_CHECKING:
|
||||||
|
|
||||||
class TransactionModel(BaseModel):
|
class TransactionModel(BaseModel):
|
||||||
id: Optional[int] = Field(default=None, alias="id")
|
id: Optional[int] = Field(default=None, alias="id")
|
||||||
timestamp: Optional[datetime] = Field(default_factory=datetime.now, alias="timestamp")
|
timestamp: Optional[datetime] = Field(
|
||||||
|
default_factory=datetime.now, alias="timestamp"
|
||||||
|
)
|
||||||
source: str
|
source: str
|
||||||
target: str
|
target: str
|
||||||
target_args: dict
|
target_args: dict
|
||||||
|
|
@ -51,8 +53,12 @@ class MessageModel(BaseModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_record(cls, record: "Record"):
|
def from_record(cls, record: "Record"):
|
||||||
# first check if the record has all the required fields
|
# first check if the record has all the required fields
|
||||||
if not record.data or ("sender" not in record.data and "sender_name" not in record.data):
|
if not record.data or (
|
||||||
raise ValueError("The record does not have the required fields 'sender' and 'sender_name' in the data.")
|
"sender" not in record.data and "sender_name" not in record.data
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
"The record does not have the required fields 'sender' and 'sender_name' in the data."
|
||||||
|
)
|
||||||
return cls(
|
return cls(
|
||||||
sender=record.data["sender"],
|
sender=record.data["sender"],
|
||||||
sender_name=record.data["sender_name"],
|
sender_name=record.data["sender_name"],
|
||||||
|
|
|
||||||
4
src/frontend/.gitignore
vendored
4
src/frontend/.gitignore
vendored
|
|
@ -24,3 +24,7 @@ yarn-error.log*
|
||||||
/test-results/
|
/test-results/
|
||||||
/playwright-report/*/
|
/playwright-report/*/
|
||||||
/playwright/.cache/
|
/playwright/.cache/
|
||||||
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
27
src/frontend/package-lock.json
generated
27
src/frontend/package-lock.json
generated
|
|
@ -48,6 +48,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.331.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
|
|
@ -77,7 +78,7 @@
|
||||||
"zustand": "^4.4.7"
|
"zustand": "^4.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.41.2",
|
"@playwright/test": "^1.42.0",
|
||||||
"@swc/cli": "^0.1.62",
|
"@swc/cli": "^0.1.62",
|
||||||
"@swc/core": "^1.3.80",
|
"@swc/core": "^1.3.80",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
|
@ -1492,12 +1493,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
"version": "1.41.2",
|
"version": "1.42.0",
|
||||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz",
|
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.0.tgz",
|
||||||
"integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==",
|
"integrity": "sha512-2k1HzC28Fs+HiwbJOQDUwrWMttqSLUVdjCqitBOjdCD0svWOMQUVqrXX6iFD7POps6xXAojsX/dGBpKnjZctLA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright": "1.41.2"
|
"playwright": "1.42.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
@ -8719,12 +8720,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.41.2",
|
"version": "1.42.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.0.tgz",
|
||||||
"integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==",
|
"integrity": "sha512-Ko7YRUgj5xBHbntrgt4EIw/nE//XBHOKVKnBjO1KuZkmkhlbgyggTe5s9hjqQ1LpN+Xg+kHsQyt5Pa0Bw5XpvQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"playwright-core": "1.41.2"
|
"playwright-core": "1.42.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright": "cli.js"
|
"playwright": "cli.js"
|
||||||
|
|
@ -8737,10 +8737,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/playwright-core": {
|
"node_modules/playwright-core": {
|
||||||
"version": "1.41.2",
|
"version": "1.42.0",
|
||||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.0.tgz",
|
||||||
"integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==",
|
"integrity": "sha512-0HD9y8qEVlcbsAjdpBaFjmaTHf+1FeIddy8VJLeiqwhcNqGCBe4Wp2e8knpqiYbzxtxarxiXyNDw2cG8sCaNMQ==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"playwright-core": "cli.js"
|
"playwright-core": "cli.js"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lucide-react": "^0.331.0",
|
"lucide-react": "^0.331.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
|
|
@ -99,7 +100,7 @@
|
||||||
},
|
},
|
||||||
"proxy": "http://127.0.0.1:7860",
|
"proxy": "http://127.0.0.1:7860",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.41.2",
|
"@playwright/test": "^1.42.0",
|
||||||
"@swc/cli": "^0.1.62",
|
"@swc/cli": "^0.1.62",
|
||||||
"@swc/core": "^1.3.80",
|
"@swc/core": "^1.3.80",
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.9",
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,6 @@
|
||||||
<li>
|
<li>
|
||||||
<a href="./e2e/index.html">e2e report</a>
|
<a href="./e2e/index.html">e2e report</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
<a href="./onlyFront/index.html">frontEnd Only report</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,16 @@ terminate_process_by_port() {
|
||||||
echo "Process terminated."
|
echo "Process terminated."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
delete_temp() {
|
||||||
|
cd ../../
|
||||||
|
echo "Deleting temp database"
|
||||||
|
rm temp
|
||||||
|
echo "Temp database deleted."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# Trap signals to ensure cleanup on script termination
|
# Trap signals to ensure cleanup on script termination
|
||||||
trap 'terminate_process_by_port 7860; terminate_process_by_port 3000' EXIT
|
trap 'terminate_process_by_port 7860; terminate_process_by_port 3000; delete_temp' EXIT
|
||||||
|
|
||||||
# install playwright if there is not installed yet
|
# install playwright if there is not installed yet
|
||||||
npx playwright install
|
npx playwright install
|
||||||
|
|
@ -42,26 +50,16 @@ make frontend &
|
||||||
# Give some time for the frontend to start (adjust sleep duration as needed)
|
# Give some time for the frontend to start (adjust sleep duration as needed)
|
||||||
sleep 10
|
sleep 10
|
||||||
|
|
||||||
# Navigate to the test directory
|
#install backend
|
||||||
cd src/frontend
|
poetry install --extras deploy
|
||||||
|
|
||||||
# Run frontend only Playwright tests with or without UI based on the --ui flag
|
|
||||||
if [ "$ui" = true ]; then
|
|
||||||
PLAYWRIGHT_HTML_REPORT=playwright-report/onlyFront npx playwright test tests/onlyFront --ui --project=chromium
|
|
||||||
else
|
|
||||||
PLAYWRIGHT_HTML_REPORT=playwright-report/onlyFront npx playwright test tests/onlyFront --project=chromium
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Navigate back to the project root directory
|
|
||||||
cd ../../
|
|
||||||
|
|
||||||
# Start the backend using 'make backend' in the background
|
# Start the backend using 'make backend' in the background
|
||||||
make backend &
|
LANGFLOW_DATABASE_URL=sqlite:///./temp LANGFLOW_AUTO_LOGIN=True poetry run langflow run --backend-only --port 7860 --host 0.0.0.0 --no-open-browser &
|
||||||
|
|
||||||
# Give some time for the backend to start (adjust sleep duration as needed)
|
# Give some time for the backend to start (adjust sleep duration as needed)
|
||||||
sleep 25
|
sleep 25
|
||||||
|
|
||||||
# Navigate back to the test directory
|
# Navigate to the test directory
|
||||||
cd src/frontend
|
cd src/frontend
|
||||||
|
|
||||||
# Run Playwright tests with or without UI based on the --ui flag
|
# Run Playwright tests with or without UI based on the --ui flag
|
||||||
|
|
|
||||||
|
|
@ -212,7 +212,7 @@ export default function ParameterComponent({
|
||||||
{item.display_name === "" ? "" : " - "}
|
{item.display_name === "" ? "" : " - "}
|
||||||
{item.display_name.split(", ").length > 2
|
{item.display_name.split(", ").length > 2
|
||||||
? item.display_name.split(", ").map((el, index) => (
|
? item.display_name.split(", ").map((el, index) => (
|
||||||
<React.Fragment key={el + index}>
|
<React.Fragment key={el + name}>
|
||||||
<span>
|
<span>
|
||||||
{index ===
|
{index ===
|
||||||
item.display_name.split(", ").length - 1
|
item.display_name.split(", ").length - 1
|
||||||
|
|
@ -229,7 +229,7 @@ export default function ParameterComponent({
|
||||||
{item.type === "" ? "" : " - "}
|
{item.type === "" ? "" : " - "}
|
||||||
{item.type.split(", ").length > 2
|
{item.type.split(", ").length > 2
|
||||||
? item.type.split(", ").map((el, index) => (
|
? item.type.split(", ").map((el, index) => (
|
||||||
<React.Fragment key={el + index}>
|
<React.Fragment key={el + name}>
|
||||||
<span>
|
<span>
|
||||||
{index === item.type.split(", ").length - 1
|
{index === item.type.split(", ").length - 1
|
||||||
? el
|
? el
|
||||||
|
|
@ -409,7 +409,7 @@ export default function ParameterComponent({
|
||||||
<div className="mt-2 flex w-full items-center">
|
<div className="mt-2 flex w-full items-center">
|
||||||
<div className="w-5/6 flex-grow">
|
<div className="w-5/6 flex-grow">
|
||||||
<InputComponent
|
<InputComponent
|
||||||
id={"input-" + index}
|
id={"input-" + name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
password={data.node?.template[name].password ?? false}
|
password={data.node?.template[name].password ?? false}
|
||||||
value={data.node?.template[name].value ?? ""}
|
value={data.node?.template[name].value ?? ""}
|
||||||
|
|
@ -432,11 +432,12 @@ export default function ParameterComponent({
|
||||||
) : left === true && type === "bool" ? (
|
) : left === true && type === "bool" ? (
|
||||||
<div className="mt-2 w-full">
|
<div className="mt-2 w-full">
|
||||||
<ToggleShadComponent
|
<ToggleShadComponent
|
||||||
id={"toggle-" + index}
|
id={"toggle-" + name}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
enabled={data.node?.template[name].value ?? false}
|
enabled={data.node?.template[name].value ?? false}
|
||||||
setEnabled={handleOnNewValue}
|
setEnabled={handleOnNewValue}
|
||||||
size="large"
|
size="large"
|
||||||
|
editNode={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true && type === "float" ? (
|
) : left === true && type === "float" ? (
|
||||||
|
|
@ -458,7 +459,7 @@ export default function ParameterComponent({
|
||||||
options={data.node.template[name].options}
|
options={data.node.template[name].options}
|
||||||
onSelect={handleOnNewValue}
|
onSelect={handleOnNewValue}
|
||||||
value={data.node.template[name].value ?? "Choose an option"}
|
value={data.node.template[name].value ?? "Choose an option"}
|
||||||
id={"dropdown-" + index}
|
id={"dropdown-" + name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{data.node?.template[name].refresh && (
|
{data.node?.template[name].refresh && (
|
||||||
|
|
@ -486,7 +487,7 @@ export default function ParameterComponent({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={data.node?.template[name].value ?? ""}
|
value={data.node?.template[name].value ?? ""}
|
||||||
onChange={handleOnNewValue}
|
onChange={handleOnNewValue}
|
||||||
id={"code-input-" + index}
|
id={"code-input-" + name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true && type === "file" ? (
|
) : left === true && type === "file" ? (
|
||||||
|
|
@ -507,7 +508,7 @@ export default function ParameterComponent({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={data.node?.template[name].value ?? ""}
|
value={data.node?.template[name].value ?? ""}
|
||||||
onChange={handleOnNewValue}
|
onChange={handleOnNewValue}
|
||||||
id={"int-input-" + index}
|
id={"int-input-" + name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true && type === "prompt" ? (
|
) : left === true && type === "prompt" ? (
|
||||||
|
|
@ -520,8 +521,8 @@ export default function ParameterComponent({
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
value={data.node?.template[name].value ?? ""}
|
value={data.node?.template[name].value ?? ""}
|
||||||
onChange={handleOnNewValue}
|
onChange={handleOnNewValue}
|
||||||
id={"prompt-input-" + index}
|
id={"prompt-input-" + name}
|
||||||
data-testid={"prompt-input-" + index}
|
data-testid={"prompt-input-" + name}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : left === true && type === "NestedDict" ? (
|
) : left === true && type === "NestedDict" ? (
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,10 @@ export default function AccordionComponent({
|
||||||
>
|
>
|
||||||
{trigger}
|
{trigger}
|
||||||
</AccordionTrigger>
|
</AccordionTrigger>
|
||||||
<AccordionContent className="AccordionContent flex flex-col">
|
<AccordionContent>
|
||||||
|
<div className="AccordionContent flex flex-col">
|
||||||
{children}
|
{children}
|
||||||
|
</div>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export default function IOOutputView({
|
||||||
case "TextOutput":
|
case "TextOutput":
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full custom-scroll"
|
className="w-full h-full custom-scroll"
|
||||||
placeholder={"Empty"}
|
placeholder={"Empty"}
|
||||||
// update to real value on flowPool
|
// update to real value on flowPool
|
||||||
value={
|
value={
|
||||||
|
|
@ -31,7 +31,7 @@ export default function IOOutputView({
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Textarea
|
||||||
className="w-full custom-scroll"
|
className="w-full h-full custom-scroll"
|
||||||
placeholder={"Enter text..."}
|
placeholder={"Enter text..."}
|
||||||
value={node.data.node!.template["input_value"]}
|
value={node.data.node!.template["input_value"]}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { cn } from "../../utils/utils";
|
||||||
import AccordionComponent from "../AccordionComponent";
|
import AccordionComponent from "../AccordionComponent";
|
||||||
import IOInputField from "../IOInputField";
|
import IOInputField from "../IOInputField";
|
||||||
import IOOutputView from "../IOOutputView";
|
import IOOutputView from "../IOOutputView";
|
||||||
|
import ShadTooltip from "../ShadTooltipComponent";
|
||||||
import IconComponent from "../genericIconComponent";
|
import IconComponent from "../genericIconComponent";
|
||||||
import NewChatView from "../newChatView";
|
import NewChatView from "../newChatView";
|
||||||
import { Badge } from "../ui/badge";
|
import { Badge } from "../ui/badge";
|
||||||
|
|
@ -213,9 +214,16 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
<AccordionComponent
|
<AccordionComponent
|
||||||
trigger={
|
trigger={
|
||||||
<div className="file-component-badge-div">
|
<div className="file-component-badge-div">
|
||||||
|
<ShadTooltip
|
||||||
|
content={output.id}
|
||||||
|
styleClasses="z-50"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
<Badge variant="gray" size="md">
|
<Badge variant="gray" size="md">
|
||||||
{output.id}
|
{output.displayName}
|
||||||
</Badge>
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</ShadTooltip>
|
||||||
{haveChat && (
|
{haveChat && (
|
||||||
<div
|
<div
|
||||||
className="-mb-1 pr-4"
|
className="-mb-1 pr-4"
|
||||||
|
|
@ -272,7 +280,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
</button>
|
</button>
|
||||||
{selectedViewField.type}
|
{selectedViewField.type}
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full">
|
<div className="h-full w-full">
|
||||||
{inputs.some(
|
{inputs.some(
|
||||||
(input) => input.id === selectedViewField.id
|
(input) => input.id === selectedViewField.id
|
||||||
) ? (
|
) ? (
|
||||||
|
|
@ -308,6 +316,9 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
<div className="absolute bottom-8 right-8"></div>
|
<div className="absolute bottom-8 right-8"></div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</BaseModal.Content>
|
||||||
|
<BaseModal.Footer>
|
||||||
{!haveChat && (
|
{!haveChat && (
|
||||||
<div className="flex w-full justify-end pt-6">
|
<div className="flex w-full justify-end pt-6">
|
||||||
<Button
|
<Button
|
||||||
|
|
@ -328,8 +339,7 @@ export default function IOView({ children, open, setOpen }): JSX.Element {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</BaseModal.Footer>
|
||||||
</BaseModal.Content>
|
|
||||||
</BaseModal>
|
</BaseModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,7 @@ export default function CodeTabsComponent({
|
||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
className={
|
className={
|
||||||
"api-modal-tabs " +
|
"api-modal-tabs m-0 inset-0 " +
|
||||||
(isMessage ? "dark " : "") +
|
(isMessage ? "dark " : "") +
|
||||||
(dark && isMessage ? "bg-background" : "")
|
(dark && isMessage ? "bg-background" : "")
|
||||||
}
|
}
|
||||||
|
|
@ -166,7 +166,7 @@ export default function CodeTabsComponent({
|
||||||
) : (
|
) : (
|
||||||
<IconComponent name="Clipboard" className="h-4 w-4" />
|
<IconComponent name="Clipboard" className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
{isCopied ? "Copied!" : "Copy code"}
|
{isCopied ? "Copied!" : "Copy Code"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
className="flex items-center gap-1.5 rounded bg-none p-1 text-xs text-gray-500 dark:text-gray-300"
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ export default function InputComponent({
|
||||||
handleKeyDown(e, value, "");
|
handleKeyDown(e, value, "");
|
||||||
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
|
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
|
||||||
}}
|
}}
|
||||||
|
data-testid={editNode ? id + "-edit" : id}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{password && (
|
{password && (
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ export default function IntComponent({
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
onChange(event.target.value);
|
onChange(event.target.value);
|
||||||
}}
|
}}
|
||||||
|
data-testid={id}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
||||||
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
||||||
{isCopied ? "Copied!" : "Copy code"}
|
{isCopied ? "Copied!" : "Copy Code"}
|
||||||
</button>
|
</button>
|
||||||
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
||||||
<IconDownload size={18} />
|
<IconDownload size={18} />
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export default function ToggleShadComponent({
|
||||||
disabled,
|
disabled,
|
||||||
size,
|
size,
|
||||||
id = "",
|
id = "",
|
||||||
|
editNode = false,
|
||||||
}: ToggleComponentType): JSX.Element {
|
}: ToggleComponentType): JSX.Element {
|
||||||
let scaleX, scaleY;
|
let scaleX, scaleY;
|
||||||
switch (size) {
|
switch (size) {
|
||||||
|
|
@ -31,6 +32,7 @@ export default function ToggleShadComponent({
|
||||||
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
|
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
|
||||||
<Switch
|
<Switch
|
||||||
id={id}
|
id={id}
|
||||||
|
data-testid={id}
|
||||||
style={{
|
style={{
|
||||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,18 @@ export const INVALID_CHARACTERS = [
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regex to highlight the variables in the text
|
* regex to highlight the variables in the text
|
||||||
* @constant
|
* @constant regexHighlight
|
||||||
|
* @type {RegExp}
|
||||||
|
* @default
|
||||||
|
* @example
|
||||||
|
* {{variable}} or {variable}
|
||||||
|
* @returns {RegExp}
|
||||||
|
* @description
|
||||||
|
* This regex is used to highlight the variables in the text.
|
||||||
|
* It matches the variables in the text that are between {{}} or {}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export const regexHighlight = /\{([^}]+)\}/g;
|
export const regexHighlight = /\{\{(.*?)\}\}|\{([^{}]+)\}/g;
|
||||||
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
|
export const specialCharsRegex = /[!@#$%^&*()\-_=+[\]{}|;:'",.<>/?\\`´]/;
|
||||||
|
|
||||||
export const programmingLanguages: languageMap = {
|
export const programmingLanguages: languageMap = {
|
||||||
|
|
@ -708,8 +716,8 @@ export const chatInputPlaceholder =
|
||||||
export const chatInputPlaceholderSend = "Send a message...";
|
export const chatInputPlaceholderSend = "Send a message...";
|
||||||
export const editCodeTitle = "Edit Code";
|
export const editCodeTitle = "Edit Code";
|
||||||
export const myCollectionDesc =
|
export const myCollectionDesc =
|
||||||
"Manage your personal projects. Download or upload your collection.";
|
"Manage your personal projects. Download and upload entire collections.";
|
||||||
export const storeDesc = "Search flows and components from the community.";
|
export const storeDesc = "Explore community-shared flows and components.";
|
||||||
export const storeTitle = "Langflow Store";
|
export const storeTitle = "Langflow Store";
|
||||||
export const noApi = "You don't have an API key. ";
|
export const noApi = "You don't have an API key. ";
|
||||||
export const insertApi = "Insert your Langflow API key.";
|
export const insertApi = "Insert your Langflow API key.";
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,7 @@ function ConfirmationModal({
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
onConfirm(index, data);
|
onConfirm(index, data);
|
||||||
}}
|
}}
|
||||||
|
data-testid="replace-button"
|
||||||
>
|
>
|
||||||
{confirmationText}
|
{confirmationText}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -35,9 +35,9 @@ export default function DeleteConfirmationModal({
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<span>
|
<span>
|
||||||
Are you sure you want to delete this {description ?? "component"}?
|
Confirm deletion of {description ?? "component"}?
|
||||||
<br></br>
|
<br></br>
|
||||||
This action cannot be undone.
|
Note: This action is irreversible.
|
||||||
</span>
|
</span>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<DialogClose>
|
<DialogClose>
|
||||||
|
|
|
||||||
|
|
@ -242,7 +242,11 @@ const EditNodeModal = forwardRef(
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<InputComponent
|
<InputComponent
|
||||||
id={"input-" + index}
|
id={
|
||||||
|
"input-" +
|
||||||
|
myData.node.template[templateParam]
|
||||||
|
.name
|
||||||
|
}
|
||||||
editNode={true}
|
editNode={true}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
password={
|
password={
|
||||||
|
|
@ -338,7 +342,10 @@ const EditNodeModal = forwardRef(
|
||||||
<div className="ml-auto">
|
<div className="ml-auto">
|
||||||
{" "}
|
{" "}
|
||||||
<ToggleShadComponent
|
<ToggleShadComponent
|
||||||
id={"toggle-edit-" + index}
|
id={
|
||||||
|
"toggle-edit-" +
|
||||||
|
myData.node.template[templateParam].name
|
||||||
|
}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
enabled={
|
enabled={
|
||||||
myData.node.template[templateParam]
|
myData.node.template[templateParam]
|
||||||
|
|
@ -351,6 +358,7 @@ const EditNodeModal = forwardRef(
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
size="small"
|
size="small"
|
||||||
|
editNode={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : myData.node?.template[templateParam]
|
) : myData.node?.template[templateParam]
|
||||||
|
|
@ -391,14 +399,20 @@ const EditNodeModal = forwardRef(
|
||||||
myData.node.template[templateParam]
|
myData.node.template[templateParam]
|
||||||
.value ?? "Choose an option"
|
.value ?? "Choose an option"
|
||||||
}
|
}
|
||||||
id={"dropdown-edit-" + index}
|
id={
|
||||||
|
"dropdown-edit-" +
|
||||||
|
myData.node.template[templateParam].name
|
||||||
|
}
|
||||||
></Dropdown>
|
></Dropdown>
|
||||||
</div>
|
</div>
|
||||||
) : myData.node?.template[templateParam]
|
) : myData.node?.template[templateParam]
|
||||||
.type === "int" ? (
|
.type === "int" ? (
|
||||||
<div className="mx-auto">
|
<div className="mx-auto">
|
||||||
<IntComponent
|
<IntComponent
|
||||||
id={"edit-int-input-" + index}
|
id={
|
||||||
|
"edit-int-input-" +
|
||||||
|
myData.node.template[templateParam].name
|
||||||
|
}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
editNode={true}
|
editNode={true}
|
||||||
value={
|
value={
|
||||||
|
|
@ -493,7 +507,10 @@ const EditNodeModal = forwardRef(
|
||||||
onChange={(value: string | string[]) => {
|
onChange={(value: string | string[]) => {
|
||||||
handleOnNewValue(value, templateParam);
|
handleOnNewValue(value, templateParam);
|
||||||
}}
|
}}
|
||||||
id={"code-area-edit" + index}
|
id={
|
||||||
|
"code-area-edit" +
|
||||||
|
myData.node.template[templateParam].name
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : myData.node?.template[templateParam]
|
) : myData.node?.template[templateParam]
|
||||||
|
|
@ -519,6 +536,7 @@ const EditNodeModal = forwardRef(
|
||||||
}}
|
}}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
size="small"
|
size="small"
|
||||||
|
editNode={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ export function CodeBlock({ language, value }: Props): JSX.Element {
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
<button className="code-block-modal-button" onClick={copyToClipboard}>
|
||||||
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />}
|
||||||
{isCopied ? "Copied!" : "Copy code"}
|
{isCopied ? "Copied!" : "Copy Code"}
|
||||||
</button>
|
</button>
|
||||||
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
<button className="code-block-modal-button" onClick={downloadAsFile}>
|
||||||
<IconDownload size={18} />
|
<IconDownload size={18} />
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,23 @@ export default function GenericModal({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setInputValue(value);
|
setInputValue(value);
|
||||||
}, [value, modalOpen]);
|
}, [value, modalOpen]);
|
||||||
|
|
||||||
const coloredContent = (inputValue || "")
|
const coloredContent = (inputValue || "")
|
||||||
.replace(/</g, "<")
|
.replace(/</g, "<")
|
||||||
.replace(/>/g, ">")
|
.replace(/>/g, ">")
|
||||||
.replace(regexHighlight, varHighlightHTML({ name: "$1" }))
|
.replace(regexHighlight, (match, p1, p2) => {
|
||||||
.replace(/\n/g, "<br />");
|
// Decide which group was matched. If p1 is not undefined, do nothing
|
||||||
|
// we don't want to change the text. If p2 is not undefined, then we
|
||||||
|
// have a variable, so we should highlight it.
|
||||||
|
// ! This will not work with multiline or indented json yet
|
||||||
|
if (p1 !== undefined) {
|
||||||
|
return match;
|
||||||
|
} else if (p2 !== undefined) {
|
||||||
|
return varHighlightHTML({ name: p2 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
})
|
||||||
|
.replace(/\n/g, "<br />");
|
||||||
function getClassByNumberLength(): string {
|
function getClassByNumberLength(): string {
|
||||||
let sumOfCaracteres: number = 0;
|
let sumOfCaracteres: number = 0;
|
||||||
wordsHighlight.forEach((element) => {
|
wordsHighlight.forEach((element) => {
|
||||||
|
|
@ -159,7 +169,7 @@ export default function GenericModal({
|
||||||
setIsEdit(true);
|
setIsEdit(true);
|
||||||
return setErrorData({
|
return setErrorData({
|
||||||
title: PROMPT_ERROR_ALERT,
|
title: PROMPT_ERROR_ALERT,
|
||||||
list: [error.toString()],
|
list: [error.response.data.detail ?? ""],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ export default function ShareModal({
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
const [internalOpen, internalSetOpen] = useState(children ? false : true);
|
const [internalOpen, internalSetOpen] = useState(children ? false : true);
|
||||||
const [openConfirmationModal, setOpenConfirmationModal] = useState(false);
|
const [openConfirmationModal, setOpenConfirmationModal] = useState(false);
|
||||||
const nameComponent = is_component ? "component" : "flow";
|
const nameComponent = is_component ? "component" : "workflow";
|
||||||
|
|
||||||
const [tags, setTags] = useState<{ id: string; name: string }[]>([]);
|
const [tags, setTags] = useState<{ id: string; name: string }[]>([]);
|
||||||
const [loadingTags, setLoadingTags] = useState<boolean>(false);
|
const [loadingTags, setLoadingTags] = useState<boolean>(false);
|
||||||
|
|
@ -179,7 +179,7 @@ export default function ShareModal({
|
||||||
</span>
|
</span>
|
||||||
<br></br>
|
<br></br>
|
||||||
<span className=" text-xs text-destructive ">
|
<span className=" text-xs text-destructive ">
|
||||||
Warning: This action cannot be undone.
|
Note: This action is irreversible.
|
||||||
</span>
|
</span>
|
||||||
</ConfirmationModal.Content>
|
</ConfirmationModal.Content>
|
||||||
</ConfirmationModal>
|
</ConfirmationModal>
|
||||||
|
|
@ -204,7 +204,7 @@ export default function ShareModal({
|
||||||
{children ? children : <></>}
|
{children ? children : <></>}
|
||||||
</BaseModal.Trigger>
|
</BaseModal.Trigger>
|
||||||
<BaseModal.Header
|
<BaseModal.Header
|
||||||
description={`Share your ${nameComponent} to the Langflow Store.`}
|
description={`Publish ${is_component ? "your component" : "workflow"} to the Langflow Store.`}
|
||||||
>
|
>
|
||||||
<span className="pr-2">Share</span>
|
<span className="pr-2">Share</span>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
|
|
@ -235,12 +235,11 @@ export default function ShareModal({
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<label htmlFor="public" className="export-modal-save-api text-sm ">
|
<label htmlFor="public" className="export-modal-save-api text-sm ">
|
||||||
Make {nameComponent} public
|
Set {nameComponent} status to public
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<span className=" text-xs text-destructive ">
|
<span className=" text-xs text-destructive ">
|
||||||
<b>Warning:</b> API keys in designated fields are removed when
|
<b>Attention:</b> API keys in specified fields are automatically removed upon sharing.
|
||||||
sharing.
|
|
||||||
</span>
|
</span>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -386,7 +386,7 @@ export default function NodeToolbarComponent({
|
||||||
value={"Share"}
|
value={"Share"}
|
||||||
disabled={!hasApiKey || !validApiKey}
|
disabled={!hasApiKey || !validApiKey}
|
||||||
>
|
>
|
||||||
<div className="flex" data-testid="save-button-modal">
|
<div className="flex" data-testid="share-button-modal">
|
||||||
<IconComponent
|
<IconComponent
|
||||||
name="Share3"
|
name="Share3"
|
||||||
className="relative top-0.5 -m-1 mr-1 h-6 w-6"
|
className="relative top-0.5 -m-1 mr-1 h-6 w-6"
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,9 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
selection.nodes.some((node) => node.data.type === "ChatInput") &&
|
selection.nodes.some((node) => node.data.type === "ChatInput") &&
|
||||||
checkChatInput(get().nodes)
|
checkChatInput(get().nodes)
|
||||||
) {
|
) {
|
||||||
useAlertStore.getState().setErrorData({
|
useAlertStore
|
||||||
|
.getState()
|
||||||
|
.setErrorData({
|
||||||
title: "Error pasting components",
|
title: "Error pasting components",
|
||||||
list: ["You can only have one ChatInput component in the flow"],
|
list: ["You can only have one ChatInput component in the flow"],
|
||||||
});
|
});
|
||||||
|
|
@ -500,7 +502,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
||||||
updateVerticesBuild: (
|
updateVerticesBuild: (
|
||||||
vertices: {
|
vertices: {
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
verticesOrder: string[][];
|
|
||||||
verticesLayers: string[][];
|
verticesLayers: string[][];
|
||||||
runId: string;
|
runId: string;
|
||||||
} | null
|
} | null
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ export type ToggleComponentType = {
|
||||||
disabled: boolean | undefined;
|
disabled: boolean | undefined;
|
||||||
size: "small" | "medium" | "large";
|
size: "small" | "medium" | "large";
|
||||||
id?: string;
|
id?: string;
|
||||||
|
editNode?: boolean;
|
||||||
};
|
};
|
||||||
export type DropDownComponentType = {
|
export type DropDownComponentType = {
|
||||||
value: string;
|
value: string;
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@ export type FlowPoolType = {
|
||||||
|
|
||||||
export type FlowStoreType = {
|
export type FlowStoreType = {
|
||||||
flowPool: FlowPoolType;
|
flowPool: FlowPoolType;
|
||||||
inputs: Array<{ type: string; id: string }>;
|
inputs: Array<{ type: string; id: string; displayName: string }>;
|
||||||
outputs: Array<{ type: string; id: string }>;
|
outputs: Array<{ type: string; id: string; displayName: string }>;
|
||||||
hasIO: boolean;
|
hasIO: boolean;
|
||||||
setFlowPool: (flowPool: FlowPoolType) => void;
|
setFlowPool: (flowPool: FlowPoolType) => void;
|
||||||
addDataToFlowPool: (data: FlowPoolObjectType, nodeId: string) => void;
|
addDataToFlowPool: (data: FlowPoolObjectType, nodeId: string) => void;
|
||||||
|
|
@ -101,7 +101,6 @@ export type FlowStoreType = {
|
||||||
vertices: {
|
vertices: {
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
verticesLayers: string[][];
|
verticesLayers: string[][];
|
||||||
verticesOrder: string[][];
|
|
||||||
runId: string;
|
runId: string;
|
||||||
} | null
|
} | null
|
||||||
) => void;
|
) => void;
|
||||||
|
|
@ -110,7 +109,6 @@ export type FlowStoreType = {
|
||||||
verticesBuild: {
|
verticesBuild: {
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
verticesLayers: string[][];
|
verticesLayers: string[][];
|
||||||
verticesOrder: string[][];
|
|
||||||
runId: string;
|
runId: string;
|
||||||
} | null;
|
} | null;
|
||||||
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
|
updateBuildStatus: (nodeId: string[], status: BuildStatus) => void;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ export async function updateVerticesOrder(
|
||||||
): Promise<{
|
): Promise<{
|
||||||
verticesLayers: string[][];
|
verticesLayers: string[][];
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
verticesOrder: string[][];
|
|
||||||
runId: string;
|
runId: string;
|
||||||
}> {
|
}> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
|
@ -64,9 +63,8 @@ export async function updateVerticesOrder(
|
||||||
throw new Error("Invalid nodes");
|
throw new Error("Invalid nodes");
|
||||||
}
|
}
|
||||||
let verticesOrder: Array<Array<string>> = orderResponse.data.ids;
|
let verticesOrder: Array<Array<string>> = orderResponse.data.ids;
|
||||||
const runId = orderResponse.data.run_id;
|
|
||||||
let verticesLayers: Array<Array<string>> = [];
|
let verticesLayers: Array<Array<string>> = [];
|
||||||
|
const runId = orderResponse.data.run_id;
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
for (let i = 0; i < verticesOrder.length; i += 1) {
|
for (let i = 0; i < verticesOrder.length; i += 1) {
|
||||||
const innerArray = verticesOrder[i];
|
const innerArray = verticesOrder[i];
|
||||||
|
|
@ -84,15 +82,13 @@ export async function updateVerticesOrder(
|
||||||
} else {
|
} else {
|
||||||
verticesLayers = verticesOrder;
|
verticesLayers = verticesOrder;
|
||||||
}
|
}
|
||||||
|
const verticesIds = verticesOrder.flat();
|
||||||
const verticesIds = verticesLayers.flat();
|
|
||||||
useFlowStore.getState().updateVerticesBuild({
|
useFlowStore.getState().updateVerticesBuild({
|
||||||
verticesLayers,
|
verticesLayers,
|
||||||
verticesIds,
|
verticesIds,
|
||||||
verticesOrder,
|
|
||||||
runId,
|
runId,
|
||||||
});
|
});
|
||||||
resolve({ verticesLayers, verticesIds, verticesOrder, runId });
|
resolve({ verticesLayers, verticesIds, runId });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,12 +104,13 @@ export async function buildVertices({
|
||||||
validateNodes,
|
validateNodes,
|
||||||
}: BuildVerticesParams) {
|
}: BuildVerticesParams) {
|
||||||
let verticesBuild = useFlowStore.getState().verticesBuild;
|
let verticesBuild = useFlowStore.getState().verticesBuild;
|
||||||
|
|
||||||
if (!verticesBuild || nodeId) {
|
if (!verticesBuild || nodeId) {
|
||||||
verticesBuild = await updateVerticesOrder(flowId, nodeId);
|
verticesBuild = await updateVerticesOrder(flowId, nodeId);
|
||||||
}
|
}
|
||||||
let verticesIds = verticesBuild?.verticesIds!;
|
|
||||||
|
const verticesIds = verticesBuild?.verticesIds!;
|
||||||
const verticesLayers = verticesBuild?.verticesLayers!;
|
const verticesLayers = verticesBuild?.verticesLayers!;
|
||||||
const verticesOrder = verticesBuild?.verticesOrder!;
|
|
||||||
const runId = verticesBuild?.runId!;
|
const runId = verticesBuild?.runId!;
|
||||||
let stop = false;
|
let stop = false;
|
||||||
|
|
||||||
|
|
@ -121,7 +118,7 @@ export async function buildVertices({
|
||||||
|
|
||||||
if (validateNodes) {
|
if (validateNodes) {
|
||||||
try {
|
try {
|
||||||
validateNodes(verticesOrder.flatMap((id) => id));
|
validateNodes(verticesIds);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -242,6 +239,7 @@ async function buildVertex({
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
const buildRes = await postBuildVertex(flowId, id, input_value);
|
const buildRes = await postBuildVertex(flowId, id, input_value);
|
||||||
|
|
||||||
const buildData: VertexBuildTypeAPI = buildRes.data;
|
const buildData: VertexBuildTypeAPI = buildRes.data;
|
||||||
if (onBuildUpdate) {
|
if (onBuildUpdate) {
|
||||||
if (!buildData.valid) {
|
if (!buildData.valid) {
|
||||||
|
|
|
||||||
|
|
@ -25,15 +25,23 @@ export function getTagsIds(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInputsAndOutputs(nodes: Node[]) {
|
export function getInputsAndOutputs(nodes: Node[]) {
|
||||||
let inputs: { type: string; id: string }[] = [];
|
let inputs: { type: string; id: string; displayName: string }[] = [];
|
||||||
let outputs: { type: string; id: string }[] = [];
|
let outputs: { type: string; id: string; displayName: string }[] = [];
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
const nodeData: NodeDataType = node.data as NodeDataType;
|
const nodeData: NodeDataType = node.data as NodeDataType;
|
||||||
if (isOutputNode(nodeData)) {
|
if (isOutputNode(nodeData)) {
|
||||||
outputs.push({ type: nodeData.type, id: nodeData.id });
|
outputs.push({
|
||||||
|
type: nodeData.type,
|
||||||
|
id: nodeData.id,
|
||||||
|
displayName: nodeData.node?.display_name ?? nodeData.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (isInputNode(nodeData)) {
|
if (isInputNode(nodeData)) {
|
||||||
inputs.push({ type: nodeData.type, id: nodeData.id });
|
inputs.push({
|
||||||
|
type: nodeData.type,
|
||||||
|
id: nodeData.id,
|
||||||
|
displayName: nodeData.node?.display_name ?? nodeData.id,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return { inputs, outputs };
|
return { inputs, outputs };
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -2,19 +2,11 @@ import { test } from "@playwright/test";
|
||||||
|
|
||||||
test.describe("Auto_login tests", () => {
|
test.describe("Auto_login tests", () => {
|
||||||
test("auto_login sign in", async ({ page }) => {
|
test("auto_login sign in", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.goto("http:localhost:3000/");
|
await page.goto("http:localhost:3000/");
|
||||||
await page.locator('//*[@id="new-project-btn"]').click();
|
await page.locator('//*[@id="new-project-btn"]').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("auto_login block_admin", async ({ page }) => {
|
test("auto_login block_admin", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.goto("http:localhost:3000/");
|
await page.goto("http:localhost:3000/");
|
||||||
await page.locator('//*[@id="new-project-btn"]').click();
|
await page.locator('//*[@id="new-project-btn"]').click();
|
||||||
await page.goto("http:localhost:3000/login");
|
await page.goto("http:localhost:3000/login");
|
||||||
|
|
@ -117,7 +117,7 @@ test("CodeAreaModalComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showcode"]').click();
|
await page.locator('//*[@id="showcode"]').click();
|
||||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="code-area-edit0"]').click();
|
await page.locator('//*[@id="code-area-editcode"]').click();
|
||||||
|
|
||||||
let value = await page.locator('//*[@id="codeValue"]').inputValue();
|
let value = await page.locator('//*[@id="codeValue"]').inputValue();
|
||||||
|
|
||||||
|
|
@ -132,6 +132,7 @@ test("CodeAreaModalComponent", async ({ page }) => {
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
await page.locator('//*[@id="code-input-0"]').click();
|
await page.getByTestId("div-generic-node").click();
|
||||||
|
await page.getByTestId("code-button-modal").click();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,11 @@ import { readFileSync } from "fs";
|
||||||
test.describe("drag and drop test", () => {
|
test.describe("drag and drop test", () => {
|
||||||
/// <reference lib="dom"/>
|
/// <reference lib="dom"/>
|
||||||
test("drop collection", async ({ page }) => {
|
test("drop collection", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http:localhost:3000/");
|
await page.goto("http:localhost:3000/");
|
||||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
||||||
// Read your file into a buffer.
|
// Read your file into a buffer.
|
||||||
const jsonContent = readFileSync(
|
const jsonContent = readFileSync(
|
||||||
"tests/onlyFront/assets/collection.json",
|
"tests/end-to-end/assets/collection.json",
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -42,9 +32,7 @@ test.describe("drag and drop test", () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await page
|
await page.getByText("Edit Flow").first().click();
|
||||||
.getByTestId("edit-flow-button-e9ac1bdc-429b-475d-ac03-d26f9a2a3210-0")
|
|
||||||
.click();
|
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
const genericNoda = page.getByTestId("div-generic-node");
|
const genericNoda = page.getByTestId("div-generic-node");
|
||||||
|
|
@ -1,17 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("dropDownComponent", async ({ page }) => {
|
test("dropDownComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -24,24 +13,24 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.getByTestId("llmsAmazon Bedrock")
|
.getByTestId("model_specsAmazon Bedrock")
|
||||||
.first()
|
.first()
|
||||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|
||||||
await page.getByTestId("dropdown-2-display").click();
|
await page.getByTestId("dropdown-model_id-display").click();
|
||||||
await page.getByTestId("ai21.j2-grande-instruct-0-option").click();
|
await page.getByTestId("ai21.j2-grande-instruct-0-option").click();
|
||||||
|
|
||||||
let value = await page.getByTestId("dropdown-2-display").innerText();
|
let value = await page.getByTestId("dropdown-model_id-display").innerText();
|
||||||
if (value !== "ai21.j2-grande-instruct") {
|
if (value !== "ai21.j2-grande-instruct") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByTestId("dropdown-2-display").click();
|
await page.getByTestId("dropdown-model_id-display").click();
|
||||||
await page.getByTestId("ai21.j2-jumbo-instruct-1-option").click();
|
await page.getByTestId("ai21.j2-jumbo-instruct-1-option").click();
|
||||||
|
|
||||||
value = await page.getByTestId("dropdown-2-display").innerText();
|
value = await page.getByTestId("dropdown-model_id-display").innerText();
|
||||||
if (value !== "ai21.j2-jumbo-instruct") {
|
if (value !== "ai21.j2-jumbo-instruct") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
@ -49,14 +38,14 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
value = await page.getByTestId("dropdown-edit-1-display").innerText();
|
value = await page.getByTestId("dropdown-edit-model_id-display").innerText();
|
||||||
if (value !== "ai21.j2-jumbo-instruct") {
|
if (value !== "ai21.j2-jumbo-instruct") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// showcode
|
// showcode
|
||||||
await page.locator('//*[@id="showcode"]').click();
|
await page.locator('//*[@id="showcode"]').click();
|
||||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||||
|
|
||||||
// showmodel_id
|
// showmodel_id
|
||||||
await page.locator('//*[@id="showmodel_id"]').click();
|
await page.locator('//*[@id="showmodel_id"]').click();
|
||||||
|
|
@ -64,7 +53,7 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
|
|
||||||
// showcode
|
// showcode
|
||||||
await page.locator('//*[@id="showcode"]').click();
|
await page.locator('//*[@id="showcode"]').click();
|
||||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
||||||
|
|
||||||
// showmodel_id
|
// showmodel_id
|
||||||
await page.locator('//*[@id="showmodel_id"]').click();
|
await page.locator('//*[@id="showmodel_id"]').click();
|
||||||
|
|
@ -74,7 +63,7 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
|
|
||||||
// showcode
|
// showcode
|
||||||
await page.locator('//*[@id="showcode"]').click();
|
await page.locator('//*[@id="showcode"]').click();
|
||||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
||||||
|
|
||||||
// showmodel_id
|
// showmodel_id
|
||||||
await page.locator('//*[@id="showmodel_id"]').click();
|
await page.locator('//*[@id="showmodel_id"]').click();
|
||||||
|
|
@ -82,7 +71,7 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
|
|
||||||
// showcode
|
// showcode
|
||||||
await page.locator('//*[@id="showcode"]').click();
|
await page.locator('//*[@id="showcode"]').click();
|
||||||
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeTruthy();
|
expect(await page.locator('//*[@id="showcode"]').isChecked()).toBeFalsy();
|
||||||
|
|
||||||
// showmodel_id
|
// showmodel_id
|
||||||
await page.locator('//*[@id="showmodel_id"]').click();
|
await page.locator('//*[@id="showmodel_id"]').click();
|
||||||
|
|
@ -90,17 +79,17 @@ test("dropDownComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showmodel_id"]').isChecked()
|
await page.locator('//*[@id="showmodel_id"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.getByTestId("dropdown-edit-1-display").click();
|
await page.getByTestId("dropdown-edit-model_id-display").click();
|
||||||
await page.getByTestId("ai21.j2-ultra-v1-5-option").click();
|
await page.getByTestId("ai21.j2-ultra-v1-5-option").click();
|
||||||
|
|
||||||
value = await page.getByTestId("dropdown-edit-1-display").innerText();
|
value = await page.getByTestId("dropdown-edit-model_id-display").innerText();
|
||||||
if (value !== "ai21.j2-ultra-v1") {
|
if (value !== "ai21.j2-ultra-v1") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
value = await page.getByTestId("dropdown-2-display").innerText();
|
value = await page.getByTestId("dropdown-model_id-display").innerText();
|
||||||
if (value !== "ai21.j2-ultra-v1") {
|
if (value !== "ai21.j2-ultra-v1") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("FloatComponent", async ({ page }) => {
|
test("FloatComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -23,7 +13,7 @@ test("FloatComponent", async ({ page }) => {
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.locator('//*[@id="llmsLlamaCpp"]')
|
.locator('//*[@id="model_specsLlamaCpp"]')
|
||||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|
@ -102,18 +92,6 @@ test("FloatComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showmax_tokens"]').isChecked()
|
await page.locator('//*[@id="showmax_tokens"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
// showmetadata
|
|
||||||
await page.locator('//*[@id="showmetadata"]').click();
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showmetadata"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showmodel_kwargs
|
|
||||||
await page.locator('//*[@id="showmodel_kwargs"]').click();
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showmodel_kwargs"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showmodel_path
|
// showmodel_path
|
||||||
await page.locator('//*[@id="showmodel_path"]').click();
|
await page.locator('//*[@id="showmodel_path"]').click();
|
||||||
expect(
|
expect(
|
||||||
|
|
@ -271,16 +249,6 @@ test("FloatComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showmax_tokens"]').isChecked()
|
await page.locator('//*[@id="showmax_tokens"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
// showmetadata
|
|
||||||
await page.locator('//*[@id="showmetadata"]').click();
|
|
||||||
expect(await page.locator('//*[@id="showmetadata"]').isChecked()).toBeFalsy();
|
|
||||||
|
|
||||||
// showmodel_kwargs
|
|
||||||
await page.locator('//*[@id="showmodel_kwargs"]').click();
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showmodel_kwargs"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showmodel_path
|
// showmodel_path
|
||||||
await page.locator('//*[@id="showmodel_path"]').click();
|
await page.locator('//*[@id="showmodel_path"]').click();
|
||||||
expect(
|
expect(
|
||||||
|
|
@ -7,16 +7,6 @@ test.describe("Flow Page tests", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
test("save", async ({ page }) => {
|
test("save", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -4,21 +4,11 @@ import { readFileSync } from "fs";
|
||||||
test.describe("group node test", () => {
|
test.describe("group node test", () => {
|
||||||
/// <reference lib="dom"/>
|
/// <reference lib="dom"/>
|
||||||
test("group and ungroup updating values", async ({ page }) => {
|
test("group and ungroup updating values", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http:localhost:3000/");
|
await page.goto("http:localhost:3000/");
|
||||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
await page.locator('//*[@id="new-project-btn"]').click();
|
||||||
// Read your file into a buffer.
|
// Read your file into a buffer.
|
||||||
const jsonContent = readFileSync(
|
const jsonContent = readFileSync(
|
||||||
"tests/onlyFront/assets/collection.json",
|
"tests/end-to-end/assets/flow_group_test.json",
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -26,7 +16,7 @@ test.describe("group node test", () => {
|
||||||
const dataTransfer = await page.evaluateHandle((data) => {
|
const dataTransfer = await page.evaluateHandle((data) => {
|
||||||
const dt = new DataTransfer();
|
const dt = new DataTransfer();
|
||||||
// Convert the buffer to a hex array
|
// Convert the buffer to a hex array
|
||||||
const file = new File([data], "flowtest.json", {
|
const file = new File([data], "flow_group_test.json", {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
});
|
});
|
||||||
dt.items.add(file);
|
dt.items.add(file);
|
||||||
|
|
@ -35,24 +25,27 @@ test.describe("group node test", () => {
|
||||||
|
|
||||||
page.waitForTimeout(2000);
|
page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
// Now dispatch
|
||||||
await page.dispatchEvent(
|
await page.dispatchEvent(
|
||||||
'//*[@id="root"]/div/div[1]/div[2]/div[3]/div/div',
|
"//*[@id='react-flow-id']/div[1]/div[1]/div",
|
||||||
"drop",
|
"drop",
|
||||||
{
|
{
|
||||||
dataTransfer,
|
dataTransfer,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await page
|
|
||||||
.getByTestId("edit-flow-button-e9ac1bdc-429b-475d-ac03-d26f9a2a3210-0")
|
|
||||||
.click();
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
const genericNoda = page.getByTestId("div-generic-node");
|
const genericNoda = page.getByTestId("div-generic-node");
|
||||||
const elementCount = await genericNoda.count();
|
const elementCount = await genericNoda.count();
|
||||||
if (elementCount > 0) {
|
if (elementCount > 0) {
|
||||||
expect(true).toBeTruthy();
|
expect(true).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
await page
|
||||||
|
.locator('//*[@id="react-flow-id"]/div[1]/div[2]/button[3]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
await page.getByTestId("title-Agent Initializer").click({
|
||||||
|
modifiers: ["Control"],
|
||||||
|
});
|
||||||
|
|
||||||
await page.getByTestId("title-PythonFunctionTool").click({
|
await page.getByTestId("title-PythonFunctionTool").click({
|
||||||
modifiers: ["Control"],
|
modifiers: ["Control"],
|
||||||
|
|
@ -61,10 +54,6 @@ test.describe("group node test", () => {
|
||||||
modifiers: ["Control"],
|
modifiers: ["Control"],
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTestId("title-AgentInitializer").click({
|
|
||||||
modifiers: ["Control"],
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Group" }).click();
|
await page.getByRole("button", { name: "Group" }).click();
|
||||||
|
|
||||||
const textArea = page.getByTestId("div-textarea-description");
|
const textArea = page.getByTestId("div-textarea-description");
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("InputComponent", async ({ page }) => {
|
test("InputComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -28,12 +18,12 @@ test("InputComponent", async ({ page }) => {
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|
||||||
await page.locator("#input-8").click();
|
await page.getByTestId("input-collection_name").click();
|
||||||
await page
|
await page
|
||||||
.locator("#input-8")
|
.getByTestId("input-collection_name")
|
||||||
.fill("collection_name_test_123123123!@#$&*(&%$@");
|
.fill("collection_name_test_123123123!@#$&*(&%$@");
|
||||||
|
|
||||||
let value = await page.locator("#input-8").inputValue();
|
let value = await page.getByTestId("input-collection_name").inputValue();
|
||||||
|
|
||||||
if (value != "collection_name_test_123123123!@#$&*(&%$@") {
|
if (value != "collection_name_test_123123123!@#$&*(&%$@") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
|
|
@ -61,9 +51,9 @@ test("InputComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showchroma_server_http_port"]').click();
|
await page.locator('//*[@id="showchroma_server_port"]').click();
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showchroma_server_http_port"]').isChecked()
|
await page.locator('//*[@id="showchroma_server_port"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
||||||
|
|
@ -76,19 +66,11 @@ test("InputComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showcollection_name"]').isChecked()
|
await page.locator('//*[@id="showcollection_name"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showpersist"]').click();
|
await page.locator('//*[@id="showindex_directory"]').click();
|
||||||
expect(await page.locator('//*[@id="showpersist"]').isChecked()).toBeFalsy();
|
|
||||||
|
|
||||||
await page.locator('//*[@id="showpersist_directory"]').click();
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpersist_directory"]').isChecked()
|
await page.locator('//*[@id="showindex_directory"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click();
|
await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click();
|
||||||
expect(
|
expect(
|
||||||
await page
|
await page
|
||||||
|
|
@ -106,9 +88,9 @@ test("InputComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
await page.locator('//*[@id="showchroma_server_host"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showchroma_server_http_port"]').click();
|
await page.locator('//*[@id="showchroma_server_port"]').click();
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showchroma_server_http_port"]').isChecked()
|
await page.locator('//*[@id="showchroma_server_port"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click();
|
||||||
|
|
@ -116,33 +98,27 @@ test("InputComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked()
|
await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showpersist"]').click();
|
await page.locator('//*[@id="showindex_directory"]').click();
|
||||||
expect(await page.locator('//*[@id="showpersist"]').isChecked()).toBeTruthy();
|
|
||||||
|
|
||||||
await page.locator('//*[@id="showpersist_directory"]').click();
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpersist_directory"]').isChecked()
|
await page.locator('//*[@id="showindex_directory"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
let valueEditNode = await page
|
||||||
expect(
|
.getByTestId("input-collection_name-edit")
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
.inputValue();
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
let valueEditNode = await page.locator('//*[@id="input-5"]').inputValue();
|
|
||||||
|
|
||||||
if (valueEditNode != "collection_name_test_123123123!@#$&*(&%$@") {
|
if (valueEditNode != "collection_name_test_123123123!@#$&*(&%$@") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="input-5"]').click();
|
await page.getByTestId("input-collection_name-edit").click();
|
||||||
await page
|
await page
|
||||||
.locator('//*[@id="input-5"]')
|
.getByTestId("input-collection_name-edit")
|
||||||
.fill("NEW_collection_name_test_123123123!@#$&*(&%$@");
|
.fill("NEW_collection_name_test_123123123!@#$&*(&%$@");
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
const plusButtonLocator = page.locator("#input-8");
|
const plusButtonLocator = page.getByTestId("input-collection_name");
|
||||||
const elementCount = await plusButtonLocator.count();
|
const elementCount = await plusButtonLocator.count();
|
||||||
if (elementCount === 0) {
|
if (elementCount === 0) {
|
||||||
expect(true).toBeTruthy();
|
expect(true).toBeTruthy();
|
||||||
|
|
@ -159,7 +135,7 @@ test("InputComponent", async ({ page }) => {
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
let value = await page.locator("#input-8").inputValue();
|
let value = await page.getByTestId("input-collection_name").inputValue();
|
||||||
|
|
||||||
if (value != "NEW_collection_name_test_123123123!@#$&*(&%$@") {
|
if (value != "NEW_collection_name_test_123123123!@#$&*(&%$@") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
|
|
@ -1,17 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("IntComponent", async ({ page }) => {
|
test("IntComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -30,21 +19,21 @@ test("IntComponent", async ({ page }) => {
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|
||||||
await page.locator('//*[@id="int-input-2"]').click();
|
await page.getByTestId("int-input-timeout").click();
|
||||||
await page
|
await page
|
||||||
.locator('//*[@id="int-input-2"]')
|
.getByTestId("int-input-timeout")
|
||||||
.fill("123456789123456789123456789");
|
.fill("123456789123456789123456789");
|
||||||
|
|
||||||
let value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
let value = await page.getByTestId("int-input-timeout").inputValue();
|
||||||
|
|
||||||
if (value != "123456789123456789123456789") {
|
if (value != "123456789123456789123456789") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="int-input-2"]').click();
|
await page.getByTestId("int-input-timeout").click();
|
||||||
await page.locator('//*[@id="int-input-2"]').fill("0");
|
await page.getByTestId("int-input-timeout").fill("0");
|
||||||
|
|
||||||
value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
value = await page.getByTestId("int-input-timeout").inputValue();
|
||||||
|
|
||||||
if (value != "0") {
|
if (value != "0") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
|
|
@ -53,15 +42,15 @@ test("IntComponent", async ({ page }) => {
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
value = await page.locator('//*[@id="edit-int-input-2"]').inputValue();
|
value = await page.getByTestId("edit-int-input-timeout").inputValue();
|
||||||
|
|
||||||
if (value != "0") {
|
if (value != "0") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="edit-int-input-2"]').click();
|
await page.getByTestId("edit-int-input-timeout").click();
|
||||||
await page
|
await page
|
||||||
.locator('//*[@id="edit-int-input-2"]')
|
.getByTestId("edit-int-input-timeout")
|
||||||
.fill("123456789123456789123456789");
|
.fill("123456789123456789123456789");
|
||||||
|
|
||||||
await page.locator('//*[@id="showheaders"]').click();
|
await page.locator('//*[@id="showheaders"]').click();
|
||||||
|
|
@ -81,7 +70,7 @@ test("IntComponent", async ({ page }) => {
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
const plusButtonLocator = page.locator('//*[@id="int-input-2"]');
|
const plusButtonLocator = page.getByTestId("int-input-timeout");
|
||||||
const elementCount = await plusButtonLocator.count();
|
const elementCount = await plusButtonLocator.count();
|
||||||
if (elementCount === 0) {
|
if (elementCount === 0) {
|
||||||
expect(true).toBeTruthy();
|
expect(true).toBeTruthy();
|
||||||
|
|
@ -95,7 +84,7 @@ test("IntComponent", async ({ page }) => {
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
const valueEditNode = await page
|
const valueEditNode = await page
|
||||||
.locator('//*[@id="edit-int-input-2"]')
|
.getByTestId("edit-int-input-timeout")
|
||||||
.inputValue();
|
.inputValue();
|
||||||
|
|
||||||
if (valueEditNode != "123456789123456789123456789") {
|
if (valueEditNode != "123456789123456789123456789") {
|
||||||
|
|
@ -103,19 +92,19 @@ test("IntComponent", async ({ page }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
await page.locator('//*[@id="int-input-2"]').click();
|
await page.getByTestId("int-input-timeout").click();
|
||||||
await page.locator('//*[@id="int-input-2"]').fill("3");
|
await page.getByTestId("int-input-timeout").fill("3");
|
||||||
|
|
||||||
let value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
let value = await page.getByTestId("int-input-timeout").inputValue();
|
||||||
|
|
||||||
if (value != "3") {
|
if (value != "3") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.locator('//*[@id="int-input-2"]').click();
|
await page.getByTestId("int-input-timeout").click();
|
||||||
await page.locator('//*[@id="int-input-2"]').fill("-3");
|
await page.getByTestId("int-input-timeout").fill("-3");
|
||||||
|
|
||||||
value = await page.locator('//*[@id="int-input-2"]').inputValue();
|
value = await page.getByTestId("int-input-timeout").inputValue();
|
||||||
|
|
||||||
if (value != "0") {
|
if (value != "0") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("KeypairListComponent", async ({ page }) => {
|
test("KeypairListComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("NestedComponent", async ({ page }) => {
|
test("NestedComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/backend_12112023.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -31,75 +21,17 @@ test("NestedComponent", async ({ page }) => {
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
// showindex_name
|
//showpool_threads
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
await page.locator('//*[@id="showpool_threads"]').click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showindex_name"]').isChecked()
|
await page.locator('//*[@id="showpool_threads"]').isChecked()
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// shownamespace
|
|
||||||
await page.locator('//*[@id="shownamespace"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="shownamespace"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showpinecone_api_key
|
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
// showpinecone_env
|
//showtext_key
|
||||||
await page.locator('//*[@id="showpinecone_env"]').click();
|
await page.locator('//*[@id="showtext_key"]').click();
|
||||||
|
|
||||||
expect(
|
expect(await page.locator('//*[@id="showtext_key"]').isChecked()).toBeFalsy();
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showsearch_kwargs
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showindex_name
|
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showindex_name"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// shownamespace
|
|
||||||
await page.locator('//*[@id="shownamespace"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="shownamespace"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showpinecone_api_key
|
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showpinecone_env
|
|
||||||
await page.locator('//*[@id="showpinecone_env"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showsearch_kwargs
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showindex_name
|
// showindex_name
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
@ -120,21 +52,14 @@ test("NestedComponent", async ({ page }) => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeFalsy();
|
||||||
|
|
||||||
// showpinecone_env
|
// showpinecone_env
|
||||||
await page.locator('//*[@id="showpinecone_env"]').click();
|
await page.locator('//*[@id="showpinecone_env"]').click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeFalsy();
|
||||||
|
|
||||||
// showsearch_kwargs
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showindex_name
|
// showindex_name
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
@ -155,21 +80,14 @@ test("NestedComponent", async ({ page }) => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeTruthy();
|
||||||
|
|
||||||
// showpinecone_env
|
// showpinecone_env
|
||||||
await page.locator('//*[@id="showpinecone_env"]').click();
|
await page.locator('//*[@id="showpinecone_env"]').click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeTruthy();
|
||||||
|
|
||||||
// showsearch_kwargs
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
// showindex_name
|
// showindex_name
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
@ -190,21 +108,14 @@ test("NestedComponent", async ({ page }) => {
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeFalsy();
|
||||||
|
|
||||||
// showpinecone_env
|
// showpinecone_env
|
||||||
await page.locator('//*[@id="showpinecone_env"]').click();
|
await page.locator('//*[@id="showpinecone_env"]').click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
).toBeTruthy();
|
).toBeFalsy();
|
||||||
|
|
||||||
// showsearch_kwargs
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').click();
|
|
||||||
|
|
||||||
expect(
|
|
||||||
await page.locator('//*[@id="showsearch_kwargs"]').isChecked()
|
|
||||||
).toBeTruthy();
|
|
||||||
|
|
||||||
// showindex_name
|
// showindex_name
|
||||||
await page.locator('//*[@id="showindex_name"]').click();
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
@ -223,6 +134,34 @@ test("NestedComponent", async ({ page }) => {
|
||||||
// showpinecone_api_key
|
// showpinecone_api_key
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
// showpinecone_env
|
||||||
|
await page.locator('//*[@id="showpinecone_env"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
// showindex_name
|
||||||
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showindex_name"]').isChecked()
|
||||||
|
).toBeFalsy();
|
||||||
|
|
||||||
|
// shownamespace
|
||||||
|
await page.locator('//*[@id="shownamespace"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="shownamespace"]').isChecked()
|
||||||
|
).toBeFalsy();
|
||||||
|
|
||||||
|
// showpinecone_api_key
|
||||||
|
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
@ -234,7 +173,47 @@ test("NestedComponent", async ({ page }) => {
|
||||||
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
|
// showindex_name
|
||||||
|
await page.locator('//*[@id="showindex_name"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showindex_name"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
// shownamespace
|
||||||
|
await page.locator('//*[@id="shownamespace"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="shownamespace"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
// showpinecone_api_key
|
||||||
|
await page.locator('//*[@id="showpinecone_api_key"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showpinecone_api_key"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
// showpinecone_env
|
||||||
|
await page.locator('//*[@id="showpinecone_env"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showpinecone_env"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
|
//showpool_threads
|
||||||
|
await page.locator('//*[@id="showpool_threads"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showpool_threads"]').isChecked()
|
||||||
|
).toBeFalsy();
|
||||||
|
|
||||||
|
//showtext_key
|
||||||
|
await page.locator('//*[@id="showtext_key"]').click();
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await page.locator('//*[@id="showtext_key"]').isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
await page.getByTestId("div-dict-input").click();
|
|
||||||
});
|
});
|
||||||
|
|
@ -8,25 +8,27 @@ test("PromptTemplateComponent", async ({ page }) => {
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page.getByPlaceholder("Search").click();
|
await page.getByPlaceholder("Search").click();
|
||||||
await page.getByPlaceholder("Search").fill("promptTemplate");
|
await page.getByPlaceholder("Search").fill("prompt");
|
||||||
|
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
await page
|
await page
|
||||||
.locator('//*[@id="promptsPromptTemplate"]')
|
.locator('//*[@id="promptsPrompt"]')
|
||||||
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
.dragTo(page.locator('//*[@id="react-flow-id"]'));
|
||||||
await page.mouse.up();
|
await page.mouse.up();
|
||||||
await page.mouse.down();
|
await page.mouse.down();
|
||||||
|
|
||||||
await page.getByTestId("prompt-input-0").click();
|
await page.getByTestId("prompt-input-template").click();
|
||||||
|
|
||||||
// await page.getByTestId("edit-prompt-sanitized").click();
|
// await page.getByTestId("edit-prompt-sanitized").click();
|
||||||
// await page.getByTestId("modal-title").click();
|
// await page.getByTestId("modal-title").click();
|
||||||
await page
|
await page
|
||||||
.getByTestId("modal-prompt-input-0")
|
.getByTestId("modal-prompt-input-template")
|
||||||
.fill("{prompt} example {prompt1}");
|
.fill("{prompt} example {prompt1}");
|
||||||
|
|
||||||
let value = await page.getByTestId("modal-prompt-input-0").inputValue();
|
let value = await page
|
||||||
|
.getByTestId("modal-prompt-input-template")
|
||||||
|
.inputValue();
|
||||||
|
|
||||||
if (value != "{prompt} example {prompt1}") {
|
if (value != "{prompt} example {prompt1}") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
|
|
@ -45,35 +47,39 @@ test("PromptTemplateComponent", async ({ page }) => {
|
||||||
await page.getByTestId("genericModalBtnSave").click();
|
await page.getByTestId("genericModalBtnSave").click();
|
||||||
|
|
||||||
await page.getByTestId("div-textarea-prompt").click();
|
await page.getByTestId("div-textarea-prompt").click();
|
||||||
await page.getByTestId("text-area-modal").fill("prompt_value_!@#!@#");
|
await page.getByTestId("textarea-prompt").fill("prompt_value_!@#!@#");
|
||||||
|
|
||||||
value = await page.getByTestId("text-area-modal").inputValue();
|
value = await page.getByTestId("textarea-prompt").inputValue();
|
||||||
|
|
||||||
if (value != "prompt_value_!@#!@#") {
|
if (value != "prompt_value_!@#!@#") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByTestId("genericModalBtnSave").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
|
await page.getByTestId("save-button-modal").click();
|
||||||
|
|
||||||
|
const replace = await page.getByTestId("replace-button");
|
||||||
|
if (replace) {
|
||||||
|
await page.getByTestId("replace-button").click();
|
||||||
|
}
|
||||||
|
|
||||||
await page.getByTestId("div-textarea-prompt1").click();
|
await page.getByTestId("div-textarea-prompt1").click();
|
||||||
await page
|
await page
|
||||||
.getByTestId("text-area-modal")
|
.getByTestId("textarea-prompt1")
|
||||||
.fill("prompt_name_test_123123!@#!@#");
|
.fill("prompt_name_test_123123!@#!@#");
|
||||||
|
|
||||||
value = await page.getByTestId("text-area-modal").inputValue();
|
value = await page.getByTestId("textarea-prompt1").inputValue();
|
||||||
|
|
||||||
if (value != "prompt_name_test_123123!@#!@#") {
|
if (value != "prompt_name_test_123123!@#!@#") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
value = await page.getByTestId("text-area-modal").inputValue();
|
value = await page.getByTestId("textarea-prompt1").inputValue();
|
||||||
|
|
||||||
if (value != "prompt_name_test_123123!@#!@#") {
|
if (value != "prompt_name_test_123123!@#!@#") {
|
||||||
expect(false).toBeTruthy();
|
expect(false).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByTestId("genericModalBtnSave").click();
|
|
||||||
|
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,22 +11,12 @@ test.describe("save component tests", () => {
|
||||||
|
|
||||||
/// <reference lib="dom"/>
|
/// <reference lib="dom"/>
|
||||||
test("save group component tests", async ({ page }) => {
|
test("save group component tests", async ({ page }) => {
|
||||||
//make front work withoput backend
|
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http:localhost:3000/");
|
await page.goto("http:localhost:3000/");
|
||||||
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
|
await page.locator('//*[@id="new-project-btn"]').click();
|
||||||
|
|
||||||
// Read your file into a buffer.
|
// Read your file into a buffer.
|
||||||
const jsonContent = readFileSync(
|
const jsonContent = readFileSync(
|
||||||
"tests/onlyFront/assets/collection.json",
|
"tests/end-to-end/assets/flow_group_test.json",
|
||||||
"utf-8"
|
"utf-8"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -34,32 +24,32 @@ test.describe("save component tests", () => {
|
||||||
const dataTransfer = await page.evaluateHandle((data) => {
|
const dataTransfer = await page.evaluateHandle((data) => {
|
||||||
const dt = new DataTransfer();
|
const dt = new DataTransfer();
|
||||||
// Convert the buffer to a hex array
|
// Convert the buffer to a hex array
|
||||||
const file = new File([data], "flowtest.json", {
|
const file = new File([data], "flow_group_test.json", {
|
||||||
type: "application/json",
|
type: "application/json",
|
||||||
});
|
});
|
||||||
dt.items.add(file);
|
dt.items.add(file);
|
||||||
return dt;
|
return dt;
|
||||||
}, jsonContent);
|
}, jsonContent);
|
||||||
|
|
||||||
|
page.waitForTimeout(2000);
|
||||||
|
|
||||||
// Now dispatch
|
// Now dispatch
|
||||||
await page.dispatchEvent(
|
await page.dispatchEvent(
|
||||||
'//*[@id="root"]/div/div[1]/div[2]/div[3]/div/div',
|
"//*[@id='react-flow-id']/div[1]/div[1]/div",
|
||||||
"drop",
|
"drop",
|
||||||
{
|
{
|
||||||
dataTransfer,
|
dataTransfer,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await page
|
|
||||||
.getByTestId("edit-flow-button-e9ac1bdc-429b-475d-ac03-d26f9a2a3210-0")
|
|
||||||
.click();
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
const genericNoda = page.getByTestId("div-generic-node");
|
const genericNoda = page.getByTestId("div-generic-node");
|
||||||
const elementCount = await genericNoda.count();
|
const elementCount = await genericNoda.count();
|
||||||
if (elementCount > 0) {
|
if (elementCount > 0) {
|
||||||
expect(true).toBeTruthy();
|
expect(true).toBeTruthy();
|
||||||
}
|
}
|
||||||
|
await page
|
||||||
|
.locator('//*[@id="react-flow-id"]/div[1]/div[2]/button[3]')
|
||||||
|
.click();
|
||||||
|
|
||||||
await page.getByTestId("title-PythonFunctionTool").click({
|
await page.getByTestId("title-PythonFunctionTool").click({
|
||||||
modifiers: ["Control"],
|
modifiers: ["Control"],
|
||||||
|
|
@ -68,7 +58,7 @@ test.describe("save component tests", () => {
|
||||||
modifiers: ["Control"],
|
modifiers: ["Control"],
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByTestId("title-AgentInitializer").click({
|
await page.getByTestId("title-Agent Initializer").click({
|
||||||
modifiers: ["Control"],
|
modifiers: ["Control"],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -87,9 +77,13 @@ test.describe("save component tests", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await page.getByTestId("title-Group").click();
|
await page.getByTestId("title-Group").click();
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("icon-SaveAll").click();
|
||||||
await page.getByTestId("save-button-modal").click();
|
|
||||||
await page.getByTestId("delete-button-modal").click();
|
const replaceButton = page.getByTestId("replace-button");
|
||||||
|
|
||||||
|
if (replaceButton) {
|
||||||
|
await replaceButton.click();
|
||||||
|
}
|
||||||
|
|
||||||
await page.getByPlaceholder("Search").click();
|
await page.getByPlaceholder("Search").click();
|
||||||
await page.getByPlaceholder("Search").fill("group");
|
await page.getByPlaceholder("Search").fill("group");
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
|
|
||||||
test("ToggleComponent", async ({ page }) => {
|
test("ToggleComponent", async ({ page }) => {
|
||||||
await page.routeFromHAR("harFiles/langflow.har", {
|
|
||||||
url: "**/api/v1/**",
|
|
||||||
update: false,
|
|
||||||
});
|
|
||||||
await page.route("**/api/v1/flows/", async (route) => {
|
|
||||||
const json = {
|
|
||||||
id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
};
|
|
||||||
await route.fulfill({ json, status: 201 });
|
|
||||||
});
|
|
||||||
await page.goto("http://localhost:3000/");
|
await page.goto("http://localhost:3000/");
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
|
|
||||||
|
|
@ -38,29 +28,27 @@ test("ToggleComponent", async ({ page }) => {
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy();
|
||||||
|
|
||||||
await page.getByTestId("div-generic-node").click();
|
await page.getByTestId("div-generic-node").click();
|
||||||
|
|
||||||
await page.getByTestId("more-options-modal").click();
|
await page.getByTestId("more-options-modal").click();
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
expect(
|
expect(await page.getByTestId("toggle-load_hidden").isChecked()).toBeFalsy();
|
||||||
await page.locator('//*[@id="toggle-edit-1"]').isChecked()
|
|
||||||
).toBeFalsy();
|
|
||||||
|
|
||||||
await page.locator('//*[@id="showglob"]').click();
|
await page.locator('//*[@id="showglob"]').click();
|
||||||
expect(await page.locator('//*[@id="showglob"]').isChecked()).toBeFalsy();
|
expect(await page.locator('//*[@id="showglob"]').isChecked()).toBeFalsy();
|
||||||
|
|
@ -129,7 +117,7 @@ test("ToggleComponent", async ({ page }) => {
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
const plusButtonLocator = page.locator('//*[@id="toggle-1"]');
|
const plusButtonLocator = page.getByTestId("toggle-load_hidden");
|
||||||
const elementCount = await plusButtonLocator.count();
|
const elementCount = await plusButtonLocator.count();
|
||||||
if (elementCount === 0) {
|
if (elementCount === 0) {
|
||||||
expect(true).toBeTruthy();
|
expect(true).toBeTruthy();
|
||||||
|
|
@ -145,24 +133,34 @@ test("ToggleComponent", async ({ page }) => {
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.locator('//*[@id="toggle-edit-1"]').isChecked()
|
await page.getByTestId("toggle-edit-load_hidden").isChecked()
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="saveChangesBtn"]').click();
|
await page.locator('//*[@id="saveChangesBtn"]').click();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
expect(
|
||||||
|
await page.getByTestId("toggle-load_hidden").isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
expect(
|
||||||
|
await page.getByTestId("toggle-load_hidden").isChecked()
|
||||||
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
expect(
|
||||||
|
await page.getByTestId("toggle-load_hidden").isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeFalsy();
|
expect(
|
||||||
|
await page.getByTestId("toggle-load_hidden").isChecked()
|
||||||
|
).toBeFalsy();
|
||||||
|
|
||||||
await page.locator('//*[@id="toggle-1"]').click();
|
await page.getByTestId("toggle-load_hidden").click();
|
||||||
expect(await page.locator('//*[@id="toggle-1"]').isChecked()).toBeTruthy();
|
expect(
|
||||||
|
await page.getByTestId("toggle-load_hidden").isChecked()
|
||||||
|
).toBeTruthy();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
import { test } from "@playwright/test";
|
|
||||||
|
|
||||||
test.describe("Login Tests", () => {
|
|
||||||
test("Login_Success", async ({ page }) => {
|
|
||||||
// await page.route("**/api/v1/login", async (route) => {
|
|
||||||
// const json = {
|
|
||||||
// access_token:
|
|
||||||
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWNlM2FkOS1iZTE2LTRiNjgtOGRhYi1hYjA4YTVjMmZjZTkiLCJleHAiOjE2OTUyNTIwNTh9.MBYFwMhTcZnsW_L7p4qavUhSDylCllJQWUCJdU1wX8o",
|
|
||||||
// refresh_token:
|
|
||||||
// "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhMWNlM2FkOS1iZTE2LTRiNjgtOGRhYi1hYjA4YTVjMmZjZTkiLCJ0eXBlIjoicmYiLCJleHAiOjE2OTUyNTI2NTh9.a4wL9-XK_zyTyrXduBFgCsODFXrqiByVr5HOeiCbiQA",
|
|
||||||
// token_type: "bearer",
|
|
||||||
// };
|
|
||||||
// await route.fulfill({ json });
|
|
||||||
// });
|
|
||||||
// await page.goto("http://localhost:3000/");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
|
||||||
// await page.getByPlaceholder("Username").click();
|
|
||||||
// await page.getByPlaceholder("Username").fill("test");
|
|
||||||
// await page.getByPlaceholder("Password").click();
|
|
||||||
// await page.getByPlaceholder("Password").fill("test");
|
|
||||||
// await page.getByRole("button", { name: "Sign in" }).click();
|
|
||||||
// await page.getByRole("button", { name: "Community Examples" }).click();
|
|
||||||
// await page.waitForSelector(".community-pages-flows-panel");
|
|
||||||
// expect(
|
|
||||||
// await page
|
|
||||||
// .locator(".community-pages-flows-panel")
|
|
||||||
// .evaluate((el) => el.children)
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// test("Login Error", async ({ page }) => {
|
|
||||||
// await page.route("**/api/v1/login", async (route) => {
|
|
||||||
// const json = { detail: "Incorrect username or password" };
|
|
||||||
// await route.fulfill({ json, status: 401 });
|
|
||||||
// });
|
|
||||||
// await page.goto("http://localhost:3000/");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
|
||||||
// await page.getByPlaceholder("Username").click();
|
|
||||||
// await page.getByPlaceholder("Username").fill("test");
|
|
||||||
// await page.getByPlaceholder("Password").click();
|
|
||||||
// await page.getByPlaceholder("Password").fill("test5");
|
|
||||||
// await page.getByRole("button", { name: "Sign in" }).click();
|
|
||||||
// await page.getByRole("heading", { name: "Error signing in" }).click();
|
|
||||||
// });
|
|
||||||
// test("Login create account wrong form", async ({ page }) => {
|
|
||||||
// const fullfillForm = async (username, password, confirmPassword) => {
|
|
||||||
// await page.getByPlaceholder("Username").click();
|
|
||||||
// await page.getByPlaceholder("Username").fill(username);
|
|
||||||
// await page.getByPlaceholder("Password", { exact: true }).click();
|
|
||||||
// await page.getByPlaceholder("Password", { exact: true }).fill(password);
|
|
||||||
// await page.getByPlaceholder("Confirm your password").click();
|
|
||||||
// await page
|
|
||||||
// .getByPlaceholder("Confirm your password")
|
|
||||||
// .fill(confirmPassword);
|
|
||||||
// };
|
|
||||||
// await page.goto("http://localhost:3000/");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
|
||||||
// await page
|
|
||||||
// .getByRole("button", { name: "Don't have an account? Sign Up" })
|
|
||||||
// .click();
|
|
||||||
// await page.getByText("Sign up to Langflow").click();
|
|
||||||
// await page.goto("http://localhost:3000/signup");
|
|
||||||
// await page.getByText("Sign up to Langflow").click();
|
|
||||||
// await fullfillForm("name", "vazz", "vazz5");
|
|
||||||
// expect(
|
|
||||||
// await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// await fullfillForm("", "vazz", "vazz");
|
|
||||||
// expect(
|
|
||||||
// await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// await fullfillForm("name", "", "");
|
|
||||||
// expect(
|
|
||||||
// await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// await fullfillForm("", "", "");
|
|
||||||
// expect(
|
|
||||||
// await page.getByRole("button", { name: "Sign up" }).isDisabled()
|
|
||||||
// ).toBeTruthy();
|
|
||||||
// });
|
|
||||||
// test("Login create account success", async ({ page }) => {
|
|
||||||
// await page.route("**/api/v1/users/", async (route) => {
|
|
||||||
// const json = {
|
|
||||||
// id: "e9ac1bdc-429b-475d-ac03-d26f9a2a3210",
|
|
||||||
// username: "teste",
|
|
||||||
// profile_image: null,
|
|
||||||
// is_active: false,
|
|
||||||
// is_superuser: false,
|
|
||||||
// create_at: "2023-09-21T01:45:51.873303",
|
|
||||||
// updated_at: "2023-09-21T01:45:51.873305",
|
|
||||||
// last_login_at: null,
|
|
||||||
// };
|
|
||||||
// await route.fulfill({ json, status: 201 });
|
|
||||||
// });
|
|
||||||
// const submitForm = async (username, password, confirmPassword) => {
|
|
||||||
// await page.getByPlaceholder("Username").click();
|
|
||||||
// await page.getByPlaceholder("Username").fill(username);
|
|
||||||
// await page.getByPlaceholder("Password", { exact: true }).click();
|
|
||||||
// await page.getByPlaceholder("Password", { exact: true }).fill(password);
|
|
||||||
// await page.getByPlaceholder("Confirm your password").click();
|
|
||||||
// await page
|
|
||||||
// .getByPlaceholder("Confirm your password")
|
|
||||||
// .fill(confirmPassword);
|
|
||||||
// };
|
|
||||||
// await page.goto("http://localhost:3000/");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login");
|
|
||||||
// await page.waitForURL("http://localhost:3000/login", { timeout: 100 });
|
|
||||||
// await page
|
|
||||||
// .getByRole("button", { name: "Don't have an account? Sign Up" })
|
|
||||||
// .click();
|
|
||||||
// await page.getByText("Sign up to Langflow").click();
|
|
||||||
// await page.goto("http://localhost:3000/signup");
|
|
||||||
// await page.getByText("Sign up to Langflow").click();
|
|
||||||
// await submitForm("teste", "pass", "pass");
|
|
||||||
// await page.getByRole("button", { name: "Sign up" }).click();
|
|
||||||
// await page.waitForURL("http://localhost:3000/login", { timeout: 1000 });
|
|
||||||
// await page.getByText("Account created! Await admin activation.").click();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue