Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
db7d221
Create convertor.py
edwinjosechittilappilly Apr 23, 2025
0d8388b
Merge branch 'main' into convert_component
edwinjosechittilappilly Apr 24, 2025
52d667c
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 24, 2025
a12ef7f
Merge branch 'main' into convert_component
edwinjosechittilappilly Apr 24, 2025
4658c14
convert component
edwinjosechittilappilly Apr 24, 2025
bf30499
Merge branch 'convert_component' of https://github.com/langflow-ai/la…
edwinjosechittilappilly Apr 24, 2025
c3164f9
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 24, 2025
588fc41
add Type_conversion base class with dispatchers for performance based…
edwinjosechittilappilly Apr 29, 2025
765d40f
fix lint issues
edwinjosechittilappilly Apr 29, 2025
e65c60e
add type_convertor test
edwinjosechittilappilly Apr 29, 2025
87ef657
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 29, 2025
3dd3307
update tests
edwinjosechittilappilly Apr 29, 2025
2c0ba21
Merge branch 'convert_component' of https://github.com/langflow-ai/la…
edwinjosechittilappilly Apr 29, 2025
6ab11ff
Merge branch 'main' into convert_component
edwinjosechittilappilly Apr 29, 2025
00a4b16
Merge branch 'convert_component' of https://github.com/langflow-ai/la…
edwinjosechittilappilly Apr 29, 2025
7ff74ae
fix tests
edwinjosechittilappilly May 9, 2025
ec023e2
Merge branch 'main' into convert_component
edwinjosechittilappilly May 9, 2025
c02b3ef
Merge branch 'main' into convert_component
edwinjosechittilappilly May 12, 2025
c555554
update with auto conversion methods
edwinjosechittilappilly May 15, 2025
2c97cab
Merge branch 'main' into convert_component
edwinjosechittilappilly May 16, 2025
db08285
update function to component file
edwinjosechittilappilly May 19, 2025
b4e83cc
Merge branch 'main' into convert_component
edwinjosechittilappilly May 20, 2025
5343410
Merge branch 'main' into convert_component
edwinjosechittilappilly May 22, 2025
b779bd3
Merge branch 'main' into convert_component
edwinjosechittilappilly May 22, 2025
78b0701
Merge branch 'main' into convert_component
edwinjosechittilappilly May 29, 2025
d00eb5c
feat: enhance input validation for Data, DataFrame, and Message types
ogabrielluiz Apr 2, 2025
357d1e3
test: add unit tests for DataInput, MessageInput, and DataFrameInput …
ogabrielluiz Apr 2, 2025
f0ff9df
updated changes to use type classes
edwinjosechittilappilly May 29, 2025
13a8d70
[autofix.ci] apply automated fixes
autofix-ci[bot] May 29, 2025
08136b9
add convert logic
edwinjosechittilappilly May 29, 2025
7705c72
update to converter
edwinjosechittilappilly May 29, 2025
bd01612
update converts
edwinjosechittilappilly May 29, 2025
61370c1
Update converter.py
edwinjosechittilappilly May 29, 2025
d1a62ef
Merge branch 'main' into convert_component
edwinjosechittilappilly May 29, 2025
6e108ae
[autofix.ci] apply automated fixes
autofix-ci[bot] May 29, 2025
484faf6
revert converter.py
edwinjosechittilappilly May 30, 2025
54d66f5
Update inputs.py
edwinjosechittilappilly May 30, 2025
33f467b
Update test_inputs.py
edwinjosechittilappilly May 30, 2025
8613a67
update to logic
edwinjosechittilappilly May 30, 2025
9475ff6
Update test_type_convertor_component.py
edwinjosechittilappilly May 30, 2025
19dcfdc
update converter
edwinjosechittilappilly May 30, 2025
f7ef291
Merge branch 'main' into convert_component
edwinjosechittilappilly May 30, 2025
5953a6b
[autofix.ci] apply automated fixes
autofix-ci[bot] May 30, 2025
9d2ffae
Merge branch 'main' into convert_component
carlosrcoelho Jun 2, 2025
3903d7b
refactor: rename conversion functions for clarity
ogabrielluiz Jun 2, 2025
835b7ce
fix: add TYPE_CHECKING for conditional imports in message.py
ogabrielluiz Jun 2, 2025
821f9f0
refactor: simplify data conversion methods in Message class
ogabrielluiz Jun 2, 2025
5a2f9f6
refactor: enhance DataFrame methods for clarity and type safety
ogabrielluiz Jun 2, 2025
b7314f0
refactor: streamline Data class methods for improved clarity
ogabrielluiz Jun 2, 2025
53af8db
refactor: simplify conversion method calls by removing redundant argu…
ogabrielluiz Jun 2, 2025
84ed405
rename test file
ogabrielluiz Jun 2, 2025
dc498f6
refactor: remove obsolete test file for data conversion
ogabrielluiz Jun 2, 2025
619cd6a
refactor: add support for converting dictionary to DataFrame
ogabrielluiz Jun 2, 2025
1d8fc2c
Merge branch 'main' into convert_component
Yukiyukiyeah Jun 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
2 changes: 2 additions & 0 deletions src/backend/base/langflow/components/processing/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from .alter_metadata import AlterMetadataComponent
from .combine_text import CombineTextComponent
from .converter import TypeConverterComponent
from .create_data import CreateDataComponent
from .data_operations import DataOperationsComponent
from .extract_key import ExtractDataKeyComponent
Expand Down Expand Up @@ -38,5 +39,6 @@
"RegexExtractorComponent",
"SelectDataComponent",
"SplitTextComponent",
"TypeConverterComponent",
"UpdateDataComponent",
]
107 changes: 107 additions & 0 deletions src/backend/base/langflow/components/processing/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
from typing import Any

