From 0d4ecd46019e3e0c6ad245fdf98e8053e4d672fc Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Mon, 4 Nov 2024 20:58:16 -0300 Subject: [PATCH] feature: add new playground events (#4280) * Refactor sessionSelector component to improve session management and interaction * Refactor IOModal component to remove unused code and improve session management * fix typing error * fix run chat input on component level * prevent toogle visibility on session menu * fix bug on rename session while in table view mode * chore: Update setSelectedView prop type in sessionSelector component * add first test version not working yet * fix bug for renaming and deleting session * refactor: Update sessionSelector component to handle session changes * improve test * fix rename session multiple session bugs * change visible session from array to string * chore: Update editMessageField component to include margin-right for text span * [autofix.ci] apply automated fixes * Update down_revision in Alembic migration script * Refactor IOModal component to simplify session visibility handling * Add new schema for playground events with various event types - Introduce `PlaygroundEvent` base class and specific event types: `MessageEvent`, `ErrorEvent`, `WarningEvent`, `InfoEvent`, and `TokenEvent`. - Implement `ContentBlock` class for flexible content handling. - Add factory functions to create different event types. - Utilize Pydantic for data validation and serialization. * Add PlaygroundEvent to LoggableType in log schema * Add timestamp validation utility to schema utils and refactor Message class to use it * Add custom serialization for datetime and Decimal in data schema * Add session_id handling to graph and chat API - Introduce `_session_id` attribute in the graph base class. - Implement getter and setter for `session_id` property. - Update chat API to set `session_id` from inputs if available. * Add error handling for event creation in EventManager - Integrate `create_event_by_type` to generate playground events. - Add try-except block to handle `TypeError` during event creation and log the error using `loguru`. - Raise any other exceptions encountered during event creation. * Enhance session ID handling in vertex base logic * Add message handling and streaming capabilities to custom component - Introduced `send_message` method to handle message sending with optional formatting and content blocks. - Implemented `_store_message`, `_send_message_event`, and `_should_stream_message` for message storage and event management. - Added `_update_stored_message` and `_stream_message` to support message streaming and updating. - Included `_handle_async_iterator` and `_process_chunk` for processing message chunks from iterators. * Add new schema types and properties for chat and messages components * Add customizable chat message styles and icons in IOModal component * Add support for handling Playground events in chat view - Introduced `PlaygroundEvent` type to manage different event types such as message, error, warning, info, and token. - Implemented `handlePlaygroundEvent` function to process and update chat history based on event type. - Enhanced file parsing logic to handle empty strings and parsing errors. - Added default values for `background_color` and `text_color` in chat messages. * revert changes made to bypass ruff-check * revert changes made to bypass ruff-check * Refactor file parsing logic in ChatView component * fix ui problem * Refactor feature flags and chat view components - Enable new IO modal in feature flags - Update imports and remove unused code in newChatMessage component - Add error icon and error message in ChatMessage component - Update lockChat icon position in newChatView component - Import OctagonAlert icon in styleUtils * Refactor IOModal newModal component styles * Refactor styles in EditMessageField and ChatMessage components * Refactor styles in EditMessageField component * Refactor styles in ChatMessage component to fix UI problem * Refactor styles in UploadFileButton component and add image attachment tooltip * Refactor styles in ChatCodeTabComponent to update language text color * fix padding in other field views * Refactor styles in ChatComponent and add tooltip to the playground * Refactor styles in constants.ts to remove unused input and output types * Refactor session selector styles * Refactor chatView component styles * update colors to use variables * Refactor code tab component and update code styling * Refactor buttonSendWrapper component styles * update colors to use variables * update colors to use variables * Refactor chatComponent and chatInput styles * update colors to use variables * chore: add new attributes to model definition * chore: generate database migration * chore: add MetaData and ContentBlock models and update Message schema * chore: add error handling to build_results and adjust message event handling * chore: deserialize meta_data and content_blocks fields in add_messagetables * raw * Refactor message and chat types to include category, meta_data, and content_blocks * raw * Refactor error handling in component.py and newChatMessage.tsx * add source to bot messages * raw * Refactor chatMessage component to include meta_data icon and source * improve icon, background color, text color handling in chat message * add edit flag correctly * use svg icon logo instead of chain emoji * add text effect * update error colors * fix error aligment * add text shimmer effect * improve is running style * Add support for importing SVG files in Vite environment * Add chatLogoIcon component with Chain logo * Refactor chat message component to use chatLogoIcon component * Refactor chatView component to use chatLogoIcon component * remove from empty chat * update text sizes and style * remove icon in message logs * fix stop button color * update background color and sender name size * prevent list index out of range error * Fix onBlur event in EditMessageField component * improve edit message field * improve edit background behaviour * Update chat input styling for better user experience * Refactor ChatCodeTabComponent to remove redundant "Copy" text in button * Refactor code to use CSS variables for background color in code blocks * Refactor ChatCodeTabComponent to update code block styling * Refactor code to update code block styling and improve chat input styling * prevent Flow Running after error rendering * Refactor code to add IOModalOpen state and setter function * Refactor code to add IOModalOpen state and setter function * Refactor code to remove console.log statement * update icon and padding * Refactor code to retrieve connected model name in ChatComponent * run formatter * fix stop button glitch * update max width * Refactor GithubStarButton component to handle null value for stars * Refactor tailwind.config.mjs to add utility classes for fading text truncation * improve faded truncate function * Refactor ChatView component to handle null value for chat history category * [autofix.ci] apply automated fixes * Refactor DialogContent component to improve close button styling * Refactor IOModal component to improve sidebar behavior and styling * Refactor ChatView component to remove unused code and improve performance * Refactor session selector component to add tooltip for options * [autofix.ci] apply automated fixes * update several icon sizes and hover states * [autofix.ci] apply automated fixes * Refactor IOModal component to improve sidebar behavior and styling * Refactor IOModal component to handle sidebar behavior on window resize * [autofix.ci] apply automated fixes * fix error related to content blocks being null * [autofix.ci] apply automated fixes * add component id as sender to the message * add component id as source to the message * Add fitViewNode function to FlowStoreType * enable closing chat on error message * Add ClickableLinks component for rendering clickable URLs in chat messages * add error message transition * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes (attempt 2/3) * use IsPlayground flag * Remove console.log statement in setIOModalOpen function * update max-w * prevent breaking the buttons on small screen * [autofix.ci] apply automated fixes * remove underline when component is not clicable * Refactor IOModal component to remove unnecessary props and update usage - Remove the "isPlayground" prop from the IOModal component in cardComponent and storeCardComponent - Update the usage of the IOModal component in chatComponent and newModal to use the "canvasOpen" prop instead of "isPlayground" * fix html missing closing tag error * [autofix.ci] apply automated fixes * Refactor PlaygroundPage component to improve flow initialization and data fetching * [autofix.ci] apply automated fixes * Refactor cardComponent and storeCardComponent to comment out playground buttons * Refactor message model to replace 'meta_data' with 'properties' and update related schema and components * Add new schema for content types with Pydantic models * Add ContentBlock schema with support for multiple content types * Refactor: Import ContentBlock from langflow.schema.content_block instead of defining locally * Add SendMessageFunctionType protocol to log schema for message handling * Refactor chat message model to replace 'meta_data' with 'properties' and update related components * Add ContentBlock import and enhance send_message method in component.py - Import ContentBlock from langflow.schema.content_block. - Replace 'meta_data' with 'properties' in message construction. - Update send_message method to handle optional Message and text parameters. * Add async on_chain_start method to callback for logging chain start events * Refactor add_messagetables function to replace 'meta_data' with 'properties' in message processing * Fix typo in 'traceback' property name in ErrorDetails interface * Add condition to check chat history length before rendering chat lock * Refactor error content structure and enhance message event handling * Add `update_messages` function to handle message updates in memory module * Add check for 'id' attribute in stored_message for message validation * Add new content types and TypeAlias for schema in content_types.py * Add custom serialization for UUID and BaseModel types in data schema * Refactor `ContentType` to use `ContentTypes` union and update `content` field in `ContentBlock` model * Update model to use specific types for properties and content_blocks fields * Change return type of 'serialize_params' method to string * Add sender fields and properties handling to PlaygroundEvent and create_message function * Refactor chat message component to use 'properties' instead of 'meta_data' for chat attributes * Refactor message handling to improve error categorization and session management * Enhance ContentBlock initialization to update model fields with defaults * Add new content types and update ContentBlock interface in chat types - Introduced BaseContent interface and various content types: ErrorContent, TextContent, MediaContent, JSONContent, CodeContent, ToolStartContent, ToolEndContent, and ToolErrorContent. - Created a union type ContentType for all content types. - Updated ContentBlock interface to include new content structure and additional fields. * Refactor error handling in chat messages to use `ErrorContent` type * Add ErrorMessage class for handling error-specific messages in schema * Add type annotations for cache and logs in custom_component.py * Refactor error handling in custom component to use `send_error` method * Add customizable properties for chat components - Introduced new properties: `background_color`, `chat_icon`, and `text_color` to chat components across various starter projects. - These properties allow for customization of the chat message appearance, including icon and text styling. - Updated the `ChatInput` and `ChatOutput` components to handle these new properties and include them in the message response. * Add 'category' field to MessageEvent and update create_message function * Remove unused message handling methods from ChatComponent * Refactor message event handling in chat module * Add 'category' field to Message creation and pass 'id_' in _send_message_event * Add 'category' field with Literal type to Message schema and default handling in chat IO * Refactor message event handling in chat module * Refactor buttonSendWrapper component to add data-testid attribute * Refactor ChatComponent to replace 'store_message' with 'send_message' method * refactor: streamline message handling in Component Update message processing to utilize new methods for improved clarity and efficiency. Enhance event data management to ensure accuracy and consistency in message updates. * feat: Update input handling and output storage for chat components Enhance chat input and output components to improve message handling, including updates to storage and display properties. This facilitates better user experience and message management in various agent projects. * Ensure 'id' field is set in message data dictionary if not present * refactor: Simplify property retrieval from source component in ChatComponent * refactor: Replace property retrieval logic with a dedicated method in ChatOutput * Refactor `TokenEvent` class and add `timestamp` field with default factory * refactor: Simplify message processing by removing unused parameters in async handling * Refactor message text update logic in messagesStore * Refactor border styling in ChatInput component * refactor: Consolidate properties handling in PlaygroundEvent and create_message function * refactor: Update timestamp formatting to include timezone information across multiple models * refactor: Add StreamingError exception for better error handling in streaming components * refactor: Add source_display_name attribute to Properties model for enhanced data representation * refactor: Enhance ChatComponent to return source display name and update properties handling in ChatOutput * refactor: Improve error handling in Component class and enhance create_error function with timestamp support * feat: enhance ChatOutput component for improved message handling Update the message_response method to include source display name and improve message storage functionality across various project templates. This enhances clarity and ensures consistent behavior of chat components. * feat: add delete_message function to remove messages by ID from the monitor service * Add error handling for message streaming and implement message deletion on failure * feat: Enhance message streaming and event handling Improve the streaming process to send message events only for non-streaming messages and ensure the initial message is sent correctly when processing chunks, enhancing the overall message handling flow. * improve UI * Refactor buttonSendWrapper component to add data-testid attribute and update tests * fix edited tag * add tooltip to add button * fix bug after merge * update examples * updateinput without chat * fix some frontend tests * fix test * update test * Update fileUploadComponent.spec.ts to fix test and update input without chat * refactor event creation functions and enhance ErrorEvent structure * refactor ErrorMessage to use Source for sender and component details * refactor Properties to use Source model for source details and add validation * refactor ChatMessage component for improved readability and structure * refactor ChatMessageType to use PropertiesType for improved structure and clarity * Add Source ID to message properties and update exception handling - Updated `message_response` method to include `source_id` in the `Properties` object for better traceability. - Modified `StreamingError` to use `Source` object instead of `component_name` for more detailed error context. - Adjusted `get_properties_from_source_component` to return `source_id` along with other properties. * Add 'Source' property to error handling in custom_component module - Introduced 'Source' property to enhance error message details. - Updated error handling to include 'source' instead of 'display_name'. - Modified exception raising to use 'source' for better traceability. * Enhance event ID generation with event type prefix in `send_event` method * Refactor ChatOutput class to use Properties and Source schemas for message properties * update style in tailwind and add fallback for nullable values * fix playground test * Update message handling in Component class to send modified message copy on first chunk * Add flow_id and session_id to ErrorEvent creation function * Update content_blocks initialization and add flow_id parameter to ErrorMessage * feat: include vertex ID in class instantiation Pass the vertex ID to the custom component during instantiation to facilitate event tracking or management capabilities. * fix: update message streaming logic to handle None messages and refactor _stream_message parameters * feat: include flow_id in ChatOutput initialization Add flow_id to the ChatOutput class to improve the tracking of conversational flows, enhancing the context management within the chat system. * fix: update chat message source display to use 'source' instead of 'display_name' * feat: add flow_id to message initialization in ChatOutput class * fix: handle JSON parsing with type checks for message properties Refine JSON loading to ensure proper type handling for message properties and content blocks, enhancing stability and preventing potential errors with non-string inputs. * refactor: update logging structure and message handling Improve the output logs to support logging multiple entries and ensure robust message streaming with type checking. Adjust error handling parameters for consistency. * feat: restrict event types in registration and sending Enhance event management by explicitly defining allowed event types, improving code clarity and reducing potential errors. This ensures only valid event types are registered and sent, leading to more robust event handling. * Update `logs` attribute to store lists of `Log` objects in `Vertex` class * feat: introduce TypedDicts for ContentBlock and Properties, update default values in Message model * fix: restrict event types in send_event method to improve data validation * Set default values for 'id', 'display_name', and 'source' fields in Source schema * [autofix.ci] apply automated fixes * fix: update query to use equality check for error messages * make lint * update simple agent test * test: enhance EventManager tests for event_id validation * feat: add background, chat icon, and text color properties to component toolkit * fix: update LogComponent name to display_name for consistency * remove playground from new cards on main page --------- Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: italojohnny --- .../versions/1eab2c3eb45e_event_error.py | 53 ++ src/backend/base/langflow/api/v1/chat.py | 3 + .../base/langflow/base/agents/callback.py | 27 + src/backend/base/langflow/base/io/chat.py | 97 +-- .../base/langflow/components/inputs/chat.py | 24 +- .../base/langflow/components/outputs/chat.py | 49 +- .../custom/custom_component/component.py | 167 ++++- .../custom_component/custom_component.py | 6 +- .../base/langflow/events/event_manager.py | 24 +- .../base/langflow/exceptions/component.py | 11 + src/backend/base/langflow/graph/graph/base.py | 11 +- .../base/langflow/graph/vertex/base.py | 7 +- .../starter_projects/Agent Flow.json | 130 +++- .../Basic Prompting (Hello, World).json | 130 +++- .../starter_projects/Blog Writer.json | 65 +- .../starter_projects/Complex Agent.json | 130 +++- .../starter_projects/Document QA.json | 130 +++- .../starter_projects/Hierarchical Agent.json | 130 +++- .../starter_projects/Memory Chatbot.json | 130 +++- .../starter_projects/Sequential Agent.json | 65 +- .../Travel Planning Agents.json | 130 +++- .../starter_projects/Vector Store RAG.json | 130 +++- .../langflow/interface/initialize/loading.py | 1 + src/backend/base/langflow/memory.py | 48 +- .../base/langflow/schema/content_block.py | 34 + .../base/langflow/schema/content_types.py | 94 +++ src/backend/base/langflow/schema/data.py | 25 +- src/backend/base/langflow/schema/log.py | 23 +- src/backend/base/langflow/schema/message.py | 101 ++- .../base/langflow/schema/playground_events.py | 180 +++++ .../base/langflow/schema/properties.py | 27 + src/backend/base/langflow/schema/utils.py | 20 + .../services/database/models/message/model.py | 50 +- .../components/outputs/test_chat_output.py | 4 +- .../unit/base/tools/test_component_toolkit.py | 18 + .../tests/unit/events/test_event_manager.py | 11 +- .../unit/graph/graph/test_callback_graph.py | 2 +- src/frontend/src/assets/logo.svg | 1 + .../src/components/cardComponent/index.tsx | 12 +- .../components/cardsWrapComponent/index.tsx | 18 +- .../ChatCodeTabComponent.tsx | 11 +- .../components/flowToolbarComponent/index.tsx | 29 +- .../components/storeCardComponent/index.tsx | 10 +- .../src/components/ui/TextShimmer.tsx | 53 ++ src/frontend/src/components/ui/button.tsx | 2 +- src/frontend/src/components/ui/dialog.tsx | 11 +- .../src/components/ui/textAnimation.tsx | 231 ++++++ src/frontend/src/constants/constants.ts | 26 +- .../src/customization/feature-flags.ts | 2 +- .../sessionSelector/newSessionSelector.tsx | 53 +- .../newButtonSendWrapper.tsx | 20 +- .../textAreaWrapper/newTextAreaWrapper.tsx | 8 +- .../uploadFileButton/newUploadFileButton.tsx | 47 +- .../chatView/chatInput/newChatInput.tsx | 45 +- .../components/chatLogoIcon/index.tsx | 15 + .../editMessageButton/newMessageOptions.tsx | 10 +- .../editMessageField/newEditMessageField.tsx | 73 +- .../components/chatView/chatMessage/index.tsx | 22 +- .../chatView/chatMessage/newChatMessage.tsx | 701 +++++++++++------- .../filePreviewChat/newFilePreview.tsx | 7 +- .../IOModal/components/chatView/index.tsx | 72 +- .../components/chatView/newChatView.tsx | 87 ++- src/frontend/src/modals/IOModal/newModal.tsx | 184 +++-- .../MainPage/components/dropdown/index.tsx | 4 +- .../pages/MainPage/components/grid/index.tsx | 12 +- .../pages/MainPage/components/list/index.tsx | 58 +- src/frontend/src/pages/Playground/index.tsx | 70 +- src/frontend/src/routes.tsx | 4 +- src/frontend/src/stores/flowStore.ts | 5 + src/frontend/src/stores/flowsManagerStore.ts | 4 + src/frontend/src/stores/messagesStore.ts | 15 + src/frontend/src/style/applies.css | 2 +- src/frontend/src/style/classes.css | 10 +- src/frontend/src/style/index.css | 11 +- src/frontend/src/types/chat/index.ts | 112 +++ src/frontend/src/types/components/index.ts | 3 + src/frontend/src/types/messages/index.ts | 7 + src/frontend/src/types/zustand/flow/index.ts | 1 + .../src/types/zustand/flowsManager/index.ts | 2 + .../src/types/zustand/messages/index.ts | 1 + src/frontend/src/utils/buildUtils.ts | 9 +- src/frontend/src/utils/styleUtils.ts | 4 + src/frontend/src/vite-env.d.ts | 6 + src/frontend/tailwind.config.mjs | 48 ++ .../chatInputOutputUser-shard-0.spec.ts | 4 +- .../tests/core/features/freeze.spec.ts | 2 +- .../tests/core/features/playground.spec.ts | 88 +-- .../core/integrations/Basic Prompting.spec.ts | 4 +- .../core/integrations/Blog Writer.spec.ts | 36 +- .../core/integrations/Document QA.spec.ts | 2 +- .../core/integrations/Memory Chatbot.spec.ts | 6 +- .../core/integrations/Simple Agent.spec.ts | 27 +- .../core/integrations/similarity.spec.ts | 11 +- .../core/integrations/textInputOutput.spec.ts | 419 +++++------ .../regression/generalBugs-shard-9.spec.ts | 6 +- .../tests/core/unit/chatInputOutput.spec.ts | 2 +- .../core/unit/fileUploadComponent.spec.ts | 17 +- .../tests/core/unit/linkComponent.spec.ts | 5 +- .../core/unit/tableInputComponent.spec.ts | 2 +- .../features/stop-button-playground.spec.ts | 10 +- .../chatInputOutputUser-shard-2.spec.ts | 18 +- .../extended/integrations/duckduckgo.spec.ts | 2 +- .../general-bugs-shard-3836.spec.ts | 2 +- .../regression/generalBugs-shard-13.spec.ts | 2 +- .../regression/generalBugs-shard-3.spec.ts | 8 +- .../regression/generalBugs-shard-7.spec.ts | 2 +- 106 files changed, 3938 insertions(+), 1162 deletions(-) create mode 100644 src/backend/base/langflow/alembic/versions/1eab2c3eb45e_event_error.py create mode 100644 src/backend/base/langflow/schema/content_block.py create mode 100644 src/backend/base/langflow/schema/content_types.py create mode 100644 src/backend/base/langflow/schema/playground_events.py create mode 100644 src/backend/base/langflow/schema/properties.py create mode 100644 src/backend/base/langflow/schema/utils.py create mode 100644 src/frontend/src/assets/logo.svg create mode 100644 src/frontend/src/components/ui/TextShimmer.tsx create mode 100644 src/frontend/src/components/ui/textAnimation.tsx create mode 100644 src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/chatLogoIcon/index.tsx create mode 100644 src/frontend/src/vite-env.d.ts diff --git a/src/backend/base/langflow/alembic/versions/1eab2c3eb45e_event_error.py b/src/backend/base/langflow/alembic/versions/1eab2c3eb45e_event_error.py new file mode 100644 index 000000000..7647a7173 --- /dev/null +++ b/src/backend/base/langflow/alembic/versions/1eab2c3eb45e_event_error.py @@ -0,0 +1,53 @@ +"""event_error + +Revision ID: 1eab2c3eb45e +Revises: eb5e72293a8e +Create Date: 2024-10-24 12:03:24.118937 + +""" +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op +from sqlalchemy.dialects import sqlite +from sqlalchemy.engine.reflection import Inspector + +# revision identifiers, used by Alembic. +revision: str = '1eab2c3eb45e' +down_revision: Union[str, None] = 'eb5e72293a8e' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + conn = op.get_bind() + inspector = Inspector.from_engine(conn) # type: ignore + table_names = inspector.get_table_names() # noqa + column_names = [column["name"] for column in inspector.get_columns("message")] + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('message', schema=None) as batch_op: + if "properties" not in column_names: + batch_op.add_column(sa.Column('properties', sa.JSON(), nullable=True)) + if "category" not in column_names: + batch_op.add_column(sa.Column('category', sa.Text(), nullable=True)) + if "content_blocks" not in column_names: + batch_op.add_column(sa.Column('content_blocks', sa.JSON(), nullable=True)) + + # ### end Alembic commands ### + + +def downgrade() -> None: + conn = op.get_bind() + inspector = Inspector.from_engine(conn) # type: ignore + table_names = inspector.get_table_names() # noqa + column_names = [column["name"] for column in inspector.get_columns("message")] + # ### commands auto generated by Alembic - please adjust! ### + with op.batch_alter_table('message', schema=None) as batch_op: + if "content_blocks" in column_names: + batch_op.drop_column('content_blocks') + if "category" in column_names: + batch_op.drop_column('category') + if "properties" in column_names: + batch_op.drop_column('properties') + + # ### end Alembic commands ### diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 742c1baea..ce7dfc579 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -177,6 +177,9 @@ async def build_flow( else: first_layer = graph.sort_vertices() + if inputs is not None and hasattr(inputs, "session") and inputs.session is not None: + graph.session_id = inputs.session + for vertex_id in first_layer: graph.run_manager.add_to_vertices_being_run(vertex_id) diff --git a/src/backend/base/langflow/base/agents/callback.py b/src/backend/base/langflow/base/agents/callback.py index b1a1a3f33..1ff6d2c04 100644 --- a/src/backend/base/langflow/base/agents/callback.py +++ b/src/backend/base/langflow/base/agents/callback.py @@ -13,6 +13,33 @@ class AgentAsyncHandler(AsyncCallbackHandler): def __init__(self, log_function: LogFunctionType | None = None): self.log_function = log_function + async def on_chain_start( + self, + serialized: dict[str, Any], + inputs: dict[str, Any], + *, + run_id: UUID, + parent_run_id: UUID | None = None, + tags: list[str] | None = None, + metadata: dict[str, Any] | None = None, + **kwargs: Any, + ) -> None: + if self.log_function is None: + return + self.log_function( + { + "type": "chain_start", + "serialized": serialized, + "inputs": inputs, + "run_id": run_id, + "parent_run_id": parent_run_id, + "tags": tags, + "metadata": metadata, + **kwargs, + }, + name="Chain Start", + ) + async def on_tool_start( self, serialized: dict[str, Any], diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 836a827d0..559058608 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -1,86 +1,15 @@ -from collections.abc import AsyncIterator, Iterator from typing import cast from langflow.custom import Component from langflow.memory import store_message from langflow.schema import Data from langflow.schema.message import Message -from langflow.services.database.models.message.crud import update_message -from langflow.utils.async_helpers import run_until_complete class ChatComponent(Component): display_name = "Chat Component" description = "Use as base for chat components." - def store_message(self, message: Message) -> Message: - messages = store_message(message, flow_id=self.graph.flow_id) - if len(messages) != 1: - msg = "Only one message can be stored at a time." - raise ValueError(msg) - - stored_message = messages[0] - self._send_message_event(stored_message) - - if self._should_stream_message(stored_message, message): - complete_message = self._stream_message(message, stored_message.id) - stored_message = self._update_stored_message(stored_message.id, complete_message) - - self.status = stored_message - return stored_message - - def _send_message_event(self, message: Message) -> None: - if hasattr(self, "_event_manager") and self._event_manager: - self._event_manager.on_message(data=message.data) - - def _should_stream_message(self, stored_message: Message, original_message: Message) -> bool: - return bool( - hasattr(self, "_event_manager") - and self._event_manager - and stored_message.id - and not isinstance(original_message.text, str) - ) - - def _update_stored_message(self, message_id: str, complete_message: str) -> Message: - message_table = update_message(message_id=message_id, message={"text": complete_message}) - updated_message = Message(**message_table.model_dump()) - self.vertex.added_message = updated_message - return updated_message - - def _process_chunk(self, chunk: str, complete_message: str, message: Message, message_id: str) -> str: - complete_message += chunk - if self._event_manager: - self._event_manager.on_token( - data={ - "text": complete_message, - "chunk": chunk, - "sender": message.sender, - "sender_name": message.sender_name, - "id": str(message_id), - } - ) - return complete_message - - async def _handle_async_iterator(self, iterator: AsyncIterator, message: Message, message_id: str) -> str: - complete_message = "" - async for chunk in iterator: - complete_message = self._process_chunk(chunk.content, complete_message, message, message_id) - return complete_message - - def _stream_message(self, message: Message, message_id: str) -> str: - iterator = message.text - if not isinstance(iterator, AsyncIterator | Iterator): - msg = "The message must be an iterator or an async iterator." - raise TypeError(msg) - - if isinstance(iterator, AsyncIterator): - return run_until_complete(self._handle_async_iterator(iterator, message, message_id)) - - complete_message = "" - for chunk in iterator: - complete_message = self._process_chunk(chunk.content, complete_message, message, message_id) - return complete_message - def build_with_data( self, *, @@ -105,9 +34,31 @@ class ChatComponent(Component): def _create_message(self, input_value, sender, sender_name, files, session_id) -> Message: if isinstance(input_value, Data): return Message.from_data(input_value) - return Message(text=input_value, sender=sender, sender_name=sender_name, files=files, session_id=session_id) + return Message( + text=input_value, + sender=sender, + sender_name=sender_name, + files=files, + session_id=session_id, + category="message", + ) def _send_messages_events(self, messages) -> None: if hasattr(self, "_event_manager") and self._event_manager: for stored_message in messages: - self._event_manager.on_message(data=stored_message.data) + id_ = stored_message.id + self._send_message_event(message=stored_message, id_=id_) + + def get_properties_from_source_component(self): + if self.vertex.incoming_edges: + source_id = self.vertex.incoming_edges[0].source_id + _source_vertex = self.graph.get_vertex(source_id) + component = _source_vertex.custom_component + source = component.display_name + icon = component.icon + possible_attributes = ["model_name", "model_id", "model"] + for attribute in possible_attributes: + if hasattr(component, attribute) and getattr(component, attribute): + return getattr(component, attribute), icon, source, component._id + return source, icon, component.display_name, component._id + return None, None, None, None diff --git a/src/backend/base/langflow/components/inputs/chat.py b/src/backend/base/langflow/components/inputs/chat.py index f52efbab1..be8e656cf 100644 --- a/src/backend/base/langflow/components/inputs/chat.py +++ b/src/backend/base/langflow/components/inputs/chat.py @@ -55,21 +55,43 @@ class ChatInput(ChatComponent): advanced=True, is_list=True, ), + MessageTextInput( + name="background_color", + display_name="Background Color", + info="The background color of the icon.", + advanced=True, + ), + MessageTextInput( + name="chat_icon", + display_name="Icon", + info="The icon of the message.", + advanced=True, + ), + MessageTextInput( + name="text_color", + display_name="Text Color", + info="The text color of the name", + advanced=True, + ), ] outputs = [ Output(display_name="Message", name="message", method="message_response"), ] def message_response(self) -> Message: + _background_color = self.background_color + _text_color = self.text_color + _icon = self.chat_icon message = Message( text=self.input_value, sender=self.sender, sender_name=self.sender_name, session_id=self.session_id, files=self.files, + properties={"background_color": _background_color, "text_color": _text_color, "icon": _icon}, ) if self.session_id and isinstance(message, Message) and self.should_store_message: - stored_message = self.store_message( + stored_message = self.send_message( message, ) self.message.value = stored_message diff --git a/src/backend/base/langflow/components/outputs/chat.py b/src/backend/base/langflow/components/outputs/chat.py index 0493a28b5..20bad1a33 100644 --- a/src/backend/base/langflow/components/outputs/chat.py +++ b/src/backend/base/langflow/components/outputs/chat.py @@ -2,6 +2,7 @@ from langflow.base.io.chat import ChatComponent from langflow.inputs import BoolInput from langflow.io import DropdownInput, MessageTextInput, Output from langflow.schema.message import Message +from langflow.schema.properties import Properties, Source from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER @@ -52,20 +53,64 @@ class ChatOutput(ChatComponent): advanced=True, info="Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", ), + MessageTextInput( + name="background_color", + display_name="Background Color", + info="The background color of the icon.", + advanced=True, + ), + MessageTextInput( + name="chat_icon", + display_name="Icon", + info="The icon of the message.", + advanced=True, + ), + MessageTextInput( + name="text_color", + display_name="Text Color", + info="The text color of the name", + advanced=True, + ), ] outputs = [ - Output(display_name="Message", name="message", method="message_response"), + Output( + display_name="Message", + name="message", + method="message_response", + ), ] + def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source: + source_dict = {} + if _id: + source_dict["id"] = _id + if display_name: + source_dict["display_name"] = display_name + if source: + source_dict["source"] = source + return Source(**source_dict) + def message_response(self) -> Message: + _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 = Message( text=self.input_value, sender=self.sender, sender_name=self.sender_name, session_id=self.session_id, + flow_id=self.graph.flow_id, + properties=Properties( + source=self._build_source(_source_id, _display_name, _source), + icon=_icon, + background_color=_background_color, + text_color=_text_color, + ), ) if self.session_id and isinstance(message, Message) and self.should_store_message: - stored_message = self.store_message( + stored_message = self.send_message( message, ) self.message.value = stored_message diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 22db8d35e..8f6e2e9ea 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -3,6 +3,7 @@ from __future__ import annotations import ast import asyncio import inspect +from collections.abc import AsyncIterator, Iterator from copy import deepcopy from textwrap import dedent from typing import TYPE_CHECKING, Any, ClassVar, get_type_hints @@ -13,12 +14,15 @@ from pydantic import BaseModel, ValidationError from langflow.base.tools.constants import TOOL_OUTPUT_NAME from langflow.custom.tree_visitor import RequiredInputsVisitor +from langflow.exceptions.component import StreamingError from langflow.field_typing import Tool # noqa: TCH001 Needed by _add_toolkit_output from langflow.graph.state.model import create_state_model from langflow.helpers.custom import format_type +from langflow.memory import delete_message, store_message, update_messages from langflow.schema.artifact import get_artifact_type, post_process_raw from langflow.schema.data import Data -from langflow.schema.message import Message +from langflow.schema.message import ErrorMessage, Message +from langflow.schema.properties import Source from langflow.services.settings.feature_flags import FEATURE_FLAGS from langflow.services.tracing.schema import Log from langflow.template.field.base import UNDEFINED, Input, Output @@ -59,7 +63,7 @@ class Component(CustomComponent): inputs: list[InputTypes] = [] outputs: list[Output] = [] code_class_base_inheritance: ClassVar[str] = "Component" - _output_logs: dict[str, Log] = {} + _output_logs: dict[str, list[Log]] = {} _current_output: str = "" _metadata: dict = {} @@ -736,14 +740,30 @@ class Component(CustomComponent): async def _build_without_tracing(self): return await self._build_results() - async def build_results( - self, - ): - if self._tracing_service: - return await self._build_with_tracing() - return await self._build_without_tracing() + async def build_results(self): + """Build the results of the component.""" + try: + if self._tracing_service: + return await self._build_with_tracing() + return await self._build_without_tracing() + except StreamingError as e: + self.send_error( + exception=e.cause, + session_id=self.graph.session_id, + trace_name=getattr(self, "trace_name", None), + source=e.source, + ) + raise e.cause # noqa: B904 + except Exception as e: + self.send_error( + exception=e, + session_id=self.graph.session_id, + source=Source(id=self._id, display_name=self.display_name, source=self.display_name), + trace_name=getattr(self, "trace_name", None), + ) + raise - async def _build_results(self): + async def _build_results(self) -> tuple[dict, dict]: _results = {} _artifacts = {} if hasattr(self, "outputs"): @@ -881,3 +901,132 @@ class Component(CustomComponent): def _append_tool_output(self) -> None: if next((output for output in self.outputs if output.name == TOOL_OUTPUT_NAME), None) is None: self.outputs.append(Output(name=TOOL_OUTPUT_NAME, display_name="Tool", method="to_toolkit", types=["Tool"])) + + def send_message(self, message: Message, id_: str | None = None): + if self.graph.session_id and message is not None and message.session_id is None: + message.session_id = self.graph.session_id + stored_message = self._store_message(message) + + self._stored_message_id = stored_message.id + try: + complete_message = "" + if ( + self._should_stream_message(stored_message, message) + and message is not None + and isinstance(message.text, AsyncIterator | Iterator) + ): + complete_message = self._stream_message(message.text, stored_message) + stored_message.text = complete_message + stored_message = self._update_stored_message(stored_message) + else: + # Only send message event for non-streaming messages + self._send_message_event(stored_message, id_=id_) + except Exception: + # remove the message from the database + delete_message(stored_message.id) + raise + self.status = stored_message + return stored_message + + def _store_message(self, message: Message) -> Message: + messages = store_message(message, flow_id=self.graph.flow_id) + if len(messages) != 1: + msg = "Only one message can be stored at a time." + raise ValueError(msg) + + return messages[0] + + def _send_message_event(self, message: Message, id_: str | None = None): + if hasattr(self, "_event_manager") and self._event_manager: + data_dict = message.data.copy() if hasattr(message, "data") else message.model_dump() + if id_ and not data_dict.get("id"): + data_dict["id"] = id_ + category = data_dict.get("category", None) + match category: + case "error": + self._event_manager.on_error(data=data_dict) + case _: + self._event_manager.on_message(data=data_dict) + + def _should_stream_message(self, stored_message: Message, original_message: Message) -> bool: + return bool( + hasattr(self, "_event_manager") + and self._event_manager + and stored_message.id + and not isinstance(original_message.text, str) + ) + + def _update_stored_message(self, stored_message: Message) -> Message: + message_tables = update_messages(stored_message) + if len(message_tables) != 1: + msg = "Only one message can be updated at a time." + raise ValueError(msg) + message_table = message_tables[0] + updated_message = Message(**message_table.model_dump()) + self.vertex._added_message = updated_message + return updated_message + + def _stream_message(self, iterator: AsyncIterator | Iterator, message: Message) -> str: + if not isinstance(iterator, AsyncIterator | Iterator): + msg = "The message must be an iterator or an async iterator." + raise TypeError(msg) + + if isinstance(iterator, AsyncIterator): + return run_until_complete(self._handle_async_iterator(iterator, message.id, message)) + try: + complete_message = "" + first_chunk = True + for chunk in iterator: + complete_message = self._process_chunk( + chunk.content, complete_message, message.id, message, first_chunk=first_chunk + ) + first_chunk = False + except Exception as e: + raise StreamingError(cause=e, source=message.properties.source) from e + else: + return complete_message + + async def _handle_async_iterator(self, iterator: AsyncIterator, message_id: str, message: Message) -> str: + complete_message = "" + first_chunk = True + async for chunk in iterator: + complete_message = self._process_chunk( + chunk.content, complete_message, message_id, message, first_chunk=first_chunk + ) + first_chunk = False + return complete_message + + def _process_chunk( + self, chunk: str, complete_message: str, message_id: str, message: Message, *, first_chunk: bool = False + ) -> str: + complete_message += chunk + if self._event_manager: + if first_chunk: + # Send the initial message only on the first chunk + msg_copy = message.model_copy() + msg_copy.text = complete_message + self._send_message_event(msg_copy, id_=message_id) + self._event_manager.on_token( + data={ + "chunk": chunk, + "id": str(message_id), + } + ) + return complete_message + + def send_error( + self, + exception: Exception, + session_id: str, + trace_name: str, + source: Source, + ) -> None: + """Send an error message to the frontend.""" + error_message = ErrorMessage( + flow_id=self.graph.flow_id, + exception=exception, + session_id=session_id, + trace_name=trace_name, + source=source, + ) + self.send_message(error_message) diff --git a/src/backend/base/langflow/custom/custom_component/custom_component.py b/src/backend/base/langflow/custom/custom_component/custom_component.py index 7544e4243..1597c7194 100644 --- a/src/backend/base/langflow/custom/custom_component/custom_component.py +++ b/src/backend/base/langflow/custom/custom_component/custom_component.py @@ -83,7 +83,7 @@ class CustomComponent(BaseComponent): _flows_data: list[Data] | None = None _outputs: list[OutputValue] = [] _logs: list[Log] = [] - _output_logs: dict[str, Log] = {} + _output_logs: dict[str, list[Log] | Log] = {} _tracing_service: TracingService | None = None _tree: dict | None = None @@ -93,8 +93,8 @@ class CustomComponent(BaseComponent): Args: **data: Additional keyword arguments to initialize the custom component. """ - self.cache = TTLCache(maxsize=1024, ttl=60) - self._logs = [] + self.cache: TTLCache = TTLCache(maxsize=1024, ttl=60) + self._logs: list[Log] = [] self._results: dict = {} self._artifacts: dict = {} super().__init__(**data) diff --git a/src/backend/base/langflow/events/event_manager.py b/src/backend/base/langflow/events/event_manager.py index ff1aea675..0032c61e7 100644 --- a/src/backend/base/langflow/events/event_manager.py +++ b/src/backend/base/langflow/events/event_manager.py @@ -4,12 +4,14 @@ import json import time import uuid from functools import partial +from typing import Literal from fastapi.encoders import jsonable_encoder +from loguru import logger from typing_extensions import Protocol -from langflow.schema.artifact import CUSTOM_ENCODERS from langflow.schema.log import LoggableType +from langflow.schema.playground_events import create_event_by_type class EventCallback(Protocol): @@ -40,7 +42,12 @@ class EventManager: msg = "Callback must have exactly 3 parameters: manager, event_type, and data" raise ValueError(msg) - def register_event(self, name: str, event_type: str, callback: EventCallback | None = None) -> None: + def register_event( + self, + name: str, + event_type: Literal["message", "error", "warning", "info", "token"], + callback: EventCallback | None = None, + ) -> None: if not name: msg = "Event name cannot be empty" raise ValueError(msg) @@ -53,10 +60,17 @@ class EventManager: _callback = partial(callback, manager=self, event_type=event_type) self.events[name] = _callback - def send_event(self, *, event_type: str, data: LoggableType) -> None: - jsonable_data = jsonable_encoder(data, custom_encoder=CUSTOM_ENCODERS) + def send_event(self, *, event_type: Literal["message", "error", "warning", "info", "token"], data: LoggableType): + try: + if isinstance(data, dict) and event_type in ["message", "error", "warning", "info", "token"]: + data = create_event_by_type(event_type, **data) + except TypeError as e: + logger.debug(f"Error creating playground event: {e}") + except Exception: + raise + jsonable_data = jsonable_encoder(data) json_data = {"event": event_type, "data": jsonable_data} - event_id = uuid.uuid4() + event_id = f"{event_type}-{uuid.uuid4()}" str_data = json.dumps(json_data) + "\n\n" self.queue.put_nowait((event_id, str_data.encode("utf-8"), time.time())) diff --git a/src/backend/base/langflow/exceptions/component.py b/src/backend/base/langflow/exceptions/component.py index 65f8f80ff..1f61ab910 100644 --- a/src/backend/base/langflow/exceptions/component.py +++ b/src/backend/base/langflow/exceptions/component.py @@ -1,6 +1,17 @@ # Create an exception class that receives the message and the formatted traceback + +from langflow.schema.properties import Source + + class ComponentBuildError(Exception): def __init__(self, message: str, formatted_traceback: str): self.message = message self.formatted_traceback = formatted_traceback super().__init__(message) + + +class StreamingError(Exception): + def __init__(self, cause: Exception, source: Source): + self.cause = cause + self.source = source + super().__init__(cause) diff --git a/src/backend/base/langflow/graph/graph/base.py b/src/backend/base/langflow/graph/graph/base.py index 3ce41859a..af06751d2 100644 --- a/src/backend/base/langflow/graph/graph/base.py +++ b/src/backend/base/langflow/graph/graph/base.py @@ -93,6 +93,7 @@ class Graph: self.has_session_id_vertices: list[str] = [] self._sorted_vertices_layers: list[list[str]] = [] self._run_id = "" + self._session_id = "" self._start_time = datetime.now(timezone.utc) self.inactivated_vertices: set = set() self.activated_vertices: list[str] = [] @@ -134,6 +135,14 @@ class Graph: msg = "You must provide both input and output components" raise ValueError(msg) + @property + def session_id(self): + return self._session_id + + @session_id.setter + def session_id(self, value: str): + self._session_id = value + @property def state_model(self): if not self._state_model: @@ -780,7 +789,7 @@ class Graph: Returns: dict: The metadata of the graph. """ - time_format = "%Y-%m-%d %H:%M:%S" + time_format = "%Y-%m-%d %H:%M:%S %Z" return { "start_time": self._start_time.strftime(time_format), "end_time": self._end_time.strftime(time_format), diff --git a/src/backend/base/langflow/graph/vertex/base.py b/src/backend/base/langflow/graph/vertex/base.py index 48853614a..cbcdfcf2a 100644 --- a/src/backend/base/langflow/graph/vertex/base.py +++ b/src/backend/base/langflow/graph/vertex/base.py @@ -94,7 +94,7 @@ class Vertex: self.result: ResultData | None = None self.results: dict[str, Any] = {} self.outputs_logs: dict[str, OutputValue] = {} - self.logs: dict[str, Log] = {} + self.logs: dict[str, list[Log]] = {} self.has_cycle_edges = False try: self.is_interface_component = self.vertex_type in InterfaceComponentTypes @@ -227,6 +227,7 @@ class Vertex: self.output = self.data["node"]["base_classes"] self.display_name: str = self.data["node"].get("display_name", self.id.split("-")[0]) + self.icon: str = self.data["node"].get("icon", self.id.split("-")[0]) self.description: str = self.data["node"].get("description", "") self.frozen: bool = self.data["node"].get("frozen", False) @@ -803,9 +804,9 @@ class Vertex: inputs and isinstance(inputs, dict) and "input_value" in inputs - and inputs["input_value"] is not None + and inputs.get("input_value") is not None ): - chat_input.update({"input_value": inputs[INPUT_FIELD_NAME]}) + chat_input.update({"input_value": inputs.get(INPUT_FIELD_NAME, "")}) if files: chat_input.update({"files": files}) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json b/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json index a30d3bdd2..5da7861b4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Agent Flow.json @@ -159,6 +159,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -175,7 +217,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "_input_type": "FileInput", @@ -318,6 +360,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -382,6 +445,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -398,7 +503,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "_input_type": "MessageTextInput", @@ -519,6 +624,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json index 271741b12..e4fbdc78c 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, World).json @@ -127,6 +127,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -143,7 +185,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -280,6 +322,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -466,6 +529,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -482,7 +587,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -597,6 +702,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json index 07f9ad0ad..e8360e49b 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json @@ -689,6 +689,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -705,7 +747,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -820,6 +862,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json index 14346ba2b..5b04027b6 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Complex Agent.json @@ -1223,6 +1223,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -1239,7 +1281,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -1353,6 +1395,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -2515,6 +2578,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -2531,7 +2636,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -2667,6 +2772,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json index a7f6e193d..40be5ce7d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document QA.json @@ -326,6 +326,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -342,7 +384,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -479,6 +521,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -544,6 +607,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -560,7 +665,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -675,6 +780,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json index 8316145c1..cf0398c6c 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json @@ -927,6 +927,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -943,7 +985,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -1057,6 +1099,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -2243,6 +2306,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -2259,7 +2364,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -2395,6 +2500,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json index fbabee675..3761499a1 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Memory Chatbot.json @@ -300,6 +300,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -316,7 +358,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -453,6 +495,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -852,6 +915,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -868,7 +973,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -983,6 +1088,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json index 3d7b2b1cc..a9502d31a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Agent.json @@ -937,6 +937,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -953,7 +995,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -1067,6 +1109,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json index 2482d8138..e27e84dd3 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json @@ -418,6 +418,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -434,7 +476,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "_input_type": "FileInput", @@ -577,6 +619,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -641,6 +704,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -657,7 +762,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "_input_type": "MessageTextInput", @@ -778,6 +883,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index 95148917b..a3038c0e0 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -310,6 +310,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -326,7 +368,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_USER, MESSAGE_SENDER_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n _background_color = self.background_color\n _text_color = self.text_color\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\"background_color\": _background_color, \"text_color\": _text_color, \"icon\": _icon},\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "files": { "advanced": true, @@ -462,6 +504,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, @@ -1325,6 +1388,48 @@ "pinned": false, "template": { "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -1341,7 +1446,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.store_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Properties, Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, _id: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if _id:\n source_dict[\"id\"] = _id\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\n _source, _icon, _display_name, _source_id = self.get_properties_from_source_component()\n _background_color = self.background_color\n _text_color = self.text_color\n if self.chat_icon:\n _icon = self.chat_icon\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n flow_id=self.graph.flow_id,\n properties=Properties(\n source=self._build_source(_source_id, _display_name, _source),\n icon=_icon,\n background_color=_background_color,\n text_color=_text_color,\n ),\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n" }, "data_template": { "advanced": true, @@ -1455,6 +1560,27 @@ "trace_as_metadata": true, "type": "bool", "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" } } }, diff --git a/src/backend/base/langflow/interface/initialize/loading.py b/src/backend/base/langflow/interface/initialize/loading.py index aacb8e3cc..9bd831112 100644 --- a/src/backend/base/langflow/interface/initialize/loading.py +++ b/src/backend/base/langflow/interface/initialize/loading.py @@ -42,6 +42,7 @@ def instantiate_class( _parameters=custom_params, _vertex=vertex, _tracing_service=get_tracing_service(), + _id=vertex.id, ) if hasattr(custom_component, "set_event_manager"): custom_component.set_event_manager(event_manager) diff --git a/src/backend/base/langflow/memory.py b/src/backend/base/langflow/memory.py index 86958c98a..61461fe24 100644 --- a/src/backend/base/langflow/memory.py +++ b/src/backend/base/langflow/memory.py @@ -1,12 +1,13 @@ +import json from collections.abc import Sequence from uuid import UUID +from langchain_core.chat_history import BaseChatMessageHistory from langchain_core.messages import BaseMessage from loguru import logger from sqlalchemy import delete from sqlmodel import Session, col, select -from langflow.field_typing import BaseChatMessageHistory from langflow.schema.message import Message from langflow.services.database.models.message.model import MessageRead, MessageTable from langflow.services.deps import session_scope @@ -74,6 +75,25 @@ def add_messages(messages: Message | list[Message], flow_id: str | None = None): raise +def update_messages(messages: Message | list[Message]) -> list[Message]: + if not isinstance(messages, list): + messages = [messages] + + with session_scope() as session: + updated_messages: list[MessageTable] = [] + for message in messages: + msg = session.get(MessageTable, message.id) + if msg: + msg.sqlmodel_update(message.model_dump(exclude_unset=True, exclude_none=True)) + session.add(msg) + session.commit() + session.refresh(msg) + updated_messages.append(msg) + else: + logger.warning(f"Message with id {message.id} not found") + return [MessageRead.model_validate(message, from_attributes=True) for message in updated_messages] + + def add_messagetables(messages: list[MessageTable], session: Session): for message in messages: try: @@ -83,7 +103,15 @@ def add_messagetables(messages: list[MessageTable], session: Session): except Exception as e: logger.exception(e) raise - return [MessageRead.model_validate(message, from_attributes=True) for message in messages] + + new_messages = [] + for msg in messages: + msg.properties = json.loads(msg.properties) if isinstance(msg.properties, str) else msg.properties # type: ignore[arg-type] + msg.content_blocks = [json.loads(j) if isinstance(j, str) else j for j in msg.content_blocks] # type: ignore[arg-type] + msg.category = msg.category or "" + new_messages.append(msg) + + return [MessageRead.model_validate(message, from_attributes=True) for message in new_messages] def delete_messages(session_id: str) -> None: @@ -100,6 +128,19 @@ def delete_messages(session_id: str) -> None: ) +def delete_message(id_: str) -> None: + """Delete a message from the monitor service based on the provided ID. + + Args: + id_ (str): The ID of the message to delete. + """ + with session_scope() as session: + message = session.get(MessageTable, id_) + if message: + session.delete(message) + session.commit() + + def store_message( message: Message, flow_id: str | None = None, @@ -124,7 +165,8 @@ def store_message( if not message.session_id or not message.sender or not message.sender_name: msg = "All of session_id, sender, and sender_name must be provided." raise ValueError(msg) - + if hasattr(message, "id") and message.id: + return update_messages([message]) return add_messages([message], flow_id=flow_id) diff --git a/src/backend/base/langflow/schema/content_block.py b/src/backend/base/langflow/schema/content_block.py new file mode 100644 index 000000000..20e7c921e --- /dev/null +++ b/src/backend/base/langflow/schema/content_block.py @@ -0,0 +1,34 @@ +from typing import Annotated + +from pydantic import BaseModel, Field +from typing_extensions import TypedDict + +from .content_types import ContentTypes + +# Create a union type of all content types +ContentType = Annotated[ + ContentTypes, + Field(discriminator="type"), +] + + +class ContentBlock(BaseModel): + """A block of content that can contain different types of content.""" + + title: str + content: ContentType + allow_markdown: bool = Field(default=True) + media_url: list[str] | None = None + + def __init__(self, **data) -> None: + super().__init__(**data) + fields = self.__pydantic_core_schema__["schema"]["fields"] + fields_with_default = (f for f, d in fields.items() if "default" in d["schema"]) + self.model_fields_set.update(fields_with_default) + + +class ContentBlockDict(TypedDict): + title: str + content: ContentType + allow_markdown: bool + media_url: list[str] | None diff --git a/src/backend/base/langflow/schema/content_types.py b/src/backend/base/langflow/schema/content_types.py new file mode 100644 index 000000000..9e9d3a88c --- /dev/null +++ b/src/backend/base/langflow/schema/content_types.py @@ -0,0 +1,94 @@ +from typing import Any, Literal, TypeAlias + +from pydantic import BaseModel, Field + + +class BaseContent(BaseModel): + """Base class for all content types.""" + + type: str = Field(..., description="Type of the content") + + def to_dict(self) -> dict[str, Any]: + return self.model_dump() + + @classmethod + def from_dict(cls, data: dict[str, Any]) -> "BaseContent": + return cls(**data) + + +class ErrorContent(BaseContent): + """Content type for error messages.""" + + type: Literal["error"] = Field(default="error") + component: str | None = None + field: str | None = None + reason: str | None = None + solution: str | None = None + traceback: str | None = None + + +class TextContent(BaseContent): + """Content type for simple text content.""" + + type: Literal["text"] = Field(default="text") + text: str + + +class MediaContent(BaseContent): + """Content type for media content.""" + + type: Literal["media"] = Field(default="media") + urls: list[str] + caption: str | None = None + + +class JSONContent(BaseContent): + """Content type for JSON content.""" + + type: Literal["json"] = Field(default="json") + data: dict[str, Any] + + +class CodeContent(BaseContent): + """Content type for code snippets.""" + + type: Literal["code"] = Field(default="code") + code: str + language: str + title: str | None = None + + +class ToolStartContent(BaseContent): + """Content type for tool start content.""" + + type: Literal["tool_start"] = Field(default="tool_start") + tool_name: str + tool_input: dict[str, Any] + + +class ToolEndContent(BaseContent): + """Content type for tool end content.""" + + type: Literal["tool_end"] = Field(default="tool_end") + tool_name: str + tool_output: Any + + +class ToolErrorContent(BaseContent): + """Content type for tool error content.""" + + type: Literal["tool_error"] = Field(default="tool_error") + tool_name: str + tool_error: str + + +ContentTypes: TypeAlias = ( + ToolStartContent + | ToolEndContent + | ToolErrorContent + | ErrorContent + | TextContent + | MediaContent + | CodeContent + | JSONContent +) diff --git a/src/backend/base/langflow/schema/data.py b/src/backend/base/langflow/schema/data.py index 96971678c..e20e692eb 100644 --- a/src/backend/base/langflow/schema/data.py +++ b/src/backend/base/langflow/schema/data.py @@ -1,6 +1,9 @@ import copy import json +from datetime import datetime +from decimal import Decimal from typing import TYPE_CHECKING, cast +from uuid import UUID from langchain_core.documents import Document from langchain_core.messages import AIMessage, BaseMessage, HumanMessage @@ -8,7 +11,6 @@ from langchain_core.prompts.image import ImagePromptTemplate from loguru import logger from pydantic import BaseModel, model_serializer, model_validator -from langflow.schema.serialize import recursive_serialize_or_str from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER if TYPE_CHECKING: @@ -200,8 +202,7 @@ class Data(BaseModel): # return a JSON string representation of the Data atributes try: data = {k: v.to_json() if hasattr(v, "to_json") else v for k, v in self.data.items()} - data = recursive_serialize_or_str(data) - return json.dumps(data, indent=4) + return serialize_data(data) # use the custom serializer except Exception: # noqa: BLE001 logger.opt(exception=True).debug("Error converting Data to JSON") return str(self.data) @@ -211,3 +212,21 @@ class Data(BaseModel): def __eq__(self, other): return isinstance(other, Data) and self.data == other.data + + +def custom_serializer(obj): + if isinstance(obj, datetime): + return obj.astimezone().isoformat() + if isinstance(obj, Decimal): + return float(obj) + if isinstance(obj, UUID): + return str(obj) + if isinstance(obj, BaseModel): + return obj.model_dump() + # Add more custom serialization rules as needed + msg = f"Type {type(obj)} not serializable" + raise TypeError(msg) + + +def serialize_data(data): + return json.dumps(data, indent=4, default=custom_serializer) diff --git a/src/backend/base/langflow/schema/log.py b/src/backend/base/langflow/schema/log.py index 72a6cb032..b3ddb338a 100644 --- a/src/backend/base/langflow/schema/log.py +++ b/src/backend/base/langflow/schema/log.py @@ -1,10 +1,29 @@ -from typing import TypeAlias +from typing import Literal, TypeAlias from pydantic import BaseModel from typing_extensions import Protocol -LoggableType: TypeAlias = str | dict | list | int | float | bool | None | BaseModel +from langflow.schema.message import ContentBlock, Message +from langflow.schema.playground_events import PlaygroundEvent + +LoggableType: TypeAlias = str | dict | list | int | float | bool | None | BaseModel | PlaygroundEvent class LogFunctionType(Protocol): def __call__(self, message: LoggableType | list[LoggableType], *, name: str | None = None) -> None: ... + + +class SendMessageFunctionType(Protocol): + def __call__( + self, + message: Message | None = None, + text: str | None = None, + background_color: str | None = None, + text_color: str | None = None, + icon: str | None = None, + content_blocks: list[ContentBlock] | None = None, + format_type: Literal["default", "error", "warning", "info"] = "default", + id_: str | None = None, + *, + allow_markdown: bool = True, + ) -> None: ... diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index df7f634fb..a953fbf4c 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -1,9 +1,11 @@ from __future__ import annotations import json +import re +import traceback from collections.abc import AsyncIterator, Iterator from datetime import datetime, timezone -from typing import TYPE_CHECKING, Annotated, Any +from typing import TYPE_CHECKING, Annotated, Any, Literal from uuid import UUID from fastapi.encoders import jsonable_encoder @@ -12,11 +14,15 @@ from langchain_core.messages import AIMessage, BaseMessage, HumanMessage, System from langchain_core.prompts import BaseChatPromptTemplate, ChatPromptTemplate, PromptTemplate from langchain_core.prompts.image import ImagePromptTemplate from loguru import logger -from pydantic import BaseModel, BeforeValidator, ConfigDict, Field, field_serializer, field_validator +from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator from langflow.base.prompts.utils import dict_values_to_string +from langflow.schema.content_block import ContentBlock +from langflow.schema.content_types import ErrorContent from langflow.schema.data import Data from langflow.schema.image import Image, get_file_paths, is_image_file +from langflow.schema.properties import Properties, Source +from langflow.schema.utils import timestamp_to_str_validator # noqa: TCH001 from langflow.utils.constants import ( MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, @@ -28,18 +34,6 @@ if TYPE_CHECKING: from langchain_core.prompt_values import ImagePromptValue -def _timestamp_to_str(timestamp: datetime | str) -> str: - if isinstance(timestamp, str): - # Just check if the string is a valid datetime - try: - datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S") # noqa: DTZ007 - except ValueError as e: - msg = f"Invalid timestamp: {timestamp}" - raise ValueError(msg) from e - return timestamp - return timestamp.strftime("%Y-%m-%d %H:%M:%S") - - class Message(Data): model_config = ConfigDict(arbitrary_types_allowed=True) # Helper class to deal with image data @@ -49,13 +43,17 @@ class Message(Data): sender_name: str | None = None files: list[str | Image] | None = Field(default=[]) session_id: str | None = Field(default="") - timestamp: Annotated[str, BeforeValidator(_timestamp_to_str)] = Field( - default_factory=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S") + timestamp: Annotated[str, timestamp_to_str_validator] = Field( + default_factory=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") ) flow_id: str | UUID | None = None error: bool = Field(default=False) edit: bool = Field(default=False) + properties: Properties = Field(default_factory=Properties) + category: Literal["message", "error", "warning", "info"] | None = "message" + content_blocks: list[ContentBlock] = Field(default_factory=list) + @field_validator("flow_id", mode="before") @classmethod def validate_flow_id(cls, value): @@ -71,7 +69,12 @@ class Message(Data): @field_serializer("timestamp") def serialize_timestamp(self, value): - return datetime.strptime(value, "%Y-%m-%d %H:%M:%S").astimezone(timezone.utc) + try: + # Try parsing with timezone + return datetime.strptime(value.strip(), "%Y-%m-%d %H:%M:%S %Z").astimezone(timezone.utc) + except ValueError: + # Try parsing without timezone + return datetime.strptime(value.strip(), "%Y-%m-%d %H:%M:%S").replace(tzinfo=timezone.utc) @field_validator("files", mode="before") @classmethod @@ -270,6 +273,11 @@ class MessageResponse(DefaultModel): session_id: str text: str files: list[str] = [] + edit: bool + + properties: Properties | None = None + category: str | None = None + content_blocks: list[ContentBlock] | None = None @field_validator("files", mode="before") @classmethod @@ -282,7 +290,7 @@ class MessageResponse(DefaultModel): @classmethod def serialize_timestamp(cls, v): v = v.replace(microsecond=0) - return v.strftime("%Y-%m-%d %H:%M:%S") + return v.strftime("%Y-%m-%d %H:%M:%S %Z") @field_serializer("files") @classmethod @@ -306,3 +314,60 @@ class MessageResponse(DefaultModel): timestamp=message.timestamp, flow_id=flow_id, ) + + +class ErrorMessage(Message): + """A message class specifically for error messages with predefined error-specific attributes.""" + + def __init__( + self, + exception: Exception, + session_id: str, + source: Source, + trace_name: str | None = None, + flow_id: str | None = None, + ) -> None: + # Get the error reason + reason = exception.__class__.__name__ + if hasattr(exception, "body") and "message" in exception.body: + reason = exception.body.get("message") + elif hasattr(exception, "code"): + reason = exception.code + + # Get the sender ID + if trace_name: + match = re.search(r"\((.*?)\)", trace_name) + if match: + match.group(1) + + super().__init__( + session_id=session_id, + sender=source.display_name, + sender_name=source.display_name, + text=reason, + properties=Properties( + text_color="red", + background_color="red", + edited=False, + source=source, + icon="error", + allow_markdown=False, + targets=[], + ), + category="error", + error=True, + content_blocks=[ + ContentBlock( + title="Error", + content=ErrorContent( + type="error", + component=source.display_name, + field=str(exception.field) if hasattr(exception, "field") else None, + reason=reason, + solution=str(exception.solution) if hasattr(exception, "solution") else None, + traceback=traceback.format_exc(), + ), + ) + ], + flow_id=flow_id, + ) diff --git a/src/backend/base/langflow/schema/playground_events.py b/src/backend/base/langflow/schema/playground_events.py new file mode 100644 index 000000000..d66bd39de --- /dev/null +++ b/src/backend/base/langflow/schema/playground_events.py @@ -0,0 +1,180 @@ +import inspect +from collections.abc import Callable +from datetime import datetime, timezone +from typing import Annotated, Literal +from uuid import UUID + +from pydantic import BaseModel, ConfigDict, Field, field_serializer, field_validator + +from langflow.schema.content_block import ContentBlock +from langflow.schema.properties import Properties +from langflow.schema.utils import timestamp_to_str_validator +from langflow.utils.constants import MESSAGE_SENDER_USER + + +class PlaygroundEvent(BaseModel): + model_config = ConfigDict(extra="allow", populate_by_name=True) + properties: Properties | None = Field(default=None) + sender_name: str | None = Field(default=None) + content_blocks: list[ContentBlock] | None = Field(default=None) + format_type: Literal["default", "error", "warning", "info"] = Field(default="default") + files: list[str] | None = Field(default=None) + text: str | None = Field(default=None) + timestamp: Annotated[str, timestamp_to_str_validator] = Field( + default_factory=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") + ) + id_: UUID | str | None = Field(alias="id") + + @field_serializer("timestamp") + @classmethod + def serialize_timestamp(cls, v: str) -> str: + return v + + @field_validator("id_") + @classmethod + def validate_id(cls, v: UUID | str | None) -> str | None: + if isinstance(v, UUID): + return str(v) + return v + + +class MessageEvent(PlaygroundEvent): + category: Literal["message", "error", "warning", "info"] = "message" + format_type: Literal["default", "error", "warning", "info"] = Field(default="default") + session_id: str | None = Field(default=None) + error: bool = Field(default=False) + edit: bool = Field(default=False) + flow_id: UUID | str | None = Field(default=None) + sender: str = Field(default=MESSAGE_SENDER_USER) + sender_name: str = Field(default="User") + + @field_validator("flow_id") + @classmethod + def validate_flow_id(cls, v: UUID | str | None) -> str | None: + if isinstance(v, UUID): + return str(v) + return v + + +class ErrorEvent(MessageEvent): + background_color: str = Field(default="#FF0000") + text_color: str = Field(default="#FFFFFF") + format_type: Literal["default", "error", "warning", "info"] = Field(default="error") + allow_markdown: bool = Field(default=False) + category: Literal["error"] = "error" + + +class WarningEvent(PlaygroundEvent): + background_color: str = Field(default="#FFA500") + text_color: str = Field(default="#000000") + format_type: Literal["default", "error", "warning", "info"] = Field(default="warning") + + +class InfoEvent(PlaygroundEvent): + background_color: str = Field(default="#0000FF") + text_color: str = Field(default="#FFFFFF") + format_type: Literal["default", "error", "warning", "info"] = Field(default="info") + + +class TokenEvent(BaseModel): + chunk: str = Field(...) + id: UUID | str | None = Field(alias="id") + timestamp: Annotated[str, timestamp_to_str_validator] = Field( + default_factory=lambda: datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S %Z") + ) + + +# Factory functions first +def create_message( + text: str, + category: Literal["message", "error", "warning", "info"] = "message", + properties: dict | None = None, + content_blocks: list[ContentBlock] | None = None, + sender_name: str | None = None, + files: list[str] | None = None, + timestamp: str | None = None, + format_type: Literal["default", "error", "warning", "info"] = "default", + sender: str | None = None, + session_id: str | None = None, + id: UUID | str | None = None, # noqa: A002 + flow_id: UUID | str | None = None, + *, + error: bool = False, + edit: bool = False, +) -> MessageEvent: + return MessageEvent( + text=text, + properties=properties, + category=category, + content_blocks=content_blocks, + sender_name=sender_name, + files=files, + timestamp=timestamp, + format_type=format_type, + sender=sender, + id=id, + session_id=session_id, + error=error, + edit=edit, + flow_id=flow_id, + ) + + +def create_error( + text: str, + properties: dict | None = None, + traceback: str | None = None, + title: str = "Error", + timestamp: str | None = None, + id: UUID | str | None = None, # noqa: A002 + flow_id: UUID | str | None = None, + session_id: str | None = None, + content_blocks: list[ContentBlock] | None = None, +) -> ErrorEvent: + if traceback: + content_blocks = content_blocks or [] + content_blocks += [ContentBlock(title=title, content=traceback)] + return ErrorEvent( + text=text, + properties=properties, + content_blocks=content_blocks, + timestamp=timestamp, + id=id, + flow_id=flow_id, + session_id=session_id, + ) + + +def create_warning(message: str) -> WarningEvent: + return WarningEvent(text=message) + + +def create_info(message: str) -> InfoEvent: + return InfoEvent(text=message) + + +def create_token(chunk: str, id: str) -> TokenEvent: # noqa: A002 + return TokenEvent( + chunk=chunk, + id=id, + ) + + +_EVENT_CREATORS: dict[str, tuple[Callable, inspect.Signature]] = { + "message": (create_message, inspect.signature(create_message)), + "error": (create_error, inspect.signature(create_error)), + "warning": (create_warning, inspect.signature(create_warning)), + "info": (create_info, inspect.signature(create_info)), + "token": (create_token, inspect.signature(create_token)), +} + + +def create_event_by_type( + event_type: Literal["message", "error", "warning", "info", "token"], **kwargs +) -> PlaygroundEvent | dict: + if event_type not in _EVENT_CREATORS: + return kwargs + + creator_func, signature = _EVENT_CREATORS[event_type] + valid_params = {k: v for k, v in kwargs.items() if k in signature.parameters} + return creator_func(**valid_params) diff --git a/src/backend/base/langflow/schema/properties.py b/src/backend/base/langflow/schema/properties.py new file mode 100644 index 000000000..ecb7bfe0a --- /dev/null +++ b/src/backend/base/langflow/schema/properties.py @@ -0,0 +1,27 @@ +from pydantic import BaseModel, Field, field_validator + + +class Source(BaseModel): + id: str = Field(default="", description="The id of the source component.") + display_name: str = Field(default="", description="The display name of the source component.") + source: str = Field( + default="", + description="The source of the message. Normally used to display the model name (e.g. 'gpt-4o')", + ) + + +class Properties(BaseModel): + text_color: str | None = None + background_color: str | None = None + edited: bool = False + source: Source = Field(default_factory=Source) + icon: str | None = None + allow_markdown: bool = False + targets: list = [] + + @field_validator("source", mode="before") + @classmethod + def validate_source(cls, v): + if isinstance(v, str): + return Source(id=v, display_name=v, source=v) + return v diff --git a/src/backend/base/langflow/schema/utils.py b/src/backend/base/langflow/schema/utils.py new file mode 100644 index 000000000..ecaaa4f7f --- /dev/null +++ b/src/backend/base/langflow/schema/utils.py @@ -0,0 +1,20 @@ +from datetime import datetime + +from pydantic import BeforeValidator + + +def timestamp_to_str(timestamp: datetime | str) -> str: + if isinstance(timestamp, str): + # Just check if the string is a valid datetime + try: + datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S %Z") # noqa: DTZ007 + result = timestamp + except ValueError as e: + msg = f"Invalid timestamp: {timestamp}" + raise ValueError(msg) from e + else: + result = timestamp.strftime("%Y-%m-%d %H:%M:%S %Z") + return result + + +timestamp_to_str_validator = BeforeValidator(timestamp_to_str) diff --git a/src/backend/base/langflow/services/database/models/message/model.py b/src/backend/base/langflow/services/database/models/message/model.py index 89d26bab6..45391fe02 100644 --- a/src/backend/base/langflow/services/database/models/message/model.py +++ b/src/backend/base/langflow/services/database/models/message/model.py @@ -2,10 +2,13 @@ from datetime import datetime, timezone from typing import TYPE_CHECKING from uuid import UUID, uuid4 -from pydantic import field_validator +from pydantic import field_serializer, field_validator from sqlalchemy import Text from sqlmodel import JSON, Column, Field, Relationship, SQLModel +from langflow.schema.content_block import ContentBlock, ContentBlockDict +from langflow.schema.properties import Properties + if TYPE_CHECKING: from langflow.schema.message import Message from langflow.services.database.models.flow.model import Flow @@ -21,6 +24,10 @@ class MessageBase(SQLModel): error: bool = Field(default=False) edit: bool = Field(default=False) + properties: Properties = Field(default_factory=Properties) + category: str = Field(default="message") + content_blocks: list[ContentBlock] = Field(default_factory=list) + @field_validator("files", mode="before") @classmethod def validate_files(cls, value): @@ -44,10 +51,12 @@ class MessageBase(SQLModel): message.files = image_paths if isinstance(message.timestamp, str): - # The message.timestamp is created using strftime("%Y-%m-%dT%H:%M:%S"). - # This format is not fully ISO 8601 compliant because it lacks timezone information. - # Aadd timezone info (UTC) back to the timestamp here. - timestamp = datetime.fromisoformat(message.timestamp).replace(tzinfo=timezone.utc) + # Convert timestamp string in format "YYYY-MM-DD HH:MM:SS UTC" to datetime + try: + timestamp = datetime.strptime(message.timestamp, "%Y-%m-%d %H:%M:%S %Z").replace(tzinfo=timezone.utc) + except ValueError: + # Fallback for ISO format if the above fails + timestamp = datetime.fromisoformat(message.timestamp).replace(tzinfo=timezone.utc) else: timestamp = message.timestamp if not flow_id and message.flow_id: @@ -55,6 +64,17 @@ class MessageBase(SQLModel): # If the text is not a string, it means it could be # async iterator so we simply add it as an empty string message_text = "" if not isinstance(message.text, str) else message.text + + properties = ( + message.properties.model_dump_json() + if hasattr(message.properties, "model_dump_json") + else message.properties + ) + content_blocks = [] + for content_block in message.content_blocks or []: + content = content_block.model_dump_json() if hasattr(content_block, "model_dump_json") else content_block + content_blocks.append(content) + return cls( sender=message.sender, sender_name=message.sender_name, @@ -63,6 +83,9 @@ class MessageBase(SQLModel): files=message.files or [], timestamp=timestamp, flow_id=flow_id, + properties=properties, + category=message.category, + content_blocks=content_blocks, ) @@ -72,6 +95,9 @@ class MessageTable(MessageBase, table=True): # type: ignore[call-arg] flow_id: UUID | None = Field(default=None, foreign_key="flow.id") flow: "Flow" = Relationship(back_populates="messages") files: list[str] = Field(sa_column=Column(JSON)) + properties: Properties = Field(default_factory=lambda: Properties().model_dump(), sa_column=Column(JSON)) # type: ignore[assignment] + category: str = Field(sa_column=Column(Text)) + content_blocks: list[ContentBlockDict] = Field(default_factory=list, sa_column=Column(JSON)) # type: ignore[assignment] @field_validator("flow_id", mode="before") @classmethod @@ -82,6 +108,20 @@ class MessageTable(MessageBase, table=True): # type: ignore[call-arg] value = UUID(value) return value + @field_validator("properties") + @classmethod + def validate_properties(cls, value): + if hasattr(value, "model_dump"): + return value.model_dump() + return value + + @field_serializer("properties") + @classmethod + def serialize_properties(cls, value): + if hasattr(value, "model_dump"): + return value.model_dump() + return value + # Needed for Column(JSON) class Config: arbitrary_types_allowed = True diff --git a/src/backend/tests/integration/components/outputs/test_chat_output.py b/src/backend/tests/integration/components/outputs/test_chat_output.py index 24fd047e2..dfe113cc9 100644 --- a/src/backend/tests/integration/components/outputs/test_chat_output.py +++ b/src/backend/tests/integration/components/outputs/test_chat_output.py @@ -24,7 +24,7 @@ async def test_message(): async def test_do_not_store_message(): session_id = "test-session-id" outputs = await run_single_component( - ChatOutput, inputs={"input_value": "hello", "should_store_message": True}, session_id=session_id + ChatOutput, inputs={"input_value": Message(text="hello"), "should_store_message": True}, session_id=session_id ) assert isinstance(outputs["message"], Message) assert outputs["message"].text == "hello" @@ -33,7 +33,7 @@ async def test_do_not_store_message(): session_id = "test-session-id-another" outputs = await run_single_component( - ChatOutput, inputs={"input_value": "hello", "should_store_message": False}, session_id=session_id + ChatOutput, inputs={"input_value": Message(text="hello"), "should_store_message": False}, session_id=session_id ) assert isinstance(outputs["message"], Message) assert outputs["message"].text == "hello" diff --git a/src/backend/tests/unit/base/tools/test_component_toolkit.py b/src/backend/tests/unit/base/tools/test_component_toolkit.py index cdde22577..f5424d3e5 100644 --- a/src/backend/tests/unit/base/tools/test_component_toolkit.py +++ b/src/backend/tests/unit/base/tools/test_component_toolkit.py @@ -72,6 +72,24 @@ def test_component_tool(): "title": "Files", "type": "array", }, + "background_color": { + "default": "", + "description": "The background color of the icon.", + "title": "Background Color", + "type": "string", + }, + "chat_icon": { + "default": "", + "description": "The icon of the message.", + "title": "Chat Icon", + "type": "string", + }, + "text_color": { + "default": "", + "description": "The text color of the name", + "title": "Text Color", + "type": "string", + }, } assert component_toolkit.component == chat_input diff --git a/src/backend/tests/unit/events/test_event_manager.py b/src/backend/tests/unit/events/test_event_manager.py index 1a7a0d84f..4b15518d1 100644 --- a/src/backend/tests/unit/events/test_event_manager.py +++ b/src/backend/tests/unit/events/test_event_manager.py @@ -198,7 +198,16 @@ class TestEventManager: assert len(queue.data) == 1 event_id, str_data, timestamp = queue.data[0] - assert isinstance(event_id, uuid.UUID) + # event_id follows this pattern: f"{event_type}-{uuid.uuid4()}" + event_type_from_id = event_id.split("-")[0] + assert event_type_from_id == "test_type" + uuid_from_id = event_id.split(event_type_from_id)[1] + assert isinstance(uuid_from_id, str) + # assert that the uuid_from_id is a valid uuid + try: + uuid.UUID(uuid_from_id) + except ValueError: + pytest.fail(f"Invalid UUID: {uuid_from_id}") assert isinstance(str_data, bytes) assert isinstance(timestamp, float) diff --git a/src/backend/tests/unit/graph/graph/test_callback_graph.py b/src/backend/tests/unit/graph/graph/test_callback_graph.py index 30fd86e18..f497924e9 100644 --- a/src/backend/tests/unit/graph/graph/test_callback_graph.py +++ b/src/backend/tests/unit/graph/graph/test_callback_graph.py @@ -10,7 +10,7 @@ from langflow.template import Output class LogComponent(Component): - name = "LogComponent" + display_name = "LogComponent" inputs = [IntInput(name="times", value=1)] outputs = [Output(name="call_log", method="call_log_method")] diff --git a/src/frontend/src/assets/logo.svg b/src/frontend/src/assets/logo.svg new file mode 100644 index 000000000..24c721e4a --- /dev/null +++ b/src/frontend/src/assets/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/frontend/src/components/cardComponent/index.tsx b/src/frontend/src/components/cardComponent/index.tsx index d5d2ab645..a92aa1e23 100644 --- a/src/frontend/src/components/cardComponent/index.tsx +++ b/src/frontend/src/components/cardComponent/index.tsx @@ -40,7 +40,7 @@ export default function CollectionCardComponent({ const setErrorData = useAlertStore((state) => state.setErrorData); const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow); const getFlowById = useFlowsManagerStore((state) => state.getFlowById); - const [openPlayground, setOpenPlayground] = useState(false); + // const [openPlayground, setOpenPlayground] = useState(false); const [loadingPlayground, setLoadingPlayground] = useState(false); const selectedFlowsComponentsCards = useFlowsManagerStore( (state) => state.selectedFlowsComponentsCards, @@ -75,7 +75,7 @@ export default function CollectionCardComponent({ return; } setCurrentFlow(data); - setOpenPlayground(true); + // setOpenPlayground(true); setLoadingPlayground(false); } else { setErrorData({ @@ -156,7 +156,7 @@ export default function CollectionCardComponent({
- {playground && ( + {/* {playground && (
- {openPlayground && ( + {/* {openPlayground && ( <> - )} + )} */} ); } diff --git a/src/frontend/src/components/cardsWrapComponent/index.tsx b/src/frontend/src/components/cardsWrapComponent/index.tsx index c7ca01588..3dec7c8e2 100644 --- a/src/frontend/src/components/cardsWrapComponent/index.tsx +++ b/src/frontend/src/components/cardsWrapComponent/index.tsx @@ -1,3 +1,4 @@ +import useFlowsManagerStore from "@/stores/flowsManagerStore"; import { useEffect, useState } from "react"; import IconComponent from "../../components/genericIconComponent"; import { cn } from "../../utils/utils"; @@ -12,6 +13,7 @@ export default function CardsWrapComponent({ dragMessage?: string; }) { const [isDragging, setIsDragging] = useState(false); + const isIOModalOpen = useFlowsManagerStore((state) => state.IOModalOpen); useEffect(() => { // Function to handle visibility change const handleVisibilityChange = () => { @@ -32,13 +34,21 @@ export default function CardsWrapComponent({ const dragOver = (e) => { e.preventDefault(); - if (e.dataTransfer.types.some((types) => types === "Files") && onFileDrop) { + if ( + e.dataTransfer.types.some((types) => types === "Files") && + onFileDrop && + !isIOModalOpen + ) { setIsDragging(true); } }; const dragEnter = (e) => { - if (e.dataTransfer.types.some((types) => types === "Files") && onFileDrop) { + if ( + e.dataTransfer.types.some((types) => types === "Files") && + onFileDrop && + !isIOModalOpen + ) { setIsDragging(true); } e.preventDefault(); @@ -46,12 +56,12 @@ export default function CardsWrapComponent({ const dragLeave = (e) => { e.preventDefault(); - if (onFileDrop) setIsDragging(false); + if (onFileDrop && !isIOModalOpen) setIsDragging(false); }; const onDrop = (e) => { e.preventDefault(); - if (onFileDrop) onFileDrop(e); + if (onFileDrop && !isIOModalOpen) onFileDrop(e); setIsDragging(false); }; diff --git a/src/frontend/src/components/codeTabsComponent/ChatCodeTabComponent.tsx b/src/frontend/src/components/codeTabsComponent/ChatCodeTabComponent.tsx index a1809314e..4ac235540 100644 --- a/src/frontend/src/components/codeTabsComponent/ChatCodeTabComponent.tsx +++ b/src/frontend/src/components/codeTabsComponent/ChatCodeTabComponent.tsx @@ -31,13 +31,14 @@ export default function SimplifiedCodeTabComponent({ }; return ( -
-
- {language} +
+
+ {language} -
+ +
+ + +
+
); }; diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatInput/newChatInput.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatInput/newChatInput.tsx index ea5c54e3c..c1902b50f 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatInput/newChatInput.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatInput/newChatInput.tsx @@ -1,7 +1,9 @@ import { Button } from "@/components/ui/button"; +import Loading from "@/components/ui/loading"; import { usePostUploadFile } from "@/controllers/API/queries/files/use-post-upload-file"; import useFileSizeValidator from "@/shared/hooks/use-file-size-validator"; import useAlertStore from "@/stores/alertStore"; +import useFlowStore from "@/stores/flowStore"; import { useEffect, useRef, useState } from "react"; import ShortUniqueId from "short-unique-id"; import { @@ -38,6 +40,7 @@ export default function ChatInput({ const fileInputRef = useRef(null); const setErrorData = useAlertStore((state) => state.setErrorData); const { validateFileSize } = useFileSizeValidator(setErrorData); + const stopBuilding = useFlowStore((state) => state.stopBuilding); useFocusOnUnlock(lockChat, inputRef); useAutoResizeTextArea(chatValue, inputRef); @@ -150,7 +153,7 @@ export default function ChatInput({ ); }; - const classNameFilePreview = `flex w-full items-center gap-2 bg-background py-2 overflow-auto custom-scroll`; + const classNameFilePreview = `flex w-full items-center gap-2 py-2 overflow-auto custom-scroll`; const handleButtonClick = () => { fileInputRef.current!.click(); @@ -164,17 +167,33 @@ export default function ChatInput({ if (noInput) { return (
-
- +
+ {!lockChat ? ( + + ) : ( + + )} +

Add a{" "} -

+
+
+ Chain logo +
+
+ ); +} diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageButton/newMessageOptions.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageButton/newMessageOptions.tsx index f878f0091..dc87c362e 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageButton/newMessageOptions.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/components/editMessageButton/newMessageOptions.tsx @@ -31,14 +31,14 @@ export function EditMessageButton({ return (
-
+
@@ -48,12 +48,12 @@ export function EditMessageButton({ content={isCopied ? "Copied!" : "Copy message"} side="top" > -
+