diff --git a/src/backend/langflow/interface/custom/attributes.py b/src/backend/langflow/interface/custom/attributes.py index 8ec2a0daa..d3119cd3d 100644 --- a/src/backend/langflow/interface/custom/attributes.py +++ b/src/backend/langflow/interface/custom/attributes.py @@ -37,4 +37,6 @@ ATTR_FUNC_MAPPING = { "documentation": getattr_return_str, "icon": validate_icon, "pinned": getattr_return_bool, + "is_input": getattr_return_bool, + "is_output": getattr_return_bool, } diff --git a/src/backend/langflow/interface/custom/custom_component/custom_component.py b/src/backend/langflow/interface/custom/custom_component/custom_component.py index 7d8794878..549c0dad3 100644 --- a/src/backend/langflow/interface/custom/custom_component/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component/custom_component.py @@ -35,6 +35,7 @@ from langflow.utils import validate if TYPE_CHECKING: from langflow.graph.edge.base import ContractEdge + from langflow.graph.vertex.base import Vertex class CustomComponent(Component): @@ -44,6 +45,12 @@ class CustomComponent(Component): """The description of the component. Defaults to None.""" icon: Optional[str] = None """The icon of the component. It should be an emoji. Defaults to None.""" + is_input: Optional[bool] = None + """The input state of the component. Defaults to None. + If True, the component must have a field named 'message'.""" + is_output: Optional[bool] = None + """The output state of the component. Defaults to None. + If True, the component must have a field named 'message'.""" code: Optional[str] = None """The code of the component. Defaults to None.""" field_config: dict = {} @@ -56,7 +63,7 @@ class CustomComponent(Component): """The build parameters of the component. Defaults to None.""" selected_output_type: Optional[str] = None """The selected output type of the component. Defaults to None.""" - outgoing_edges: Optional[List["ContractEdge"]] = None + vertex: Optional["Vertex"] = None """The edge target parameter of the component. Defaults to None.""" code_class_base_inheritance: ClassVar[str] = "CustomComponent" function_entrypoint_name: ClassVar[str] = "build" @@ -65,6 +72,7 @@ class CustomComponent(Component): user_id: Optional[Union[UUID, str]] = None status: Optional[Any] = None """The status of the component. This is displayed on the frontend. Defaults to None.""" + _tree: Optional[dict] = None def __init__(self, **data): diff --git a/src/backend/langflow/template/frontend_node/base.py b/src/backend/langflow/template/frontend_node/base.py index 8b400b849..9f62c7054 100644 --- a/src/backend/langflow/template/frontend_node/base.py +++ b/src/backend/langflow/template/frontend_node/base.py @@ -47,6 +47,12 @@ class FrontendNode(BaseModel): """Description of the frontend node.""" icon: Optional[str] = None """Icon of the frontend node.""" + is_input: Optional[bool] = None + """Whether the frontend node is used as an input when processing the Graph. + If True, there should be a field named 'message'.""" + is_output: Optional[bool] = None + """Whether the frontend node is used as an output when processing the Graph. + If True, there should be a field named 'message'.""" is_composition: Optional[bool] = None """Whether the frontend node is used for composition.""" base_classes: List[str] @@ -165,7 +171,9 @@ class FrontendNode(BaseModel): return _type @staticmethod - def handle_special_field(field, key: str, _type: str, SPECIAL_FIELD_HANDLERS) -> str: + def handle_special_field( + field, key: str, _type: str, SPECIAL_FIELD_HANDLERS + ) -> str: """Handles special field by using the respective handler if present.""" handler = SPECIAL_FIELD_HANDLERS.get(key) return handler(field) if handler else _type @@ -176,7 +184,11 @@ class FrontendNode(BaseModel): if "dict" in _type.lower() and field.name == "dict_": field.field_type = "file" field.file_types = [".json", ".yaml", ".yml"] - elif _type.startswith("Dict") or _type.startswith("Mapping") or _type.startswith("dict"): + elif ( + _type.startswith("Dict") + or _type.startswith("Mapping") + or _type.startswith("dict") + ): field.field_type = "dict" return _type @@ -187,7 +199,9 @@ class FrontendNode(BaseModel): field.value = value["default"] @staticmethod - def handle_specific_field_values(field: TemplateField, key: str, name: Optional[str] = None) -> None: + def handle_specific_field_values( + field: TemplateField, key: str, name: Optional[str] = None + ) -> None: """Handles specific field values for certain fields.""" if key == "headers": field.value = """{"Authorization": "Bearer "}""" @@ -195,7 +209,9 @@ class FrontendNode(BaseModel): FrontendNode._handle_api_key_specific_field_values(field, key, name) @staticmethod - def _handle_model_specific_field_values(field: TemplateField, key: str, name: Optional[str] = None) -> None: + def _handle_model_specific_field_values( + field: TemplateField, key: str, name: Optional[str] = None + ) -> None: """Handles specific field values related to models.""" model_dict = { "OpenAI": constants.OPENAI_MODELS, @@ -208,7 +224,9 @@ class FrontendNode(BaseModel): field.is_list = True @staticmethod - def _handle_api_key_specific_field_values(field: TemplateField, key: str, name: Optional[str] = None) -> None: + def _handle_api_key_specific_field_values( + field: TemplateField, key: str, name: Optional[str] = None + ) -> None: """Handles specific field values related to API keys.""" if "api_key" in key and "OpenAI" in str(name): field.display_name = "OpenAI API Key" @@ -248,7 +266,10 @@ class FrontendNode(BaseModel): @staticmethod def should_be_password(key: str, show: bool) -> bool: """Determines whether the field should be a password field.""" - return any(text in key.lower() for text in {"password", "token", "api", "key"}) and show + return ( + any(text in key.lower() for text in {"password", "token", "api", "key"}) + and show + ) @staticmethod def should_be_multiline(key: str) -> bool: diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index 59b8a5ca5..3e9f4a0f5 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -18,6 +18,8 @@ export type APIClassType = { template: APITemplateType; display_name: string; icon?: string; + is_input?: boolean; + is_output?: boolean; input_types?: Array; output_types?: Array; custom_fields?: CustomFieldsType;