from langflow.custom import Component
from langflow.io import HandleInput, Output, TabInput
from langflow.schema import Data, DataFrame, Message


def convert_to_message(v) -> Message:
"""Convert input to Message type.

Args:
v: Input to convert (Message, Data, DataFrame, or dict)

Returns:
Message: Converted Message object
"""
return v if isinstance(v, Message) else v.to_message()


def convert_to_data(v: DataFrame | Data | Message | dict) -> Data:
"""Convert input to Data type.

Args:
v: Input to convert (Message, Data, DataFrame, or dict)

Returns:
Data: Converted Data object
"""
if isinstance(v, dict):
return Data(v)
return v if isinstance(v, Data) else v.to_data()


def convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:
"""Convert input to DataFrame type.

Args:
v: Input to convert (Message, Data, DataFrame, or dict)

Returns:
DataFrame: Converted DataFrame object
"""
if isinstance(v, dict):
return DataFrame([v])
return v if isinstance(v, DataFrame) else v.to_dataframe()


class TypeConverterComponent(Component):
display_name = "Type Convert"
description = "Convert between different types (Message, Data, DataFrame)"
icon = "repeat"

inputs = [
HandleInput(
name="input_data",
display_name="Input",
input_types=["Message", "Data", "DataFrame"],
info="Accept Message, Data or DataFrame as input",
required=True,
),
TabInput(
name="output_type",
display_name="Output Type",
options=["Message", "Data", "DataFrame"],
info="Select the desired output data type",
real_time_refresh=True,
value="Message",
),
]

outputs = [Output(display_name="Message Output", name="message_output", method="convert_to_message")]

Comment on lines +48 to +72
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add class docstring and improve component metadata.

The component class needs documentation and could benefit from better metadata.

 class TypeConverterComponent(Component):
+    """
+    A component for converting between different data types.
+    
+    Supports conversion between Message, Data, and DataFrame types with
+    dynamic output configuration based on the selected output type.
+    """
     display_name = "Type Convert"
-    description = "Convert between different types (Message, Data, DataFrame)"
+    description = "Convert between Message, Data, and DataFrame types with dynamic output selection"
     icon = "repeat"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class TypeConverterComponent(Component):
display_name = "Type Convert"
description = "Convert between different types (Message, Data, DataFrame)"
icon = "repeat"
inputs = [
HandleInput(
name="input_data",
display_name="Input",
input_types=["Message", "Data", "DataFrame"],
info="Accept Message, Data or DataFrame as input",
required=True,
),
TabInput(
name="output_type",
display_name="Output Type",
options=["Message", "Data", "DataFrame"],
info="Select the desired output data type",
real_time_refresh=True,
value="Message",
),
]
outputs = [Output(display_name="Message Output", name="message_output", method="convert_to_message")]
class TypeConverterComponent(Component):
"""
A component for converting between different data types.
Supports conversion between Message, Data, and DataFrame types with
dynamic output configuration based on the selected output type.
"""
display_name = "Type Convert"
description = "Convert between Message, Data, and DataFrame types with dynamic output selection"
icon = "repeat"
inputs = [
HandleInput(
name="input_data",
display_name="Input",
input_types=["Message", "Data", "DataFrame"],
info="Accept Message, Data or DataFrame as input",
required=True,
),
TabInput(
name="output_type",
display_name="Output Type",
options=["Message", "Data", "DataFrame"],
info="Select the desired output data type",
real_time_refresh=True,
value="Message",
),
]
outputs = [
Output(
display_name="Message Output",
name="message_output",
method="convert_to_message",
)
]
🧰 Tools
🪛 Pylint (3.3.7)

