From 585ae464a109b23f4d21c48c423228f5b60854ac Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Feb 2024 10:01:29 -0300 Subject: [PATCH 1/4] Add icon support to CustomComponent --- .../custom/custom_component/component.py | 4 ++- .../custom_component/custom_component.py | 27 ++++++++++++++++--- .../langflow/interface/custom/utils.py | 4 ++- .../langflow/template/frontend_node/base.py | 4 +-- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/backend/langflow/interface/custom/custom_component/component.py b/src/backend/langflow/interface/custom/custom_component/component.py index 594ec982f..8f8fa122a 100644 --- a/src/backend/langflow/interface/custom/custom_component/component.py +++ b/src/backend/langflow/interface/custom/custom_component/component.py @@ -5,7 +5,6 @@ from typing import Any, ClassVar, Optional from cachetools import TTLCache, cachedmethod from fastapi import HTTPException - from langflow.interface.custom.code_parser import CodeParser from langflow.utils import validate @@ -82,6 +81,9 @@ class Component: elif "documentation" in item_name: template_config["documentation"] = ast.literal_eval(item_value) + elif "icon" in item_name: + template_config["icon"] = ast.literal_eval(item_value) + return template_config def build(self, *args: Any, **kwargs: Any) -> Any: 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 15e7cd845..46024b1e7 100644 --- a/src/backend/langflow/interface/custom/custom_component/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component/custom_component.py @@ -2,10 +2,11 @@ import operator from typing import Any, Callable, ClassVar, List, Optional, Union from uuid import UUID +import emoji +from langflow.interface.custom.custom_component.component import Component import yaml from cachetools import TTLCache, cachedmethod from fastapi import HTTPException - from langflow.interface.custom.code_parser.utils import ( extract_inner_type_from_generic_alias, extract_union_types_from_generic_alias, @@ -15,26 +16,46 @@ from langflow.services.database.utils import session_getter from langflow.services.deps import get_credential_service, get_db_service from langflow.utils import validate -from .component import Component - class CustomComponent(Component): display_name: Optional[str] = None + """The display name of the component. Defaults to None.""" description: Optional[str] = None + """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.""" code: Optional[str] = None + """The code of the component. Defaults to None.""" field_config: dict = {} + """The field configuration of the component. Defaults to an empty dictionary.""" code_class_base_inheritance: ClassVar[str] = "CustomComponent" function_entrypoint_name: ClassVar[str] = "build" function: Optional[Callable] = None repr_value: Optional[Any] = "" 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): self.cache = TTLCache(maxsize=1024, ttl=60) super().__init__(**data) + # Validate the emoji at the icon field + if self.icon: + self.icon = self.validate_icon(self.icon) + + def validate_icon(self, value: str): + # we are going to use the emoji library to validate the emoji + # emojis can be defined using the :emoji_name: syntax + if not value.startswith(":") or not value.endswith(":"): + raise ValueError("Invalid emoji. Please use the :emoji_name: syntax.") + + emoji_value = emoji.emojize(value) + if value == emoji_value: + raise ValueError(f"Invalid emoji. {value} is not a valid emoji.") + return emoji_value + def custom_repr(self): if self.repr_value == "": self.repr_value = self.status diff --git a/src/backend/langflow/interface/custom/utils.py b/src/backend/langflow/interface/custom/utils.py index e6125b515..1a8bdc91f 100644 --- a/src/backend/langflow/interface/custom/utils.py +++ b/src/backend/langflow/interface/custom/utils.py @@ -7,6 +7,8 @@ from typing import Any, Dict, List, Optional, Union from uuid import UUID from fastapi import HTTPException +from loguru import logger + from langflow.field_typing.range_spec import RangeSpec from langflow.interface.custom.code_parser.utils import extract_inner_type from langflow.interface.custom.custom_component import CustomComponent @@ -19,7 +21,6 @@ from langflow.interface.importing.utils import eval_custom_component_code from langflow.template.field.base import TemplateField from langflow.template.frontend_node.custom_components import CustomComponentFrontendNode from langflow.utils.util import get_base_classes -from loguru import logger def add_output_types(frontend_node: CustomComponentFrontendNode, return_types: List[str]): @@ -231,6 +232,7 @@ def sanitize_template_config(template_config): "beta", "documentation", "output_types", + "icon", } for key in template_config.copy(): if key not in attributes: diff --git a/src/backend/langflow/template/frontend_node/base.py b/src/backend/langflow/template/frontend_node/base.py index 6379dd358..daef4f9a8 100644 --- a/src/backend/langflow/template/frontend_node/base.py +++ b/src/backend/langflow/template/frontend_node/base.py @@ -2,13 +2,12 @@ import re from collections import defaultdict from typing import ClassVar, Dict, List, Optional, Union -from pydantic import BaseModel, Field, field_serializer, model_serializer - from langflow.template.field.base import TemplateField from langflow.template.frontend_node.constants import CLASSES_TO_REMOVE, FORCE_SHOW_FIELDS from langflow.template.frontend_node.formatter import field_formatters from langflow.template.template.base import Template from langflow.utils import constants +from pydantic import BaseModel, Field, field_serializer, model_serializer class FieldFormatters(BaseModel): @@ -43,6 +42,7 @@ class FrontendNode(BaseModel): _format_template: bool = True template: Template description: Optional[str] = None + icon: Optional[str] = None base_classes: List[str] name: str = "" display_name: Optional[str] = "" From f48b4468f6ae5ca5f8251ff88694bcd1c769d239 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Feb 2024 10:02:33 -0300 Subject: [PATCH 2/4] Add icon property to APIClassType --- src/frontend/src/types/api/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/types/api/index.ts b/src/frontend/src/types/api/index.ts index 16c840e37..46e62376c 100644 --- a/src/frontend/src/types/api/index.ts +++ b/src/frontend/src/types/api/index.ts @@ -17,6 +17,7 @@ export type APIClassType = { description: string; template: APITemplateType; display_name: string; + icon?: string; input_types?: Array; output_types?: Array; custom_fields?: CustomFieldsType; From d8522f82364be32f989de108e315ac6d53514ea7 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Thu, 8 Feb 2024 10:12:31 -0300 Subject: [PATCH 3/4] Add emoji validation to icon field in custom components --- .../custom/custom_component/component.py | 19 ++++++++++++++++++- .../custom_component/custom_component.py | 19 ++----------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/backend/langflow/interface/custom/custom_component/component.py b/src/backend/langflow/interface/custom/custom_component/component.py index 8f8fa122a..e2d84ed70 100644 --- a/src/backend/langflow/interface/custom/custom_component/component.py +++ b/src/backend/langflow/interface/custom/custom_component/component.py @@ -3,6 +3,7 @@ import operator import warnings from typing import Any, ClassVar, Optional +import emoji from cachetools import TTLCache, cachedmethod from fastapi import HTTPException from langflow.interface.custom.code_parser import CodeParser @@ -34,6 +35,10 @@ class Component: else: setattr(self, key, value) + # Validate the emoji at the icon field + if self.icon: + self.icon = self.validate_icon(self.icon) + def __setattr__(self, key, value): if key == "_user_id" and hasattr(self, "_user_id"): warnings.warn("user_id is immutable and cannot be changed.") @@ -82,9 +87,21 @@ class Component: template_config["documentation"] = ast.literal_eval(item_value) elif "icon" in item_name: - template_config["icon"] = ast.literal_eval(item_value) + icon_str = ast.literal_eval(item_value) + template_config["icon"] = self.validate_icon(icon_str) return template_config + def validate_icon(self, value: str): + # we are going to use the emoji library to validate the emoji + # emojis can be defined using the :emoji_name: syntax + if not value.startswith(":") or not value.endswith(":"): + raise ValueError("Invalid emoji. Please use the :emoji_name: syntax.") + + emoji_value = emoji.emojize(value, variant="emoji_type") + if value == emoji_value: + raise ValueError(f"Invalid emoji. {value} is not a valid emoji.") + return emoji_value + def build(self, *args: Any, **kwargs: Any) -> Any: raise NotImplementedError 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 46024b1e7..f4cbf5ea5 100644 --- a/src/backend/langflow/interface/custom/custom_component/custom_component.py +++ b/src/backend/langflow/interface/custom/custom_component/custom_component.py @@ -2,15 +2,15 @@ import operator from typing import Any, Callable, ClassVar, List, Optional, Union from uuid import UUID -import emoji -from langflow.interface.custom.custom_component.component import Component import yaml from cachetools import TTLCache, cachedmethod from fastapi import HTTPException + from langflow.interface.custom.code_parser.utils import ( extract_inner_type_from_generic_alias, extract_union_types_from_generic_alias, ) +from langflow.interface.custom.custom_component.component import Component from langflow.services.database.models.flow import Flow from langflow.services.database.utils import session_getter from langflow.services.deps import get_credential_service, get_db_service @@ -41,21 +41,6 @@ class CustomComponent(Component): self.cache = TTLCache(maxsize=1024, ttl=60) super().__init__(**data) - # Validate the emoji at the icon field - if self.icon: - self.icon = self.validate_icon(self.icon) - - def validate_icon(self, value: str): - # we are going to use the emoji library to validate the emoji - # emojis can be defined using the :emoji_name: syntax - if not value.startswith(":") or not value.endswith(":"): - raise ValueError("Invalid emoji. Please use the :emoji_name: syntax.") - - emoji_value = emoji.emojize(value) - if value == emoji_value: - raise ValueError(f"Invalid emoji. {value} is not a valid emoji.") - return emoji_value - def custom_repr(self): if self.repr_value == "": self.repr_value = self.status From b9570b9c321ea4b5af79c7f854c104e5bef971c3 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 8 Feb 2024 18:06:59 -0300 Subject: [PATCH 4/4] add emoji icon --- .../src/CustomNodes/GenericNode/index.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index e8a95fb1b..b8d8f5a9d 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -156,14 +156,19 @@ export default function GenericNode({ (!showNode && "justify-center") } > - + {data?.node?.icon ? ( + {data?.node?.icon} + ) : ( + + )} + {showNode && (
{nameEditable && inputName ? (