from enum import Enum from typing import Annotated, Any from pydantic import ( BaseModel, ConfigDict, Field, PlainSerializer, field_validator, model_serializer, ) from langflow.field_typing.range_spec import RangeSpec from langflow.inputs.validators import CoalesceBool from langflow.schema.table import Column, TableSchema class FieldTypes(str, Enum): TEXT = "str" INTEGER = "int" PASSWORD = "str" # noqa: PIE796, S105 FLOAT = "float" BOOLEAN = "bool" DICT = "dict" NESTED_DICT = "NestedDict" FILE = "file" PROMPT = "prompt" CODE = "code" OTHER = "other" TABLE = "table" LINK = "link" SLIDER = "slider" SerializableFieldTypes = Annotated[FieldTypes, PlainSerializer(lambda v: v.value, return_type=str)] # Base mixin for common input field attributes and methods class BaseInputMixin(BaseModel, validate_assignment=True): # type: ignore[call-arg] model_config = ConfigDict( arbitrary_types_allowed=True, extra="forbid", populate_by_name=True, ) field_type: SerializableFieldTypes = Field(default=FieldTypes.TEXT, alias="type") required: bool = False """Specifies if the field is required. Defaults to False.""" placeholder: str = "" """A placeholder string for the field. Default is an empty string.""" show: bool = True """Should the field be shown. Defaults to True.""" name: str = Field(description="Name of the field.") """Name of the field. Default is an empty string.""" value: Any = "" """The value of the field. Default is an empty string.""" display_name: str | None = None """Display name of the field. Defaults to None.""" advanced: bool = False """Specifies if the field will an advanced parameter (hidden). Defaults to False.""" input_types: list[str] | None = None """List of input types for the handle when the field has more than one type. Default is an empty list.""" dynamic: bool = False """Specifies if the field is dynamic. Defaults to False.""" info: str | None = "" """Additional information about the field to be shown in the tooltip. Defaults to an empty string.""" real_time_refresh: bool | None = None """Specifies if the field should have real time refresh. `refresh_button` must be False. Defaults to None.""" refresh_button: bool | None = None """Specifies if the field should have a refresh button. Defaults to False.""" refresh_button_text: str | None = None """Specifies the text for the refresh button. Defaults to None.""" title_case: bool = False """Specifies if the field should be displayed in title case. Defaults to True.""" def to_dict(self): return self.model_dump(exclude_none=True, by_alias=True) @field_validator("field_type", mode="before") @classmethod def validate_field_type(cls, v): try: return FieldTypes(v) except ValueError: return FieldTypes.OTHER @model_serializer(mode="wrap") def serialize_model(self, handler): dump = handler(self) if "field_type" in dump: dump["type"] = dump.pop("field_type") dump["_input_type"] = self.__class__.__name__ return dump class ToolModeMixin(BaseModel): tool_mode: bool = False class InputTraceMixin(BaseModel): trace_as_input: bool = True class MetadataTraceMixin(BaseModel): trace_as_metadata: bool = True # Mixin for input fields that can be listable class ListableInputMixin(BaseModel): is_list: bool = Field(default=False, alias="list") # Specific mixin for fields needing database interaction class DatabaseLoadMixin(BaseModel): load_from_db: bool = Field(default=True) # Specific mixin for fields needing file interaction class FileMixin(BaseModel): file_path: str | None = Field(default="") file_types: list[str] = Field(default=[], alias="fileTypes") @field_validator("file_types") @classmethod def validate_file_types(cls, v): if not isinstance(v, list): msg = "file_types must be a list" raise ValueError(msg) # noqa: TRY004 # types should be a list of extensions without the dot for file_type in v: if not isinstance(file_type, str): msg = "file_types must be a list of strings" raise ValueError(msg) # noqa: TRY004 if file_type.startswith("."): msg = "file_types should not start with a dot" raise ValueError(msg) return v class RangeMixin(BaseModel): range_spec: RangeSpec | None = None class DropDownMixin(BaseModel): options: list[str] | None = None """List of options for the field. Only used when is_list=True. Default is an empty list.""" combobox: CoalesceBool = False """Variable that defines if the user can insert custom values in the dropdown.""" class MultilineMixin(BaseModel): multiline: CoalesceBool = True class LinkMixin(BaseModel): icon: str | None = None """Icon to be displayed in the link.""" text: str | None = None """Text to be displayed in the link.""" class SliderMixin(BaseModel): min_label: str = Field(default="") max_label: str = Field(default="") min_label_icon: str = Field(default="") max_label_icon: str = Field(default="") slider_buttons: bool = Field(default=False) slider_buttons_options: list[str] = Field(default=[]) slider_input: bool = Field(default=False) class TableMixin(BaseModel): table_schema: TableSchema | list[Column] | None = None @field_validator("table_schema") @classmethod def validate_table_schema(cls, v): if isinstance(v, list) and all(isinstance(column, Column) for column in v): return TableSchema(columns=v) if isinstance(v, TableSchema): return v msg = "table_schema must be a TableSchema or a list of Columns" raise ValueError(msg)