Merge branch 'two_edges' into two_edges_dev
This commit is contained in:
commit
902e616286
22 changed files with 235 additions and 186 deletions
|
|
@ -5,6 +5,8 @@ from langflow.inputs import StrInput
|
|||
from langflow.schema import Data
|
||||
from langflow.template import Output
|
||||
|
||||
import re
|
||||
|
||||
|
||||
class URLComponent(Component):
|
||||
display_name = "URL"
|
||||
|
|
@ -25,8 +27,39 @@ class URLComponent(Component):
|
|||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
]
|
||||
|
||||
def ensure_url(self, string: str) -> str:
|
||||
"""
|
||||
Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.
|
||||
Raises an error if the string is not a valid URL.
|
||||
|
||||
Parameters:
|
||||
string (str): The string to be checked and possibly modified.
|
||||
|
||||
Returns:
|
||||
str: The modified string that is ensured to be a URL.
|
||||
|
||||
Raises:
|
||||
ValueError: If the string is not a valid URL.
|
||||
"""
|
||||
if not string.startswith(("http://", "https://")):
|
||||
string = "http://" + string
|
||||
|
||||
# Basic URL validation regex
|
||||
url_regex = re.compile(
|
||||
r"^(http://|https://)?" # http:// or https://
|
||||
r"(([a-zA-Z0-9\.-]+)" # domain
|
||||
r"(\.[a-zA-Z]{2,}))" # top-level domain
|
||||
r"(:[0-9]{1,5})?" # optional port
|
||||
r"(\/.*)?$" # optional path
|
||||
)
|
||||
|
||||
if not re.match(url_regex, string):
|
||||
raise ValueError(f"Invalid URL: {string}")
|
||||
|
||||
return string
|
||||
|
||||
def fetch_content(self) -> Data:
|
||||
urls = [url.strip() for url in self.urls if url.strip()]
|
||||
urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]
|
||||
loader = WebBaseLoader(web_paths=urls)
|
||||
docs = loader.load()
|
||||
data = [Data(content=doc.page_content, **doc.metadata) for doc in docs]
|
||||
|
|
|
|||
|
|
@ -1,90 +1,92 @@
|
|||
from typing import Union
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Text
|
||||
from langflow.schema import Data
|
||||
from langflow.template import Input, Output
|
||||
from langflow.inputs import BoolInput, DropdownInput, StrInput
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class TextOperatorComponent(Component):
|
||||
display_name = "Text Operator"
|
||||
description = "Compares two text inputs based on a specified condition such as equality or inequality, with optional case sensitivity."
|
||||
icon = "equal"
|
||||
|
||||
inputs = [
|
||||
Input(name="input_text", type=str, display_name="Input Text", info="The primary text input for the operation."),
|
||||
Input(name="match_text", type=str, display_name="Match Text", info="The text input to compare against."),
|
||||
Input(
|
||||
name="operator",
|
||||
type=str,
|
||||
display_name="Operator",
|
||||
info="The operator to apply for comparing the texts.",
|
||||
options=["equals", "not equals", "contains", "starts with", "ends with", "exists"],
|
||||
StrInput(
|
||||
name="input_text",
|
||||
display_name="Input Text",
|
||||
info="The primary text input for the operation.",
|
||||
),
|
||||
Input(
|
||||
StrInput(
|
||||
name="match_text",
|
||||
display_name="Match Text",
|
||||
info="The text input to compare against.",
|
||||
),
|
||||
DropdownInput(
|
||||
name="operator",
|
||||
display_name="Operator",
|
||||
options=["equals", "not equals", "contains", "starts with", "ends with"],
|
||||
info="The operator to apply for comparing the texts.",
|
||||
),
|
||||
BoolInput(
|
||||
name="case_sensitive",
|
||||
type=bool,
|
||||
display_name="Case Sensitive",
|
||||
info="If true, the comparison will be case sensitive.",
|
||||
default=False,
|
||||
value=False,
|
||||
advanced=True,
|
||||
),
|
||||
Input(
|
||||
StrInput(
|
||||
name="true_output",
|
||||
type=Union[str, Data],
|
||||
display_name="True Output",
|
||||
info="The output to return or display when the comparison is true.",
|
||||
input_types=["Text", "Data"],
|
||||
advanced=True,
|
||||
),
|
||||
Input(
|
||||
StrInput(
|
||||
name="false_output",
|
||||
type=Union[str, Data],
|
||||
display_name="False Output",
|
||||
info="The output to return or display when the comparison is false.",
|
||||
input_types=["Text", "Data"],
|
||||
advanced=True,
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="True Result", name="true_result", method="result_response"),
|
||||
Output(display_name="False Result", name="false_result", method="result_response"),
|
||||
Output(display_name="True Result", name="true_result", method="true_response"),
|
||||
Output(display_name="False Result", name="false_result", method="false_response"),
|
||||
]
|
||||
|
||||
def true_response(self) -> Union[Text, Data]:
|
||||
self.stop("False Result")
|
||||
return self.true_output if self.true_output else self.input_text
|
||||
|
||||
def false_response(self) -> Union[Text, Data]:
|
||||
self.stop("True Result")
|
||||
return self.false_output if self.false_output else self.input_text
|
||||
|
||||
def result_response(self) -> Union[Text, Data]:
|
||||
input_text = self.input_text
|
||||
match_text = self.match_text
|
||||
operator = self.operator
|
||||
case_sensitive = self.case_sensitive
|
||||
|
||||
if not input_text or not match_text:
|
||||
raise ValueError("Both 'input_text' and 'match_text' must be provided and non-empty.")
|
||||
|
||||
def evaluate_condition(self, input_text: str, match_text: str, operator: str, case_sensitive: bool) -> bool:
|
||||
if not case_sensitive:
|
||||
input_text = input_text.lower()
|
||||
match_text = match_text.lower()
|
||||
|
||||
result = False
|
||||
if operator == "equals":
|
||||
result = input_text == match_text
|
||||
return input_text == match_text
|
||||
elif operator == "not equals":
|
||||
result = input_text != match_text
|
||||
return input_text != match_text
|
||||
elif operator == "contains":
|
||||
result = match_text in input_text
|
||||
return match_text in input_text
|
||||
elif operator == "starts with":
|
||||
result = input_text.startswith(match_text)
|
||||
return input_text.startswith(match_text)
|
||||
elif operator == "ends with":
|
||||
result = input_text.endswith(match_text)
|
||||
return input_text.endswith(match_text)
|
||||
return False
|
||||
|
||||
def true_response(self) -> Text:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if result:
|
||||
response = self.true_response()
|
||||
self.stop("false_result")
|
||||
response = self.true_output if self.true_output else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
else:
|
||||
response = self.false_response()
|
||||
self.stop("true_result")
|
||||
return ""
|
||||
|
||||
def false_response(self) -> Text:
|
||||
result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive)
|
||||
if not result:
|
||||
self.stop("true_result")
|
||||
response = self.false_output if self.false_output else self.input_text
|
||||
self.status = response
|
||||
return response
|
||||
else:
|
||||
self.stop("false_result")
|
||||
return ""
|
||||
|
|
|
|||
|
|
@ -1,29 +1,38 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.field_typing import Text
|
||||
from langflow.inputs import StrInput
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class CombineTextComponent(CustomComponent):
|
||||
class CombineTextComponent(Component):
|
||||
display_name = "Combine Text"
|
||||
description = "Concatenate two text sources into a single text chunk using a specified delimiter."
|
||||
icon = "merge"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"text1": {
|
||||
"display_name": "First Text",
|
||||
"info": "The first text input to concatenate.",
|
||||
},
|
||||
"text2": {
|
||||
"display_name": "Second Text",
|
||||
"info": "The second text input to concatenate.",
|
||||
},
|
||||
"delimiter": {
|
||||
"display_name": "Delimiter",
|
||||
"info": "A string used to separate the two text inputs. Defaults to a whitespace.",
|
||||
},
|
||||
}
|
||||
inputs = [
|
||||
StrInput(
|
||||
name="text1",
|
||||
display_name="First Text",
|
||||
info="The first text input to concatenate.",
|
||||
),
|
||||
StrInput(
|
||||
name="text2",
|
||||
display_name="Second Text",
|
||||
info="The second text input to concatenate.",
|
||||
),
|
||||
StrInput(
|
||||
name="delimiter",
|
||||
display_name="Delimiter",
|
||||
info="A string used to separate the two text inputs. Defaults to a whitespace.",
|
||||
default=" ",
|
||||
),
|
||||
]
|
||||
|
||||
def build(self, text1: str, text2: str, delimiter: str = " ") -> Text:
|
||||
combined = delimiter.join([text1, text2])
|
||||
outputs = [
|
||||
Output(display_name="Combined Text", name="combined_text", method="combine_texts"),
|
||||
]
|
||||
|
||||
def combine_texts(self) -> Text:
|
||||
combined = self.delimiter.join([self.text1, self.text2])
|
||||
self.status = combined
|
||||
return combined
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class CombineTextsUnsortedComponent(CustomComponent):
|
||||
display_name = "Combine Texts (Unsorted)"
|
||||
description = "Concatenate text sources into a single text chunk using a specified delimiter."
|
||||
icon = "merge"
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"texts": {
|
||||
"display_name": "Texts",
|
||||
"info": "The first text input to concatenate.",
|
||||
},
|
||||
"delimiter": {
|
||||
"display_name": "Delimiter",
|
||||
"info": "A string used to separate the two text inputs. Defaults to a whitespace.",
|
||||
},
|
||||
}
|
||||
|
||||
def build(self, texts: list[str], delimiter: str = " ") -> Text:
|
||||
combined = delimiter.join(texts)
|
||||
self.status = combined
|
||||
return combined
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
from langflow.custom import CustomComponent
|
||||
from langflow.field_typing import Text
|
||||
from langflow.helpers.data import data_to_text
|
||||
from langflow.schema import Data
|
||||
|
||||
|
||||
class DataToTextComponent(CustomComponent):
|
||||
display_name = "Data To Text"
|
||||
description = "Convert Data into plain text following a specified template."
|
||||
|
||||
def build_config(self):
|
||||
return {
|
||||
"data": {
|
||||
"display_name": "Data",
|
||||
"info": "The data to convert to text.",
|
||||
},
|
||||
"template": {
|
||||
"display_name": "Template",
|
||||
"info": "The template to use for formatting the data. It can contain the keys {text}, {data} or any other key in the Data.",
|
||||
"multiline": True,
|
||||
},
|
||||
}
|
||||
|
||||
def build(
|
||||
self,
|
||||
data: list[Data],
|
||||
template: str = "Text: {text}\nData: {data}",
|
||||
) -> Text:
|
||||
if not data:
|
||||
return ""
|
||||
if isinstance(data, Data):
|
||||
data = [data]
|
||||
|
||||
result_string = data_to_text(template, data)
|
||||
self.status = result_string
|
||||
return result_string
|
||||
|
|
@ -6,9 +6,10 @@ from langflow.custom import CustomComponent
|
|||
from langflow.schema import Data
|
||||
|
||||
|
||||
class DocumentToDataComponent(CustomComponent):
|
||||
display_name = "Documents To Data"
|
||||
class DocumentsToDataComponent(CustomComponent):
|
||||
display_name = "Documents ⇢ Data"
|
||||
description = "Convert LangChain Documents into Data."
|
||||
icon = "LangChain"
|
||||
|
||||
field_config = {
|
||||
"documents": {"display_name": "Documents"},
|
||||
|
|
@ -1,20 +1,28 @@
|
|||
from typing import List
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.inputs import StrInput
|
||||
from langflow.inputs import StrInput, HandleInput
|
||||
from langflow.schema import Data
|
||||
from langflow.template import Input, Output
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class FilterDataComponent(Component):
|
||||
display_name = "Filter Message"
|
||||
description = "Filters a Message object based on a list of strings."
|
||||
display_name = "Filter Data"
|
||||
description = "Filters a Data object based on a list of keys."
|
||||
icon = "filter"
|
||||
|
||||
inputs = [
|
||||
Input(name="message", display_name="Message", info="Message object to filter.", input_types=["Message"]),
|
||||
HandleInput(
|
||||
name="data",
|
||||
display_name="Data",
|
||||
info="Data object to filter.",
|
||||
input_types=["Message", "Data"],
|
||||
),
|
||||
StrInput(
|
||||
name="filter_criteria", display_name="Filter Criteria", info="List of strings to filter by.", is_list=True
|
||||
name="filter_criteria",
|
||||
display_name="Filter Criteria",
|
||||
info="List of keys to filter by.",
|
||||
is_list=True,
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -24,10 +32,12 @@ class FilterDataComponent(Component):
|
|||
|
||||
def filter_data(self) -> Data:
|
||||
filter_criteria: List[str] = self.filter_criteria
|
||||
data = self.data.data if isinstance(self.data, Data) else {}
|
||||
|
||||
# Filter the data
|
||||
filtered = {key: value for key, value in self.message.data.items() if key == filter_criteria}
|
||||
filtered = {key: value for key, value in data.items() if key in filter_criteria}
|
||||
|
||||
# Create a new Data object with the filtered data
|
||||
self.status = filtered
|
||||
return filtered
|
||||
filtered_data = Data(data=filtered)
|
||||
self.status = filtered_data
|
||||
return filtered_data
|
||||
|
|
|
|||
34
src/backend/base/langflow/components/helpers/ParseData.py
Normal file
34
src/backend/base/langflow/components/helpers/ParseData.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_text
|
||||
from langflow.field_typing import Text
|
||||
from langflow.inputs import MultilineInput, HandleInput
|
||||
from langflow.template import Output
|
||||
|
||||
|
||||
class ParseDataComponent(Component):
|
||||
display_name = "Parse Data"
|
||||
description = "Convert Data into plain text following a specified template."
|
||||
icon = "braces"
|
||||
|
||||
inputs = [
|
||||
HandleInput(
|
||||
name="data", display_name="Data", info="The data to convert to text.", input_types=["Message", "Data"]
|
||||
),
|
||||
MultilineInput(
|
||||
name="template",
|
||||
display_name="Template",
|
||||
info="The template to use for formatting the data. It can contain the keys {text}, {data} or any other key in the Data.",
|
||||
),
|
||||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Text", name="text", method="parse_data_to_text"),
|
||||
]
|
||||
|
||||
def parse_data_to_text(self) -> Text:
|
||||
data = self.data if isinstance(self.data, list) else [self.data]
|
||||
template = self.template or "Text: {text}"
|
||||
|
||||
result_string = data_to_text(template, data)
|
||||
self.status = result_string
|
||||
return result_string
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
from .CreateData import CreateDataComponent
|
||||
from .CustomComponent import Component
|
||||
from .DataToText import DataToTextComponent
|
||||
from .DocumentToData import DocumentToDataComponent
|
||||
from .ParseData import ParseDataComponent
|
||||
from .DocumentToData import DocumentsToDataComponent
|
||||
from .IDGenerator import UUIDGeneratorComponent
|
||||
from .UpdateData import UpdateDataComponent
|
||||
|
||||
__all__ = [
|
||||
"Component",
|
||||
"UpdateDataComponent",
|
||||
"DocumentToDataComponent",
|
||||
"DocumentsToDataComponent",
|
||||
"UUIDGeneratorComponent",
|
||||
"DataToTextComponent",
|
||||
"ParseDataComponent",
|
||||
"CreateDataComponent",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class ChatAntropicSpecsComponent(CustomComponent):
|
|||
self,
|
||||
model: str,
|
||||
anthropic_api_key: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
max_tokens: Optional[int] = 1000,
|
||||
temperature: Optional[float] = None,
|
||||
api_endpoint: Optional[str] = None,
|
||||
) -> BaseLanguageModel:
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ class AnthropicLLM(CustomComponent):
|
|||
self,
|
||||
model: str,
|
||||
anthropic_api_key: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
max_tokens: Optional[int] = 1000,
|
||||
temperature: Optional[float] = None,
|
||||
anthropic_api_url: Optional[str] = None,
|
||||
) -> BaseLanguageModel:
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ class AnthropicLLM(LCModelComponent):
|
|||
input_value: Text,
|
||||
system_message: Optional[str] = None,
|
||||
anthropic_api_key: Optional[str] = None,
|
||||
max_tokens: Optional[int] = None,
|
||||
max_tokens: Optional[int] = 1000,
|
||||
temperature: Optional[float] = None,
|
||||
anthropic_api_url: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
|
|
|
|||
|
|
@ -98,10 +98,15 @@ class Component(CustomComponent):
|
|||
raw = self.status
|
||||
if hasattr(raw, "data") and raw is not None:
|
||||
raw = raw.data
|
||||
if raw is None:
|
||||
raw = custom_repr
|
||||
|
||||
elif hasattr(raw, "model_dump") and raw is not None:
|
||||
raw = raw.model_dump()
|
||||
artifact_type = get_artifact_type(self.status, result)
|
||||
if raw is None and isinstance(result, (dict, Data, str)):
|
||||
raw = result.data if isinstance(result, Data) else result
|
||||
|
||||
artifact_type = get_artifact_type(self.repr_value or raw, result)
|
||||
raw = post_process_raw(raw, artifact_type)
|
||||
artifact = {"repr": custom_repr, "raw": raw, "type": artifact_type}
|
||||
_artifacts[output.name] = artifact
|
||||
|
|
@ -110,21 +115,15 @@ class Component(CustomComponent):
|
|||
return _results, _artifacts
|
||||
|
||||
def custom_repr(self):
|
||||
# ! Temporary REPR
|
||||
# Since all are dict, yaml.dump them
|
||||
if isinstance(self._results, dict):
|
||||
_build_results = recursive_serialize_or_str(self._results)
|
||||
try:
|
||||
custom_repr = yaml.dump(_build_results)
|
||||
except Exception as e:
|
||||
logger.error(f"Error while dumping build_result: {e}")
|
||||
custom_repr = str(self._results)
|
||||
|
||||
if custom_repr is None and isinstance(self._results, (dict, Data, str)):
|
||||
custom_repr = self._results
|
||||
if not isinstance(custom_repr, str):
|
||||
custom_repr = str(custom_repr)
|
||||
return custom_repr
|
||||
if self.repr_value == "":
|
||||
self.repr_value = self.status
|
||||
if isinstance(self.repr_value, dict):
|
||||
return yaml.dump(self.repr_value)
|
||||
if isinstance(self.repr_value, str):
|
||||
return self.repr_value
|
||||
if isinstance(self.repr_value, BaseModel) and not isinstance(self.repr_value, Data):
|
||||
return str(self.repr_value)
|
||||
return self.repr_value
|
||||
|
||||
def build_inputs(self, user_id: Optional[Union[str, UUID]] = None):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ class ResultData(BaseModel):
|
|||
message = values["artifacts"][key]
|
||||
|
||||
# ! Temporary fix
|
||||
if not isinstance(message, dict):
|
||||
message = {"message": message}
|
||||
if message is None:
|
||||
continue
|
||||
|
||||
if "stream_url" in message and "type" in message:
|
||||
stream_url = StreamURL(location=message["stream_url"])
|
||||
|
|
|
|||
|
|
@ -234,16 +234,15 @@ class Vertex:
|
|||
|
||||
self.has_session_id = "session_id" in template_dicts
|
||||
|
||||
self.required_inputs = [
|
||||
template_dicts[key]["type"] for key, value in template_dicts.items() if value["required"]
|
||||
]
|
||||
self.optional_inputs = [
|
||||
template_dicts[key]["type"] for key, value in template_dicts.items() if not value["required"]
|
||||
]
|
||||
# Add the template_dicts[key]["input_types"] to the optional_inputs
|
||||
self.optional_inputs.extend(
|
||||
[input_type for value in template_dicts.values() for input_type in value.get("input_types", [])]
|
||||
)
|
||||
self.required_inputs = []
|
||||
self.optional_inputs = []
|
||||
for value_dict in template_dicts.values():
|
||||
list_to_append = self.required_inputs if value_dict.get("required") else self.optional_inputs
|
||||
|
||||
if "type" in value_dict:
|
||||
list_to_append.append(value_dict["type"])
|
||||
if "input_types" in value_dict:
|
||||
list_to_append.extend(value_dict["input_types"])
|
||||
|
||||
template_dict = self.data["node"]["template"]
|
||||
self.vertex_type = (
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ from .inputs import (
|
|||
FileInput,
|
||||
FloatInput,
|
||||
IntInput,
|
||||
MultilineInput,
|
||||
NestedDictInput,
|
||||
PromptInput,
|
||||
SecretStrInput,
|
||||
StrInput,
|
||||
MultilineInput,
|
||||
HandleInput,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
|
|
@ -24,4 +25,5 @@ __all__ = [
|
|||
"FileInput",
|
||||
"PromptInput",
|
||||
"MultilineInput",
|
||||
"HandleInput",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ SerializableFieldTypes = Annotated[FieldTypes, PlainSerializer(lambda v: v.value
|
|||
class BaseInputMixin(BaseModel):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
field_type: Optional[SerializableFieldTypes] = Field(default=FieldTypes.TEXT)
|
||||
field_type: Optional[SerializableFieldTypes | str] = Field(default=FieldTypes.TEXT)
|
||||
|
||||
required: bool = False
|
||||
"""Specifies if the field is required. Defaults to False."""
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Callable, Optional, Union
|
||||
|
||||
from pydantic import Field
|
||||
from pydantic import Field, model_validator
|
||||
|
||||
from langflow.inputs.validators import StrictBoolean
|
||||
|
||||
|
|
@ -16,6 +16,17 @@ from .input_mixin import (
|
|||
)
|
||||
|
||||
|
||||
class HandleInput(BaseInputMixin):
|
||||
input_types: list[str] = Field(default_factory=list)
|
||||
field_type: Optional[str] = ""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def validate_model_type(self):
|
||||
# FieldType should be a string
|
||||
self.field_type = " | ".join(self.input_types)
|
||||
return self
|
||||
|
||||
|
||||
class PromptInput(BaseInputMixin, ListableInputMixin):
|
||||
field_type: Optional[SerializableFieldTypes] = FieldTypes.PROMPT
|
||||
|
||||
|
|
@ -24,6 +35,7 @@ class PromptInput(BaseInputMixin, ListableInputMixin):
|
|||
class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin): # noqa: F821
|
||||
field_type: Optional[SerializableFieldTypes] = FieldTypes.TEXT
|
||||
load_from_db: StrictBoolean = False
|
||||
input_types: list[str] = ["Text"]
|
||||
"""Defines if the field will allow the user to open a text editor. Default is False."""
|
||||
|
||||
|
||||
|
|
@ -83,4 +95,5 @@ InputTypes = Union[
|
|||
FileInput,
|
||||
PromptInput,
|
||||
MultilineInput,
|
||||
HandleInput,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ class Template(BaseModel):
|
|||
# first sort alphabetically
|
||||
# then sort fields so that fields that have .field_type in DIRECT_TYPES are first
|
||||
self.fields.sort(key=lambda x: x.name)
|
||||
self.fields.sort(key=lambda x: x.field_type in DIRECT_TYPES, reverse=False)
|
||||
self.fields.sort(
|
||||
key=lambda x: x.field_type in DIRECT_TYPES if hasattr(x, "field_type") else False, reverse=False
|
||||
)
|
||||
|
||||
@model_serializer(mode="wrap")
|
||||
def serialize_model(self, handler):
|
||||
|
|
|
|||
|
|
@ -302,6 +302,12 @@ export default function GenericNode({
|
|||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (hiddenOutputs && hiddenOutputs.length == 0) {
|
||||
setShowHiddenOutputs(false);
|
||||
}
|
||||
}, [hiddenOutputs]);
|
||||
|
||||
const memoizedNodeToolbarComponent = useMemo(() => {
|
||||
return (
|
||||
<NodeToolbar>
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
title: UPLOAD_ERROR_ALERT,
|
||||
list: [error],
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
}}
|
||||
>
|
||||
|
|
@ -193,7 +193,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
name="RefreshCcw"
|
||||
className="header-menu-options"
|
||||
/>
|
||||
Reload Components
|
||||
Refresh All
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
|
|
@ -221,7 +221,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
|
|||
name={isBuilding || saveLoading ? "Loader2" : "CheckCircle2"}
|
||||
className={cn(
|
||||
"h-4 w-4",
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle"
|
||||
isBuilding || saveLoading ? "animate-spin" : "animate-wiggle",
|
||||
)}
|
||||
/>
|
||||
{printByBuildStatus()}
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ export const nodeColors: { [char: string]: string } = {
|
|||
outputs: "#AA2411",
|
||||
data: "#198BF6",
|
||||
prompts: "#4367BF",
|
||||
models: "#6344BE",
|
||||
models: "#ab11ab",
|
||||
model_specs: "#6344BE",
|
||||
chains: "#FE7500",
|
||||
Document: "#7AAE42",
|
||||
|
|
@ -271,10 +271,10 @@ export const nodeColors: { [char: string]: string } = {
|
|||
Text: "#4367BF",
|
||||
retrievers: "#e6b25a",
|
||||
unknown: "#9CA3AF",
|
||||
custom_components: "#ab11ab",
|
||||
Data: "#31a3cc",
|
||||
Data: "#31a3cc",
|
||||
// custom_components: "#ab11ab",
|
||||
Data: "#9CA3AF",
|
||||
Message: "#4367BF",
|
||||
BaseLanguageModel: "#ab11ab",
|
||||
};
|
||||
|
||||
export const nodeNames: { [char: string]: string } = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue