Spaces:
Running
Running
| import operator | |
| import re | |
| from typing import Any, ClassVar | |
| from uuid import UUID | |
| from cachetools import TTLCache, cachedmethod | |
| from fastapi import HTTPException | |
| from loguru import logger | |
| from langflow.custom.attributes import ATTR_FUNC_MAPPING | |
| from langflow.custom.code_parser import CodeParser | |
| from langflow.custom.eval import eval_custom_component_code | |
| from langflow.utils import validate | |
| class ComponentCodeNullError(HTTPException): | |
| pass | |
| class ComponentFunctionEntrypointNameNullError(HTTPException): | |
| pass | |
| class BaseComponent: | |
| ERROR_CODE_NULL: ClassVar[str] = "Python code must be provided." | |
| ERROR_FUNCTION_ENTRYPOINT_NAME_NULL: ClassVar[str] = "The name of the entrypoint function must be provided." | |
| _code: str | None = None | |
| """The code of the component. Defaults to None.""" | |
| _function_entrypoint_name: str = "build" | |
| field_config: dict = {} | |
| _user_id: str | UUID | None = None | |
| _template_config: dict = {} | |
| def __init__(self, **data) -> None: | |
| self.cache: TTLCache = TTLCache(maxsize=1024, ttl=60) | |
| for key, value in data.items(): | |
| if key == "user_id": | |
| self._user_id = value | |
| else: | |
| setattr(self, key, value) | |
| def __setattr__(self, key, value) -> None: | |
| if key == "_user_id" and self._user_id is not None: | |
| logger.warning("user_id is immutable and cannot be changed.") | |
| super().__setattr__(key, value) | |
| def get_code_tree(self, code: str): | |
| parser = CodeParser(code) | |
| return parser.parse_code() | |
| def get_function(self): | |
| if not self._code: | |
| raise ComponentCodeNullError( | |
| status_code=400, | |
| detail={"error": self.ERROR_CODE_NULL, "traceback": ""}, | |
| ) | |
| if not self._function_entrypoint_name: | |
| raise ComponentFunctionEntrypointNameNullError( | |
| status_code=400, | |
| detail={ | |
| "error": self.ERROR_FUNCTION_ENTRYPOINT_NAME_NULL, | |
| "traceback": "", | |
| }, | |
| ) | |
| return validate.create_function(self._code, self._function_entrypoint_name) | |
| def get_template_config(component): | |
| """Gets the template configuration for the custom component itself.""" | |
| template_config = {} | |
| for attribute, func in ATTR_FUNC_MAPPING.items(): | |
| if hasattr(component, attribute): | |
| value = getattr(component, attribute) | |
| if value is not None: | |
| template_config[attribute] = func(value=value) | |
| for key in template_config.copy(): | |
| if key not in ATTR_FUNC_MAPPING: | |
| template_config.pop(key, None) | |
| return template_config | |
| def build_template_config(self) -> dict: | |
| """Builds the template configuration for the custom component. | |
| Returns: | |
| A dictionary representing the template configuration. | |
| """ | |
| if not self._code: | |
| return {} | |
| try: | |
| cc_class = eval_custom_component_code(self._code) | |
| except AttributeError as e: | |
| pattern = r"module '.*?' has no attribute '.*?'" | |
| if re.search(pattern, str(e)): | |
| raise ImportError(e) from e | |
| raise | |
| component_instance = cc_class(_code=self._code) | |
| return self.get_template_config(component_instance) | |
| def build(self, *args: Any, **kwargs: Any) -> Any: | |
| raise NotImplementedError | |