[convention] 102-102: Line too long (105/100)

(C0301)


[convention] 79-79: Missing class docstring

(C0115)

🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/converter.py around lines 79
to 103, add a clear and concise class docstring to the TypeConverterComponent
class explaining its purpose and functionality. Also, improve the component
metadata by ensuring display_name, description, and icon are descriptive and
accurate to enhance readability and maintainability.

def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:
"""Dynamically show only the relevant output based on the selected output type."""
if field_name == "output_type":
# Start with empty outputs
frontend_node["outputs"] = []

# Add only the selected output type
if field_value == "Message":
frontend_node["outputs"].append(
Output(display_name="Message Output", name="message_output", method="convert_to_message").to_dict()
)
elif field_value == "Data":
frontend_node["outputs"].append(
Output(display_name="Data Output", name="data_output", method="convert_to_data").to_dict()
)
elif field_value == "DataFrame":
frontend_node["outputs"].append(
Output(
display_name="DataFrame Output", name="dataframe_output", method="convert_to_dataframe"
).to_dict()
)

return frontend_node
Comment on lines +73 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for invalid output types.

The update_outputs method doesn't handle the case where an invalid field_value is provided, which could leave the component in an inconsistent state.

     def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:
         """Dynamically show only the relevant output based on the selected output type."""
         if field_name == "output_type":
             # Start with empty outputs
             frontend_node["outputs"] = []

             # Add only the selected output type
             if field_value == "Message":
                 frontend_node["outputs"].append(
                     Output(
                         display_name="Message Output", 
                         name="message_output", 
                         method="convert_to_message"
                     ).to_dict()
                 )
             elif field_value == "Data":
                 frontend_node["outputs"].append(
                     Output(
                         display_name="Data Output", 
                         name="data_output", 
                         method="convert_to_data"
                     ).to_dict()
                 )
             elif field_value == "DataFrame":
                 frontend_node["outputs"].append(
                     Output(
                         display_name="DataFrame Output", 
                         name="dataframe_output", 
                         method="convert_to_dataframe"
                     ).to_dict()
                 )
+            else:
+                # Handle invalid output type by defaulting to Message
+                frontend_node["outputs"].append(
+                    Output(
+                         display_name="Message Output", 
+                         name="message_output", 
+                         method="convert_to_message"
+                     ).to_dict()
+                )

         return frontend_node
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:
"""Dynamically show only the relevant output based on the selected output type."""
if field_name == "output_type":
# Start with empty outputs
frontend_node["outputs"] = []
# Add only the selected output type
if field_value == "Message":
frontend_node["outputs"].append(
Output(display_name="Message Output", name="message_output", method="convert_to_message").to_dict()
)
elif field_value == "Data":
frontend_node["outputs"].append(
Output(display_name="Data Output", name="data_output", method="convert_to_data").to_dict()
)
elif field_value == "DataFrame":
frontend_node["outputs"].append(
Output(
display_name="DataFrame Output", name="dataframe_output", method="convert_to_dataframe"
).to_dict()
)
return frontend_node
def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:
"""Dynamically show only the relevant output based on the selected output type."""
if field_name == "output_type":
# Start with empty outputs
frontend_node["outputs"] = []
# Add only the selected output type
if field_value == "Message":
frontend_node["outputs"].append(
Output(display_name="Message Output", name="message_output", method="convert_to_message").to_dict()
)
elif field_value == "Data":
frontend_node["outputs"].append(
Output(display_name="Data Output", name="data_output", method="convert_to_data").to_dict()
)
elif field_value == "DataFrame":
frontend_node["outputs"].append(
Output(
display_name="DataFrame Output", name="dataframe_output", method="convert_to_dataframe"
).to_dict()
)
else:
# Handle invalid output type by defaulting to Message
frontend_node["outputs"].append(
Output(display_name="Message Output", name="message_output", method="convert_to_message").to_dict()
)
return frontend_node
🧰 Tools
🪛 Pylint (3.3.7)

