diff --git a/src/backend/langflow/interface/custom/custom_component/component.py b/src/backend/langflow/interface/custom/custom_component/component.py
index 594ec982f..e2d84ed70 100644
--- a/src/backend/langflow/interface/custom/custom_component/component.py
+++ b/src/backend/langflow/interface/custom/custom_component/component.py
@@ -3,9 +3,9 @@ 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
from langflow.utils import validate
@@ -35,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,7 +86,22 @@ class Component:
elif "documentation" in item_name:
template_config["documentation"] = ast.literal_eval(item_value)
+ elif "icon" in item_name:
+ 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 15e7cd845..f4cbf5ea5 100644
--- a/src/backend/langflow/interface/custom/custom_component/custom_component.py
+++ b/src/backend/langflow/interface/custom/custom_component/custom_component.py
@@ -10,25 +10,31 @@ 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
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):
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] = ""
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")
}
>
-