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] 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] = ""