[convention] 80-80: Line too long (119/100)

(C0301)


[convention] 84-84: Line too long (110/100)

(C0301)


[convention] 89-89: Line too long (111/100)

(C0301)

🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/converter.py around lines 71
to 93, the update_outputs method lacks error handling for invalid output types
in the field_value parameter. To fix this, add a check after the existing
if-elif blocks to handle unexpected field_value values by either raising a
ValueError with a clear message or logging an error and leaving outputs empty.
This ensures the component does not enter an inconsistent state when given
invalid input.


def convert_to_message(self) -> Message:
"""Convert input to Message type."""
return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)

def convert_to_data(self) -> Data:
"""Convert input to Data type."""
return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)

def convert_to_dataframe(self) -> DataFrame:
"""Convert input to DataFrame type."""
return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
Comment on lines +97 to +107
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Extract common input processing logic to reduce duplication.

All three conversion methods use identical logic to handle list inputs. This violates the DRY principle.

+    def _get_input_value(self):
+        """Extract the actual input value, handling list inputs."""
+        return self.input_data[0] if isinstance(self.input_data, list) else self.input_data
+
     def convert_to_message(self) -> Message:
         """Convert input to Message type."""
-        return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
+        return convert_to_message(self._get_input_value())

     def convert_to_data(self) -> Data:
         """Convert input to Data type."""
-        return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
+        return convert_to_data(self._get_input_value())

     def convert_to_dataframe(self) -> DataFrame:
         """Convert input to DataFrame type."""
-        return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
+        return convert_to_dataframe(self._get_input_value())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def convert_to_message(self) -> Message:
"""Convert input to Message type."""
return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
def convert_to_data(self) -> Data:
"""Convert input to Data type."""
return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
def convert_to_dataframe(self) -> DataFrame:
"""Convert input to DataFrame type."""
return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)
def _get_input_value(self):
"""Extract the actual input value, handling list inputs."""
return self.input_data[0] if isinstance(self.input_data, list) else self.input_data
def convert_to_message(self) -> Message:
"""Convert input to Message type."""
return convert_to_message(self._get_input_value())
def convert_to_data(self) -> Data:
"""Convert input to Data type."""
return convert_to_data(self._get_input_value())
def convert_to_dataframe(self) -> DataFrame:
"""Convert input to DataFrame type."""
return convert_to_dataframe(self._get_input_value())
🧰 Tools
🪛 Pylint (3.3.7)

[convention] 97-97: Line too long (111/100)

(C0301)


[convention] 101-101: Line too long (108/100)

(C0301)


[convention] 105-105: Line too long (113/100)

(C0301)

🤖 Prompt for AI Agents
In src/backend/base/langflow/components/processing/converter.py around lines 95
to 105, the three conversion methods duplicate the logic to check if input_data
is a list and select the first element. Extract this common input processing
logic into a private helper method that returns the appropriate input element,
then update each conversion method to call this helper before passing the input
to the respective conversion function. This will reduce code duplication and
improve maintainability.

36 changes: 31 additions & 5 deletions src/backend/base/langflow/schema/data.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from __future__ import annotations

import copy
import json
from datetime import datetime, timezone
from decimal import Decimal
from typing import cast
from typing import TYPE_CHECKING, cast
from uuid import UUID

from langchain_core.documents import Document
Expand All @@ -13,6 +15,10 @@
from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER
from langflow.utils.image import create_data_url

if TYPE_CHECKING:
from langflow.schema.dataframe import DataFrame
from langflow.schema.message import Message


class Data(BaseModel):
"""Represents a record with text and optional data.
Expand Down Expand Up @@ -81,7 +87,7 @@ def set_text(self, text: str | None) -> str:
return new_text

@classmethod
def from_document(cls, document: Document) -> "Data":
def from_document(cls, document: Document) -> Data:
"""Converts a Document to a Data.

Args:
Expand All @@ -95,7 +101,7 @@ def from_document(cls, document: Document) -> "Data":
return cls(data=data, text_key="text")

