diff --git a/src/backend/base/langflow/inputs/inputs.py b/src/backend/base/langflow/inputs/inputs.py index a5470fd6c..cc932d52a 100644 --- a/src/backend/base/langflow/inputs/inputs.py +++ b/src/backend/base/langflow/inputs/inputs.py @@ -30,7 +30,7 @@ from .input_mixin import ( ) -class TableInput(BaseInputMixin, MetadataTraceMixin, TableMixin, ListableInputMixin): +class TableInput(BaseInputMixin, MetadataTraceMixin, TableMixin, ListableInputMixin, ToolModeMixin): field_type: SerializableFieldTypes = FieldTypes.TABLE is_list: bool = True @@ -91,7 +91,7 @@ class CodeInput(BaseInputMixin, ListableInputMixin, InputTraceMixin): # Applying mixins to a specific input type -class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTraceMixin): +class StrInput(BaseInputMixin, ListableInputMixin, DatabaseLoadMixin, MetadataTraceMixin, ToolModeMixin): field_type: SerializableFieldTypes = FieldTypes.TEXT load_from_db: CoalesceBool = False """Defines if the field will allow the user to open a text editor. Default is False.""" @@ -299,7 +299,7 @@ class SecretStrInput(BaseInputMixin, DatabaseLoadMixin): return value -class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixin): +class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixin, ToolModeMixin): """Represents an integer field. This class represents an integer input and provides functionality for handling integer values. @@ -334,7 +334,7 @@ class IntInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixi return v -class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixin): +class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMixin, ToolModeMixin): """Represents a float field. This class represents a float input and provides functionality for handling float values. @@ -369,7 +369,7 @@ class FloatInput(BaseInputMixin, ListableInputMixin, RangeMixin, MetadataTraceMi return v -class BoolInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin): +class BoolInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin, ToolModeMixin): """Represents a boolean field. This class represents a boolean input and provides functionality for handling boolean values. @@ -384,7 +384,7 @@ class BoolInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin): value: CoalesceBool = False -class NestedDictInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin, InputTraceMixin): +class NestedDictInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin, InputTraceMixin, ToolModeMixin): """Represents a nested dictionary field. This class represents a nested dictionary input and provides functionality for handling dictionary values. @@ -399,7 +399,7 @@ class NestedDictInput(BaseInputMixin, ListableInputMixin, MetadataTraceMixin, In value: dict | Data | None = {} -class DictInput(BaseInputMixin, ListableInputMixin, InputTraceMixin): +class DictInput(BaseInputMixin, ListableInputMixin, InputTraceMixin, ToolModeMixin): """Represents a dictionary field. This class represents a dictionary input and provides functionality for handling dictionary values. @@ -431,7 +431,7 @@ class DropdownInput(BaseInputMixin, DropDownMixin, MetadataTraceMixin, ToolModeM combobox: CoalesceBool = False -class MultiselectInput(BaseInputMixin, ListableInputMixin, DropDownMixin, MetadataTraceMixin): +class MultiselectInput(BaseInputMixin, ListableInputMixin, DropDownMixin, MetadataTraceMixin, ToolModeMixin): """Represents a multiselect input field. This class represents a multiselect input field and provides functionality for handling multiselect values. @@ -479,7 +479,7 @@ class LinkInput(BaseInputMixin, LinkMixin): field_type: SerializableFieldTypes = FieldTypes.LINK -class SliderInput(BaseInputMixin, RangeMixin, SliderMixin): +class SliderInput(BaseInputMixin, RangeMixin, SliderMixin, ToolModeMixin): field_type: SerializableFieldTypes = FieldTypes.SLIDER diff --git a/src/backend/tests/unit/base/tools/test_toolmodemixin.py b/src/backend/tests/unit/base/tools/test_toolmodemixin.py new file mode 100644 index 000000000..13c2081b5 --- /dev/null +++ b/src/backend/tests/unit/base/tools/test_toolmodemixin.py @@ -0,0 +1,155 @@ +from langflow.base.tools.component_tool import ComponentToolkit +from langflow.custom import Component + +# Import all input types +from langflow.io import ( + BoolInput, + DataFrameInput, + DataInput, + DictInput, + DropdownInput, + FloatInput, + IntInput, + MessageInput, + MessageTextInput, + MultilineInput, + MultiselectInput, + NestedDictInput, + Output, + PromptInput, + StrInput, + TableInput, +) +from langflow.schema import Data +from pydantic import BaseModel + + +class AllInputsComponent(Component): + display_name = "All Inputs Component" + description = "A component with all input types available in Langflow." + documentation: str = "http://docs.langflow.org/components/all_inputs" + icon = "code" + name = "AllInputsComponent" + + inputs = [ + TableInput( + name="table_input", + display_name="Table Input", + info="Input for table data.", + value=[], + tool_mode=True, + table_schema=[ + {"name": "id", "type": "int"}, + {"name": "name", "type": "str"}, + ], + ), + DataInput(name="data_input", display_name="Data Input", info="Input for data objects.", tool_mode=True), + DataFrameInput( + name="dataframe_input", display_name="DataFrame Input", info="Input for DataFrame objects.", tool_mode=True + ), + PromptInput(name="prompt_input", display_name="Prompt Input", info="Input for prompt data.", tool_mode=True), + StrInput(name="str_input", display_name="String Input", info="Input for string data.", tool_mode=True), + MessageInput( + name="message_input", display_name="Message Input", info="Input for message objects.", tool_mode=True + ), + MessageTextInput( + name="message_text_input", display_name="Message Text Input", info="Input for message text.", tool_mode=True + ), + MultilineInput( + name="multiline_input", display_name="Multiline Input", info="Input for multiline text.", tool_mode=True + ), + IntInput(name="int_input", display_name="Integer Input", info="Input for integer values.", tool_mode=True), + FloatInput(name="float_input", display_name="Float Input", info="Input for float values.", tool_mode=True), + BoolInput(name="bool_input", display_name="Boolean Input", info="Input for boolean values.", tool_mode=True), + NestedDictInput( + name="nested_dict_input", + display_name="Nested Dictionary Input", + info="Input for nested dictionary data.", + tool_mode=True, + value={"key1": "value1", "key2": "value2"}, + ), + DictInput( + name="dict_input", + display_name="Dictionary Input", + info="Input for dictionary data.", + tool_mode=True, + is_list=True, + value={"key1": "value1", "key2": "value2"}, + ), + DropdownInput( + name="dropdown_input", + display_name="Dropdown Input", + info="Input for dropdown selections.", + tool_mode=True, + options=["option1", "option2", "option3"], + value="option1", + ), + MultiselectInput( + name="multiselect_input", + display_name="Multiselect Input", + info="Input for multiple selections.", + tool_mode=True, + options=["option1", "option2", "option3"], + value=["option1", "option2"], + ), + ] + + outputs = [ + Output(display_name="Output", name="output", method="build_output"), + ] + + def build_output(self) -> Data: + # Example logic to process inputs and produce an output + data_dict = { + "table_input": self.table_input, + "data_input": self.data_input, + "dataframe_input": self.dataframe_input, + "prompt_input": self.prompt_input, + "str_input": self.str_input, + "message_input": self.message_input, + "message_text_input": self.message_text_input, + "multiline_input": self.multiline_input, + "int_input": self.int_input, + "float_input": self.float_input, + "bool_input": self.bool_input, + } + data = Data(value=data_dict) + self.status = data + return data + + +async def test_component_inputs_toolkit(): + component = AllInputsComponent() + component_toolkit = ComponentToolkit(component=component) + component_tool = component_toolkit.get_tools()[0] + assert component_tool.name == "AllInputsComponent-build_output" + assert issubclass(component_tool.args_schema, BaseModel) + properties = component_tool.args_schema.model_json_schema()["properties"] + + # Define expected properties based on the component's inputs + expected_inputs = { + "table_input": {"title": "Table Input", "description": "Input for table data."}, + "data_input": {"title": "Data Input", "description": "Input for data objects."}, + "dataframe_input": {"title": "Dataframe Input", "description": "Input for DataFrame objects."}, + "prompt_input": {"title": "Prompt Input", "description": "Input for prompt data."}, + "str_input": {"title": "Str Input", "description": "Input for string data."}, + "message_input": {"title": "Message Input", "description": "Input for message objects."}, + "message_text_input": {"title": "Message Text Input", "description": "Input for message text."}, + "multiline_input": {"title": "Multiline Input", "description": "Input for multiline text."}, + # TODO: to check how the title is generated, Shouldnt it be the display name? + "int_input": {"title": "Int Input", "description": "Input for integer values."}, + "float_input": {"title": "Float Input", "description": "Input for float values."}, + "bool_input": {"title": "Bool Input", "description": "Input for boolean values."}, + "nested_dict_input": {"title": "Nested Dict Input", "description": "Input for nested dictionary data."}, + "dict_input": {"title": "Dict Input", "description": "Input for dictionary data."}, + "dropdown_input": {"title": "Dropdown Input", "description": "Input for dropdown selections."}, + "multiselect_input": {"title": "Multiselect Input", "description": "Input for multiple selections."}, + } + + # Iterate and assert each input's properties + for input_name, expected in expected_inputs.items(): + assert input_name in properties, f"{input_name} is missing in properties." + assert properties[input_name]["title"] == expected["title"], f"Title mismatch for {input_name}." + assert ( + properties[input_name]["description"] == expected["description"] + ), f"Description mismatch for {input_name}."