Skip to content

Commit

Permalink
feat: updated ChatOutput component that can accept Data, Dataframe an…
Browse files Browse the repository at this point in the history
…d Message (#6643)

* update chatoutput

* [autofix.ci] apply automated fixes

* update

* tests

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* Update chat.py

* update template

* fix lint errors

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* fix: rename variable for clarity in chat output component test

* [autofix.ci] apply automated fixes

* fix: enable loading from database for API key in starter project configurations

* update templates

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Gabriel Luiz Freitas Almeida <gabriel@langflow.org>
Co-authored-by: cristhianzl <cristhian.lousa@gmail.com>
  • Loading branch information
4 people authored Feb 19, 2025
1 parent d143fe4 commit 69df913
Show file tree
Hide file tree
Showing 29 changed files with 5,557 additions and 1,935 deletions.
91 changes: 83 additions & 8 deletions src/backend/base/langflow/components/outputs/chat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
from typing import Any

from langflow.base.io.chat import ChatComponent
from langflow.inputs import BoolInput
from langflow.io import DropdownInput, MessageInput, MessageTextInput, Output
from langflow.inputs.inputs import HandleInput
from langflow.io import DropdownInput, MessageTextInput, Output
from langflow.schema.data import Data
from langflow.schema.dataframe import DataFrame
from langflow.schema.message import Message
from langflow.schema.properties import Source
from langflow.utils.constants import (
Expand All @@ -18,10 +23,12 @@ class ChatOutput(ChatComponent):
minimized = True

inputs = [
MessageInput(
HandleInput(
name="input_value",
display_name="Text",
info="Message to be passed as output.",
input_types=["Data", "DataFrame", "Message"],
required=True,
),
BoolInput(
name="should_store_message",
Expand Down Expand Up @@ -76,6 +83,13 @@ class ChatOutput(ChatComponent):
info="The text color of the name",
advanced=True,
),
BoolInput(
name="clean_data",
display_name="Basic Clean Data",
value=True,
info="Whether to clean the data",
advanced=True,
),
]
outputs = [
Output(
Expand All @@ -92,16 +106,35 @@ def _build_source(self, id_: str | None, display_name: str | None, source: str |
if display_name:
source_dict["display_name"] = display_name
if source:
source_dict["source"] = source
# Handle case where source is a ChatOpenAI object
if hasattr(source, "model_name"):
source_dict["source"] = source.model_name
elif hasattr(source, "model"):
source_dict["source"] = str(source.model)
else:
source_dict["source"] = str(source)
return Source(**source_dict)

async def message_response(self) -> Message:
# First convert the input to string if needed
text = self.convert_to_string()

# Get source properties
source, icon, display_name, source_id = self.get_properties_from_source_component()
background_color = self.background_color
text_color = self.text_color
if self.chat_icon:
icon = self.chat_icon
message = self.input_value if isinstance(self.input_value, Message) else Message(text=self.input_value)

# Create or use existing Message object
if isinstance(self.input_value, Message):
message = self.input_value
# Update message properties
message.text = text
else:
message = Message(text=text)

# Set message properties
message.sender = self.sender
message.sender_name = self.sender_name
message.session_id = self.session_id
Expand All @@ -110,12 +143,54 @@ async def message_response(self) -> Message:
message.properties.icon = icon
message.properties.background_color = background_color
message.properties.text_color = text_color
if self.session_id and isinstance(message, Message) and self.should_store_message:
stored_message = await self.send_message(
message,
)

# Store message if needed
if self.session_id and self.should_store_message:
stored_message = await self.send_message(message)
self.message.value = stored_message
message = stored_message

self.status = message
return message

def _validate_input(self) -> None:
"""Validate the input data and raise ValueError if invalid."""
if self.input_value is None:
msg = "Input data cannot be None"
raise ValueError(msg)
if not isinstance(self.input_value, Data | DataFrame | Message | str | list):
msg = f"Expected Data or DataFrame or Message or str, got {type(self.input_value).__name__}"
raise TypeError(msg)

def _safe_convert(self, data: Any) -> str:
"""Safely convert input data to string."""
try:
if isinstance(data, str):
return data
if isinstance(data, Message):
return data.get_text()
if isinstance(data, Data):
if data.get_text() is None:
msg = "Empty Data object"
raise ValueError(msg)
return data.get_text()
if isinstance(data, DataFrame):
if self.clean_data:
# Remove empty rows
data = data.dropna(how="all")
# Remove empty lines in each cell
data = data.replace(r"^\s*$", "", regex=True)
# Replace multiple newlines with a single newline
data = data.replace(r"\n+", "\n", regex=True)
return data.to_markdown(index=False)
return str(data)
except (ValueError, TypeError, AttributeError) as e:
msg = f"Error converting data: {e!s}"
raise ValueError(msg) from e

def convert_to_string(self) -> str:
"""Convert input data to string with proper error handling."""
self._validate_input()
if isinstance(self.input_value, list):
return "\n".join([self._safe_convert(item) for item in self.input_value])
return self._safe_convert(self.input_value)
Loading

0 comments on commit 69df913

Please # to comment.