@classmethod
def from_lc_message(cls, message: BaseMessage) -> "Data":
def from_lc_message(cls, message: BaseMessage) -> Data:
"""Converts a BaseMessage to a Data.

Args:
Expand All @@ -108,7 +114,7 @@ def from_lc_message(cls, message: BaseMessage) -> "Data":
data["metadata"] = cast("dict", message.to_json())
return cls(data=data, text_key="text")

def __add__(self, other: "Data") -> "Data":
def __add__(self, other: Data) -> Data:
"""Combines the data of two data by attempting to add values for overlapping keys.

Combines the data of two data by attempting to add values for overlapping keys
Expand Down Expand Up @@ -235,7 +241,7 @@ def __contains__(self, key) -> bool:
def __eq__(self, /, other):
return isinstance(other, Data) and self.data == other.data

def filter_data(self, filter_str: str) -> "Data":
def filter_data(self, filter_str: str) -> Data:
"""Filters the data dictionary based on the filter string.

Args:
Expand All @@ -248,6 +254,26 @@ def filter_data(self, filter_str: str) -> "Data":

return apply_json_filter(self.data, filter_str)

def to_message(self) -> Message:
from langflow.schema.message import Message # Local import to avoid circular import

if self.text_key in self.data:
return Message(text=self.get_text())
return Message(text=str(self.data))

def to_dataframe(self) -> DataFrame:
from langflow.schema.dataframe import DataFrame # Local import to avoid circular import

data_dict = self.data
# If data contains only one key and the value is a list of dictionaries, convert to DataFrame
if (
len(data_dict) == 1
and isinstance(next(iter(data_dict.values())), list)
and all(isinstance(item, dict) for item in next(iter(data_dict.values())))
):
return DataFrame(data=next(iter(data_dict.values())))
return DataFrame(data=[self])


def custom_serializer(obj):
if isinstance(obj, datetime):
Expand Down
26 changes: 26 additions & 0 deletions src/backend/base/langflow/schema/dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pandas import DataFrame as pandas_DataFrame

from langflow.schema.data import Data
from langflow.schema.message import Message


class DataFrame(pandas_DataFrame):
Expand Down Expand Up @@ -178,3 +179,28 @@ def __eq__(self, other):
if not isinstance(other, DataFrame | pd.DataFrame): # Non-DataFrame case
return False
return super().__eq__(other)

def to_data(self) -> Data:
"""Convert this DataFrame to a Data object.

Returns:
Data: A Data object containing the DataFrame records under 'results' key.
"""
dict_list = self.to_dict(orient="records")
return Data(data={"results": dict_list})

def to_message(self) -> Message:
from langflow.schema.message import Message # Local import to avoid circular import

# Process DataFrame similar to the _safe_convert method
# Remove empty rows
processed_df = self.dropna(how="all")
# Remove empty lines in each cell
processed_df = processed_df.replace(r"^\s*$", "", regex=True)
# Replace multiple newlines with a single newline
processed_df = processed_df.replace(r"\n+", "\n", regex=True)
# Replace pipe characters to avoid markdown table issues
processed_df = processed_df.replace(r"\|", r"\\|", regex=True)
processed_df = processed_df.map(lambda x: str(x).replace("\n", "<br/>") if isinstance(x, str) else x)
# Convert to markdown and wrap in a Message
return Message(text=processed_df.to_markdown(index=False))
13 changes: 12 additions & 1 deletion src/backend/base/langflow/schema/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import traceback
from collections.abc import AsyncIterator, Iterator
from datetime import datetime, timezone
from typing import Annotated, Any, Literal
from typing import TYPE_CHECKING, Annotated, Any, Literal
from uuid import UUID

from fastapi.encoders import jsonable_encoder
Expand All @@ -31,6 +31,9 @@
)
from langflow.utils.image import create_data_url

if TYPE_CHECKING:
from langflow.schema.dataframe import DataFrame


class Message(Data):
model_config = ConfigDict(arbitrary_types_allowed=True)
Expand Down Expand Up @@ -276,6 +279,14 @@ async def create(cls, **kwargs):
return await asyncio.to_thread(cls, **kwargs)
return cls(**kwargs)

def to_data(self) -> Data:
return Data(data=self.data)

def to_dataframe(self) -> DataFrame:
from langflow.schema.dataframe import DataFrame # Local import to avoid circular import

return DataFrame(data=[self])


class DefaultModel(BaseModel):
class Config:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import pandas as pd
import pytest
from langflow.components.processing.converter import TypeConverterComponent
from langflow.schema.data import Data
from langflow.schema.dataframe import DataFrame
from langflow.schema.message import Message

from tests.base import ComponentTestBaseWithoutClient


class TestTypeConverterComponent(ComponentTestBaseWithoutClient):
@pytest.fixture
def component_class(self):
"""Return the component class to test."""
return TypeConverterComponent

@pytest.fixture
def file_names_mapping(self):
"""Return an empty list since this component doesn't have version-specific files."""
return []

# Message to other types
def test_message_to_message(self, component_class):
"""Test converting Message to Message."""
component = component_class(input_data=Message(text="Hello World"), output_type="Message")
result = component.convert_to_message()
assert isinstance(result, Message)
assert result.text == "Hello World"

def test_message_to_data(self, component_class):
"""Test converting Message to Data."""
component = component_class(input_data=Message(text="Hello"), output_type="Data")
result = component.convert_to_data()
assert isinstance(result, Data)
assert "text" in result.data
assert result.data["text"] == "Hello"

def test_message_to_dataframe(self, component_class):
"""Test converting Message to DataFrame."""
component = component_class(input_data=Message(text="Hello"), output_type="DataFrame")
result = component.convert_to_dataframe()
assert isinstance(result, DataFrame)
assert "text" in result.columns
assert result.iloc[0]["text"] == "Hello"

# Data to other types
def test_data_to_message(self, component_class):
"""Test converting Data to Message."""
component = component_class(input_data=Data(data={"text": "Hello World"}), output_type="Message")
result = component.convert_to_message()
assert isinstance(result, Message)
assert result.text == "Hello World"

def test_data_to_data(self, component_class):
"""Test converting Data to Data."""
component = component_class(input_data=Data(data={"key": "value"}), output_type="Data")
result = component.convert_to_data()
assert isinstance(result, Data)
assert result.data == {"key": "value"}

def test_data_to_dataframe(self, component_class):
"""Test converting Data to DataFrame."""
component = component_class(input_data=Data(data={"text": "Hello World"}), output_type="DataFrame")
result = component.convert_to_dataframe()
assert isinstance(result, DataFrame)
assert "text" in result.columns
assert result.iloc[0]["text"] == "Hello World"

# DataFrame to other types
def test_dataframe_to_message(self, component_class):
"""Test converting DataFrame to Message."""
df_data = pd.DataFrame({"col1": ["Hello"], "col2": ["World"]})
component = component_class(input_data=DataFrame(data=df_data), output_type="Message")
result = component.convert_to_message()
assert isinstance(result, Message)
assert result.text == "| col1 | col2 |\n|:-------|:-------|\n| Hello | World |"

def test_dataframe_to_data(self, component_class):
"""Test converting DataFrame to Data."""
df_data = pd.DataFrame({"col1": ["Hello"]})
component = component_class(input_data=DataFrame(data=df_data), output_type="Data")
result = component.convert_to_data()
assert isinstance(result, Data)
assert isinstance(result.data, dict)

def test_dataframe_to_dataframe(self, component_class):
"""Test converting DataFrame to DataFrame."""
df_data = pd.DataFrame({"col1": ["Hello"], "col2": ["World"]})
component = component_class(input_data=DataFrame(data=df_data), output_type="DataFrame")
result = component.convert_to_dataframe()
assert isinstance(result, DataFrame)
assert "col1" in result.columns
assert "col2" in result.columns
assert result.iloc[0]["col1"] == "Hello"
assert result.iloc[0]["col2"] == "World"

def test_update_outputs(self, component_class):
"""Test the update_outputs method."""
component = component_class(input_data=Message(text="Hello"), output_type="Message")
frontend_node = {"outputs": []}

# Test with Message output
updated = component.update_outputs(frontend_node, "output_type", "Message")
assert len(updated["outputs"]) == 1
assert updated["outputs"][0]["name"] == "message_output"

# Test with Data output
updated = component.update_outputs(frontend_node, "output_type", "Data")
assert len(updated["outputs"]) == 1
assert updated["outputs"][0]["name"] == "data_output"

# Test with DataFrame output
updated = component.update_outputs(frontend_node, "output_type", "DataFrame")
assert len(updated["outputs"]) == 1
assert updated["outputs"][0]["name"] == "dataframe_output"
Loading