diff --git a/src/backend/base/langflow/base/io/chat.py b/src/backend/base/langflow/base/io/chat.py index 0ee15bde3..4b89062c2 100644 --- a/src/backend/base/langflow/base/io/chat.py +++ b/src/backend/base/langflow/base/io/chat.py @@ -49,18 +49,6 @@ class ChatComponent(Component): }, } - def store_message( - self, - message: Message, - ) -> list[Message]: - messages = store_message( - message, - flow_id=self.graph.flow_id, - ) - - self.status = messages - return messages - def build_with_data( self, sender: Optional[str] = "User", @@ -86,5 +74,9 @@ class ChatComponent(Component): self.status = message_text if session_id and isinstance(message, Message) and isinstance(message.text, str): - self.store_message(message) + messages = store_message( + message, + flow_id=self.graph.flow_id, + ) + self.status = messages return message_text # type: ignore diff --git a/src/backend/base/langflow/base/memory/model.py b/src/backend/base/langflow/base/memory/model.py index 22f9a02a3..a940dd0a7 100644 --- a/src/backend/base/langflow/base/memory/model.py +++ b/src/backend/base/langflow/base/memory/model.py @@ -11,13 +11,13 @@ class LCChatMemoryComponent(Component): outputs = [ Output( display_name="Memory", - name="base_memory", - method="build_base_memory", + name="memory", + method="build_message_history", ) ] def _validate_outputs(self): - required_output_methods = ["build_base_memory"] + required_output_methods = ["build_message_history"] output_names = [output.name for output in self.outputs] for method_name in required_output_methods: if method_name not in output_names: diff --git a/src/backend/base/langflow/components/helpers/Memory.py b/src/backend/base/langflow/components/helpers/Memory.py index db5fe6acc..7c27b23a4 100644 --- a/src/backend/base/langflow/components/helpers/Memory.py +++ b/src/backend/base/langflow/components/helpers/Memory.py @@ -1,18 +1,27 @@ from langflow.custom import Component from langflow.helpers.data import data_to_text +from langflow.inputs import HandleInput from langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output -from langflow.memory import get_messages +from langflow.memory import get_messages, LCBuiltinChatMemory from langflow.schema import Data from langflow.schema.message import Message +from langflow.field_typing import BaseChatMemory +from langchain.memory import ConversationBufferMemory class MemoryComponent(Component): display_name = "Chat Memory" - description = "Retrieves stored chat messages." + description = "Retrieves stored chat messages from Langflow tables or an external memory." icon = "message-square-more" name = "Memory" inputs = [ + HandleInput( + name="memory", + display_name="External Memory", + input_types=["BaseChatMessageHistory"], + info="Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + ), DropdownInput( name="sender", display_name="Sender Type", @@ -58,8 +67,9 @@ class MemoryComponent(Component): ] outputs = [ - Output(display_name="Chat History", name="messages", method="retrieve_messages"), + Output(display_name="Messages (Data)", name="messages", method="retrieve_messages"), Output(display_name="Messages (Text)", name="messages_text", method="retrieve_messages_as_text"), + Output(display_name="Memory", name="lc_memory", method="build_lc_memory"), ] def retrieve_messages(self) -> Data: @@ -72,17 +82,38 @@ class MemoryComponent(Component): if sender == "Machine and User": sender = None - messages = get_messages( - sender=sender, - sender_name=sender_name, - session_id=session_id, - limit=n_messages, - order=order, - ) - self.status = messages - return messages + if self.memory: + # override session_id + self.memory.session_id = session_id + + stored = self.memory.messages + if sender: + expected_type = "Machine" if sender == "Machine" else "User" + stored = [m for m in stored if m.type == expected_type] + if order == "ASC": + stored = stored[::-1] + if n_messages: + stored = stored[:n_messages] + stored = [Message.from_lc_message(m) for m in stored] + else: + stored = get_messages( + sender=sender, + sender_name=sender_name, + session_id=session_id, + limit=n_messages, + order=order, + ) + self.status = stored + return stored def retrieve_messages_as_text(self) -> Message: - messages_text = data_to_text(self.template, self.retrieve_messages()) - self.status = messages_text - return Message(text=messages_text) + stored_text = data_to_text(self.template, self.retrieve_messages()) + self.status = stored_text + return Message(text=stored_text) + + def build_lc_memory(self) -> BaseChatMemory: + if self.memory: + chat_memory = self.memory + else: + chat_memory = LCBuiltinChatMemory(flow_id=self.graph.flow_id, session_id=self.session_id) + return ConversationBufferMemory(chat_memory=chat_memory) diff --git a/src/backend/base/langflow/components/helpers/StoreMessage.py b/src/backend/base/langflow/components/helpers/StoreMessage.py index ce138bb9c..0b0a56148 100644 --- a/src/backend/base/langflow/components/helpers/StoreMessage.py +++ b/src/backend/base/langflow/components/helpers/StoreMessage.py @@ -1,5 +1,5 @@ from langflow.custom import Component -from langflow.inputs import MessageInput, StrInput +from langflow.inputs import MessageInput, StrInput, HandleInput from langflow.schema.message import Message from langflow.template import Output from langflow.memory import get_messages, store_message @@ -7,12 +7,18 @@ from langflow.memory import get_messages, store_message class StoreMessageComponent(Component): display_name = "Store Message" - description = "Stores a chat message or text." + description = "Stores a chat message or text into Langflow tables or an external memory." icon = "save" name = "StoreMessage" inputs = [ MessageInput(name="message", display_name="Message", info="The chat message to be stored.", required=True), + HandleInput( + name="memory", + display_name="External Memory", + input_types=["BaseChatMessageHistory"], + info="The external memory to store the message. If empty, it will use the Langflow tables.", + ), StrInput( name="sender", display_name="Sender", @@ -42,7 +48,17 @@ class StoreMessageComponent(Component): message.sender = self.sender or message.sender message.sender_name = self.sender_name or message.sender_name - store_message(message, flow_id=self.graph.flow_id) - stored = get_messages(session_id=message.session_id, sender_name=message.sender_name, sender=message.sender) + if self.memory: + # override session_id + self.memory.session_id = message.session_id + lc_message = message.to_lc_message() + self.memory.add_messages([lc_message]) + stored = self.memory.messages + stored = [Message.from_lc_message(m) for m in stored] + if message.sender: + stored = [m for m in stored if m.sender == message.sender] + else: + store_message(message, flow_id=self.graph.flow_id) + stored = get_messages(session_id=message.session_id, sender_name=message.sender_name, sender=message.sender) self.status = stored return stored diff --git a/src/backend/base/langflow/components/inputs/ChatInput.py b/src/backend/base/langflow/components/inputs/ChatInput.py index b921b1673..41620eb3e 100644 --- a/src/backend/base/langflow/components/inputs/ChatInput.py +++ b/src/backend/base/langflow/components/inputs/ChatInput.py @@ -1,6 +1,8 @@ from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES from langflow.base.io.chat import ChatComponent +from langflow.inputs import BoolInput from langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output +from langflow.memory import store_message from langflow.schema.message import Message @@ -17,6 +19,12 @@ class ChatInput(ChatComponent): value="", info="Message to be passed as input.", ), + BoolInput( + name="store_message", + display_name="Store Messages", + info="Store the message in the history.", + value=True, + ), DropdownInput( name="sender", display_name="Sender Type", @@ -56,8 +64,12 @@ class ChatInput(ChatComponent): session_id=self.session_id, files=self.files, ) + if self.session_id and isinstance(message, Message) and isinstance(message.text, str): - self.store_message(message) + store_message( + message, + flow_id=self.graph.flow_id, + ) self.message.value = message self.status = message diff --git a/src/backend/base/langflow/components/outputs/ChatOutput.py b/src/backend/base/langflow/components/outputs/ChatOutput.py index e1f40ec76..9e5bb85ac 100644 --- a/src/backend/base/langflow/components/outputs/ChatOutput.py +++ b/src/backend/base/langflow/components/outputs/ChatOutput.py @@ -1,5 +1,7 @@ from langflow.base.io.chat import ChatComponent +from langflow.inputs import BoolInput from langflow.io import DropdownInput, MessageTextInput, Output +from langflow.memory import store_message from langflow.schema.message import Message @@ -15,6 +17,12 @@ class ChatOutput(ChatComponent): display_name="Text", info="Message to be passed as output.", ), + BoolInput( + name="store_message", + display_name="Store Messages", + info="Store the message in the history.", + value=True, + ), DropdownInput( name="sender", display_name="Sender Type", @@ -49,7 +57,10 @@ class ChatOutput(ChatComponent): session_id=self.session_id, ) if self.session_id and isinstance(message, Message) and isinstance(message.text, str): - self.store_message(message) + store_message( + message, + flow_id=self.graph.flow_id, + ) self.message.value = message self.status = message 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 9411190a0..51ba3d616 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 @@ -1,138 +1,22 @@ { + "id": "9570a502-34bb-44bf-aa15-9f92e7d10efe", "data": { - "edges": [ - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-x6Vrd", - "name": "message", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "user_message", - "id": "Prompt-FEEsr", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ChatInput-x6Vrd{œdataTypeœ:œChatInputœ,œidœ:œChatInput-x6Vrdœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-FEEsr{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-FEEsrœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-x6Vrd", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-x6Vrdœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-FEEsr", - "targetHandle": "{œfieldNameœ: œuser_messageœ, œidœ: œPrompt-FEEsrœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "Prompt", - "id": "Prompt-FEEsr", - "name": "prompt", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-M4C3O", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Prompt-FEEsr{œdataTypeœ:œPromptœ,œidœ:œPrompt-FEEsrœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-M4C3O{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-M4C3Oœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "source": "Prompt-FEEsr", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-FEEsrœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-M4C3O", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-M4C3Oœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-M4C3O", - "name": "text_output", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-gpEZp", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-OpenAIModel-M4C3O{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-M4C3Oœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-gpEZp{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-gpEZpœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "source": "OpenAIModel-M4C3O", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-M4C3Oœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-gpEZp", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-gpEZpœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-zJQHe", - "name": "messages_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "context", - "id": "Prompt-FEEsr", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Memory-zJQHe{œdataTypeœ:œMemoryœ,œidœ:œMemory-zJQHeœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-FEEsr{œfieldNameœ:œcontextœ,œidœ:œPrompt-FEEsrœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "Memory-zJQHe", - "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-zJQHeœ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-FEEsr", - "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-FEEsrœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - } - ], "nodes": [ { "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-FEEsr", + "id": "Prompt-qeJau", "node": { - "base_classes": [ - "Message" - ], + "base_classes": ["Message"], "beta": false, "conditional_paths": [], - "custom_fields": { - "template": [ - "context", - "user_message" - ] - }, + "custom_fields": { "template": ["context", "user_message"] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": [ - "template" - ], + "field_order": ["template"], "frozen": false, "icon": "prompts", "output_types": [], @@ -143,9 +27,7 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": [ - "Message" - ], + "types": ["Message"], "value": "__UNDEFINED__" } ], @@ -178,10 +60,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Message", - "Text" - ], + "input_types": ["Message", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -218,10 +97,7 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": [ - "Message", - "Text" - ], + "input_types": ["Message", "Text"], "list": false, "load_from_db": false, "multiline": true, @@ -240,15 +116,9 @@ }, "dragging": false, "height": 517, - "id": "Prompt-FEEsr", - "position": { - "x": 1880.8227904110583, - "y": 625.8049209882275 - }, - "positionAbsolute": { - "x": 1880.8227904110583, - "y": 625.8049209882275 - }, + "id": "Prompt-qeJau", + "position": { "x": 1880.8227904110583, "y": 625.8049209882275 }, + "positionAbsolute": { "x": 1880.8227904110583, "y": 625.8049209882275 }, "selected": false, "type": "genericNode", "width": 384 @@ -257,66 +127,13 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-x6Vrd", + "id": "ChatInput-NFUUk", "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Get chat inputs from the Playground.", - "display_name": "Chat Input", - "documentation": "", - "edited": false, - "field_order": [ - "input_value", - "sender", - "sender_name", - "session_id", - "files" - ], - "frozen": false, - "icon": "ChatInput", - "output_types": [], - "outputs": [ - { - "cache": true, - "display_name": "Message", - "method": "message_response", - "name": "message", - "selected": "Message", - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, "template": { "_type": "Component", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "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.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.schema.message import Message\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\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 DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"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=\"User\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", 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 isinstance(message.text, str):\n self.store_message(message)\n self.message.value = message\n\n self.status = message\n return message\n" - }, "files": { - "advanced": true, - "display_name": "Files", - "dynamic": false, + "trace_as_metadata": true, + "file_path": "", "fileTypes": [ "txt", "md", @@ -342,112 +159,162 @@ "bmp", "image" ], - "file_path": "", - "info": "Files to be sent with the message.", "list": true, - "name": "files", - "placeholder": "", "required": false, + "placeholder": "", "show": true, + "value": "", + "name": "files", + "display_name": "Files", + "advanced": true, + "dynamic": false, + "info": "Files to be sent with the message.", "title_case": false, - "trace_as_metadata": true, - "type": "file", - "value": "" + "type": "file" + }, + "code": { + "type": "code", + "required": true, + "placeholder": "", + "list": false, + "show": true, + "multiline": true, + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import HandleInput, BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\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=\"store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"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=\"User\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", 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\n if self.session_id and isinstance(message, Message) and isinstance(message.text, str):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n", + "fileTypes": [], + "file_path": "", + "password": false, + "name": "code", + "advanced": true, + "dynamic": true, + "info": "", + "load_from_db": false, + "title_case": false }, "input_value": { - "advanced": false, + "trace_as_input": true, + "multiline": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "input_value", "display_name": "Text", + "advanced": false, + "input_types": ["Message"], "dynamic": false, "info": "Message to be passed as input.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "quando è stato costrutui" + "type": "str" }, "sender": { - "advanced": true, + "trace_as_metadata": true, + "options": ["Machine", "User"], + "required": false, + "placeholder": "", + "show": true, + "value": "User", + "name": "sender", "display_name": "Sender Type", + "advanced": true, "dynamic": false, "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User" - ], - "placeholder": "", - "required": false, - "show": true, "title_case": false, - "trace_as_metadata": true, - "type": "str", - "value": "User" + "type": "str" }, "sender_name": { - "advanced": true, + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "User", + "name": "sender_name", "display_name": "Sender Name", + "advanced": true, + "input_types": ["Message"], "dynamic": false, "info": "Name of the sender.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "User" + "type": "str" }, "session_id": { - "advanced": true, - "display_name": "Session ID", - "dynamic": false, - "info": "Session ID for the message.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "" + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "session_id", + "display_name": "Session ID", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Session ID for the message.", + "title_case": false, + "type": "str" + }, + "store_message": { + "trace_as_metadata": true, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": true, + "name": "store_message", + "display_name": "Store Messages", + "advanced": false, + "dynamic": false, + "info": "Store the message in the history.", + "title_case": false, + "type": "bool" } - } + }, + "description": "Get chat inputs from the Playground.", + "icon": "ChatInput", + "base_classes": ["Message"], + "display_name": "Chat Input", + "documentation": "", + "custom_fields": {}, + "output_types": [], + "pinned": false, + "conditional_paths": [], + "frozen": false, + "outputs": [ + { + "types": ["Message"], + "selected": "Message", + "name": "message", + "display_name": "Message", + "method": "message_response", + "value": "__UNDEFINED__", + "cache": true + } + ], + "field_order": [ + "input_value", + "store_message", + "sender", + "sender_name", + "session_id", + "files" + ], + "beta": false, + "edited": false }, "type": "ChatInput" }, "dragging": false, - "height": 309, - "id": "ChatInput-x6Vrd", - "position": { - "x": 1275.9262193671882, - "y": 836.1228056896347 - }, - "positionAbsolute": { - "x": 1275.9262193671882, - "y": 836.1228056896347 - }, + "height": 385, + "id": "ChatInput-NFUUk", + "position": { "x": 1275.9262193671882, "y": 836.1228056896347 }, + "positionAbsolute": { "x": 1275.9262193671882, "y": 836.1228056896347 }, "selected": false, "type": "genericNode", "width": 384 @@ -456,12 +323,9 @@ "data": { "description": "Generates text using OpenAI LLMs.", "display_name": "OpenAI", - "id": "OpenAIModel-M4C3O", + "id": "OpenAIModel-gGnwp", "node": { - "base_classes": [ - "LanguageModel", - "Message" - ], + "base_classes": ["LanguageModel", "Message"], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -493,9 +357,7 @@ "method": "text_response", "name": "text_output", "selected": "Message", - "types": [ - "Message" - ], + "types": ["Message"], "value": "__UNDEFINED__" }, { @@ -504,9 +366,7 @@ "method": "build_model", "name": "model_output", "selected": "LanguageModel", - "types": [ - "LanguageModel" - ], + "types": ["LanguageModel"], "value": "__UNDEFINED__" } ], @@ -536,9 +396,7 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": [ - "Message" - ], + "input_types": ["Message"], "list": false, "load_from_db": false, "name": "input_value", @@ -732,16 +590,10 @@ }, "dragging": false, "height": 623, - "id": "OpenAIModel-M4C3O", - "position": { - "x": 2468.968379487559, - "y": 560.0689522326683 - }, - "positionAbsolute": { - "x": 2468.968379487559, - "y": 560.0689522326683 - }, - "selected": false, + "id": "OpenAIModel-gGnwp", + "position": { "x": 2468.968379487559, "y": 560.0689522326683 }, + "positionAbsolute": { "x": 2468.968379487559, "y": 560.0689522326683 }, + "selected": true, "type": "genericNode", "width": 384 }, @@ -749,192 +601,356 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-gpEZp", + "id": "ChatOutput-yJml1", "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, + "template": { + "_type": "Component", + "code": { + "type": "code", + "required": true, + "placeholder": "", + "list": false, + "show": true, + "multiline": true, + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import HandleInput, BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\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=\"store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True\n ),\n MessageTextInput(\n name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", 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 isinstance(message.text, str):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n", + "fileTypes": [], + "file_path": "", + "password": false, + "name": "code", + "advanced": true, + "dynamic": true, + "info": "", + "load_from_db": false, + "title_case": false + }, + "data_template": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "{text}", + "name": "data_template", + "display_name": "Data Template", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", + "title_case": false, + "type": "str" + }, + "input_value": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "input_value", + "display_name": "Text", + "advanced": false, + "input_types": ["Message"], + "dynamic": false, + "info": "Message to be passed as output.", + "title_case": false, + "type": "str" + }, + "sender": { + "trace_as_metadata": true, + "options": ["Machine", "User"], + "required": false, + "placeholder": "", + "show": true, + "value": "Machine", + "name": "sender", + "display_name": "Sender Type", + "advanced": true, + "dynamic": false, + "info": "Type of sender.", + "title_case": false, + "type": "str" + }, + "sender_name": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "AI", + "name": "sender_name", + "display_name": "Sender Name", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Name of the sender.", + "title_case": false, + "type": "str" + }, + "session_id": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "session_id", + "display_name": "Session ID", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Session ID for the message.", + "title_case": false, + "type": "str" + }, + "store_message": { + "trace_as_metadata": true, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": true, + "name": "store_message", + "display_name": "Store Messages", + "advanced": false, + "dynamic": false, + "info": "Store the message in the history.", + "title_case": false, + "type": "bool" + } + }, "description": "Display a chat message in the Playground.", + "icon": "ChatOutput", + "base_classes": ["Message"], "display_name": "Chat Output", "documentation": "", - "edited": false, + "custom_fields": {}, + "output_types": [], + "pinned": false, + "conditional_paths": [], + "frozen": false, + "outputs": [ + { + "types": ["Message"], + "selected": "Message", + "name": "message", + "display_name": "Message", + "method": "message_response", + "value": "__UNDEFINED__", + "cache": true + } + ], "field_order": [ "input_value", + "store_message", "sender", "sender_name", "session_id", "data_template" ], - "frozen": false, - "icon": "ChatOutput", - "output_types": [], - "outputs": [ - { - "cache": true, - "display_name": "Message", - "method": "message_response", - "name": "message", - "selected": "Message", - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\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 DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\"],\n value=\"Machine\",\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\", display_name=\"Sender Name\", info=\"Name of the sender.\", value=\"AI\", advanced=True\n ),\n MessageTextInput(\n name=\"session_id\", display_name=\"Session ID\", info=\"Session ID for the message.\", 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 isinstance(message.text, str):\n self.store_message(message)\n self.message.value = message\n\n self.status = message\n return message\n" - }, - "data_template": { - "advanced": true, - "display_name": "Data Template", - "dynamic": false, - "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "data_template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "{text}" - }, - "input_value": { - "advanced": false, - "display_name": "Text", - "dynamic": false, - "info": "Message to be passed as output.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User" - ], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "str", - "value": "Machine" - }, - "sender_name": { - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "info": "Name of the sender.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "AI" - }, - "session_id": { - "advanced": true, - "display_name": "Session ID", - "dynamic": false, - "info": "Session ID for the message.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - } + "beta": false, + "edited": false }, "type": "ChatOutput" }, - "height": 309, - "id": "ChatOutput-gpEZp", - "position": { - "x": 3083.1710516244116, - "y": 701.521688846004 - }, + "height": 385, + "id": "ChatOutput-yJml1", + "position": { "x": 3083.1710516244116, "y": 701.521688846004 }, "selected": false, "type": "genericNode", "width": 384 }, { "data": { - "description": "Retrieves stored chat messages.", + "description": "Retrieves stored chat messages from Langflow tables or an external memory.", "display_name": "Chat Memory", - "id": "Memory-zJQHe", + "id": "Memory-1cBNz", "node": { - "base_classes": [ - "Data", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Retrieves stored chat messages.", + "template": { + "_type": "Component", + "memory": { + "trace_as_metadata": true, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "memory", + "display_name": "External Memory", + "advanced": false, + "input_types": ["BaseChatMessageHistory"], + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "title_case": false, + "type": "other" + }, + "code": { + "type": "code", + "required": true, + "placeholder": "", + "list": false, + "show": true, + "multiline": true, + "value": "from typing import List, Sequence\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import get_messages, LCBuiltinChatMemory\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\nfrom langflow.field_typing import BaseChatMemory\nfrom langchain.memory import ConversationBufferMemory\n\n\nclass MemoryComponent(Component):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n\n inputs = [\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"BaseChatMessageHistory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\", \"Machine and User\"],\n value=\"Machine and 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 advanced=True,\n ),\n IntInput(\n name=\"n_messages\",\n display_name=\"Number of Messages\",\n value=100,\n info=\"Number of messages to retrieve.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"Session ID of the chat history.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"order\",\n display_name=\"Order\",\n options=[\"Ascending\", \"Descending\"],\n value=\"Ascending\",\n info=\"Order of the messages.\",\n advanced=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Messages (Data)\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Messages (Text)\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"Memory\", name=\"lc_memory\", method=\"build_lc_memory\"),\n ]\n\n def retrieve_messages(self) -> Data:\n sender = self.sender\n sender_name = self.sender_name\n session_id = self.session_id\n n_messages = self.n_messages\n order = \"DESC\" if self.order == \"Descending\" else \"ASC\"\n\n if sender == \"Machine and User\":\n sender = None\n\n if self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = self.memory.messages\n if sender:\n expected_type = \"Machine\" if sender == \"Machine\" else \"User\"\n stored = [m for m in stored if m.type == expected_type]\n if order == \"ASC\":\n stored = stored[::-1]\n if n_messages:\n stored = stored[:n_messages]\n stored = [Message.from_lc_message(m) for m in stored]\n else:\n stored = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n self.status = stored\n return stored\n\n def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n def build_lc_memory(self) -> BaseChatMemory:\n if self.memory:\n chat_memory = self.memory\n else:\n chat_memory = LCBuiltinChatMemory(flow_id=self.graph.flow_id, session_id=self.session_id)\n return ConversationBufferMemory(chat_memory=chat_memory)", + "fileTypes": [], + "file_path": "", + "password": false, + "name": "code", + "advanced": true, + "dynamic": true, + "info": "", + "load_from_db": false, + "title_case": false + }, + "n_messages": { + "trace_as_metadata": true, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": 100, + "name": "n_messages", + "display_name": "Number of Messages", + "advanced": true, + "dynamic": false, + "info": "Number of messages to retrieve.", + "title_case": false, + "type": "int" + }, + "order": { + "trace_as_metadata": true, + "options": ["Ascending", "Descending"], + "required": false, + "placeholder": "", + "show": true, + "value": "Ascending", + "name": "order", + "display_name": "Order", + "advanced": true, + "dynamic": false, + "info": "Order of the messages.", + "title_case": false, + "type": "str" + }, + "sender": { + "trace_as_metadata": true, + "options": ["Machine", "User", "Machine and User"], + "required": false, + "placeholder": "", + "show": true, + "value": "Machine and User", + "name": "sender", + "display_name": "Sender Type", + "advanced": true, + "dynamic": false, + "info": "Type of sender.", + "title_case": false, + "type": "str" + }, + "sender_name": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "sender_name", + "display_name": "Sender Name", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Name of the sender.", + "title_case": false, + "type": "str" + }, + "session_id": { + "trace_as_input": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "", + "name": "session_id", + "display_name": "Session ID", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "Session ID of the chat history.", + "title_case": false, + "type": "str" + }, + "template": { + "trace_as_input": true, + "multiline": true, + "trace_as_metadata": true, + "load_from_db": false, + "list": false, + "required": false, + "placeholder": "", + "show": true, + "value": "{sender_name}: {text}", + "name": "template", + "display_name": "Template", + "advanced": true, + "input_types": ["Message"], + "dynamic": false, + "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", + "title_case": false, + "type": "str" + } + }, + "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "icon": "message-square-more", + "base_classes": ["BaseChatMemory", "Data", "Message"], "display_name": "Chat Memory", "documentation": "", - "edited": false, + "custom_fields": {}, + "output_types": [], + "pinned": false, + "conditional_paths": [], + "frozen": false, + "outputs": [ + { + "types": ["Data"], + "selected": "Data", + "name": "messages", + "display_name": "Messages (Data)", + "method": "retrieve_messages", + "value": "__UNDEFINED__", + "cache": true + }, + { + "types": ["Message"], + "selected": "Message", + "name": "messages_text", + "display_name": "Messages (Text)", + "method": "retrieve_messages_as_text", + "value": "__UNDEFINED__", + "cache": true + }, + { + "types": ["BaseChatMemory"], + "selected": "BaseChatMemory", + "name": "lc_memory", + "display_name": "Memory", + "method": "build_lc_memory", + "value": "__UNDEFINED__", + "cache": true + } + ], "field_order": [ + "memory", "sender", "sender_name", "n_messages", @@ -942,197 +958,120 @@ "order", "template" ], - "frozen": false, - "icon": "message-square-more", - "output_types": [], - "outputs": [ - { - "cache": true, - "display_name": "Chat History", - "method": "retrieve_messages", - "name": "messages", - "selected": "Data", - "types": [ - "Data" - ], - "value": "__UNDEFINED__" - }, - { - "cache": true, - "display_name": "Messages (Text)", - "method": "retrieve_messages_as_text", - "name": "messages_text", - "selected": "Message", - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "code": { - "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", - "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "code", - "value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import get_messages\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass MemoryComponent(Component):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n\n inputs = [\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[\"Machine\", \"User\", \"Machine and User\"],\n value=\"Machine and 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 advanced=True,\n ),\n IntInput(\n name=\"n_messages\",\n display_name=\"Number of Messages\",\n value=100,\n info=\"Number of messages to retrieve.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"Session ID of the chat history.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"order\",\n display_name=\"Order\",\n options=[\"Ascending\", \"Descending\"],\n value=\"Ascending\",\n info=\"Order of the messages.\",\n advanced=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Chat History\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Messages (Text)\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n ]\n\n def retrieve_messages(self) -> Data:\n sender = self.sender\n sender_name = self.sender_name\n session_id = self.session_id\n n_messages = self.n_messages\n order = \"DESC\" if self.order == \"Descending\" else \"ASC\"\n\n if sender == \"Machine and User\":\n sender = None\n\n messages = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n self.status = messages\n return messages\n\n def retrieve_messages_as_text(self) -> Message:\n messages_text = data_to_text(self.template, self.retrieve_messages())\n self.status = messages_text\n return Message(text=messages_text)\n" - }, - "n_messages": { - "advanced": true, - "display_name": "Number of Messages", - "dynamic": false, - "info": "Number of messages to retrieve.", - "list": false, - "name": "n_messages", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "int", - "value": 100 - }, - "order": { - "advanced": true, - "display_name": "Order", - "dynamic": false, - "info": "Order of the messages.", - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "str", - "value": "Ascending" - }, - "sender": { - "advanced": true, - "display_name": "Sender Type", - "dynamic": false, - "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "str", - "value": "Machine and User" - }, - "sender_name": { - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "info": "Name of the sender.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "session_id": { - "advanced": true, - "display_name": "Session ID", - "dynamic": false, - "info": "Session ID of the chat history.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "template": { - "advanced": true, - "display_name": "Template", - "dynamic": false, - "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "{sender_name}: {text}" - } - } + "beta": false, + "edited": false }, "type": "Memory" }, "dragging": false, - "height": 267, - "id": "Memory-zJQHe", - "position": { - "x": 1301.98330242754, - "y": 422.33865605652574 - }, - "positionAbsolute": { - "x": 1301.98330242754, - "y": 422.33865605652574 - }, + "height": 387, + "id": "Memory-1cBNz", + "position": { "x": 1301.98330242754, "y": 422.33865605652574 }, + "positionAbsolute": { "x": 1301.98330242754, "y": 422.33865605652574 }, "selected": false, "type": "genericNode", "width": 384 } ], + "edges": [ + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-NFUUk", + "name": "message", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "user_message", + "id": "Prompt-qeJau", + "inputTypes": ["Message", "Text"], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-NFUUk{œdataTypeœ:œChatInputœ,œidœ:œChatInput-NFUUkœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-qeJau{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-qeJauœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-NFUUk", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-NFUUkœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-qeJau", + "targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-qeJauœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-qeJau", + "name": "prompt", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "OpenAIModel-gGnwp", + "inputTypes": ["Message"], + "type": "str" + } + }, + "id": "reactflow__edge-Prompt-qeJau{œdataTypeœ:œPromptœ,œidœ:œPrompt-qeJauœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-gGnwp{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gGnwpœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "Prompt-qeJau", + "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-qeJauœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", + "target": "OpenAIModel-gGnwp", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gGnwpœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "OpenAIModel", + "id": "OpenAIModel-gGnwp", + "name": "text_output", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-yJml1", + "inputTypes": ["Message"], + "type": "str" + } + }, + "id": "reactflow__edge-OpenAIModel-gGnwp{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gGnwpœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-yJml1{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-yJml1œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-gGnwp", + "sourceHandle": "{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gGnwpœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "ChatOutput-yJml1", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-yJml1œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-1cBNz", + "name": "messages_text", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "context", + "id": "Prompt-qeJau", + "inputTypes": ["Message", "Text"], + "type": "str" + } + }, + "id": "reactflow__edge-Memory-1cBNz{œdataTypeœ:œMemoryœ,œidœ:œMemory-1cBNzœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-qeJau{œfieldNameœ:œcontextœ,œidœ:œPrompt-qeJauœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "Memory-1cBNz", + "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-1cBNzœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-qeJau", + "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-qeJauœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + } + ], "viewport": { - "x": -556.4579979699035, - "y": -102.83844480905748, + "x": -377.45799796990354, + "y": 18.161555190942522, "zoom": 0.45494095964690673 } }, "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", - "endpoint_name": null, - "id": "ec014aa6-106a-45bf-a898-bceddd9041e0", - "is_component": false, + "name": "memory_good", "last_tested_version": "1.0.7", - "name": "Memory Chatbot" -} \ No newline at end of file + "endpoint_name": null, + "is_component": false +} diff --git a/src/backend/base/langflow/memory.py b/src/backend/base/langflow/memory.py index f052acdee..f87629327 100644 --- a/src/backend/base/langflow/memory.py +++ b/src/backend/base/langflow/memory.py @@ -1,4 +1,5 @@ import warnings +from typing import List, Sequence from uuid import UUID from loguru import logger @@ -9,6 +10,8 @@ from langflow.schema.message import Message from langflow.services.database.models.message.model import MessageRead, MessageTable from langflow.services.database.utils import migrate_messages_from_monitor_service_to_database from langflow.services.deps import session_scope +from langflow.field_typing import BaseChatMessageHistory +from langchain_core.messages import BaseMessage def get_messages( @@ -19,7 +22,7 @@ def get_messages( order: str | None = "DESC", flow_id: UUID | None = None, limit: int | None = None, -): +) -> List[Message]: """ Retrieves messages from the monitor service based on the provided filters. @@ -136,3 +139,29 @@ def store_message( raise ValueError("All of session_id, sender, and sender_name must be provided.") return add_messages([message], flow_id=flow_id) + + +class LCBuiltinChatMemory(BaseChatMessageHistory): + def __init__( + self, + flow_id: str, + session_id: str, + ) -> None: + self.flow_id = flow_id + self.session_id = session_id + + @property + def messages(self) -> List[BaseMessage]: + messages = get_messages( + session_id=self.session_id, + ) + return [m.to_lc_message() for m in messages] + + def add_messages(self, messages: Sequence[BaseMessage]) -> None: + for lc_message in messages: + message = Message.from_lc_message(lc_message) + message.session_id = self.session_id + store_message(message, flow_id=self.flow_id) + + def clear(self) -> None: + delete_messages(self.session_id) diff --git a/src/backend/base/langflow/schema/message.py b/src/backend/base/langflow/schema/message.py index 28839cf98..483315021 100644 --- a/src/backend/base/langflow/schema/message.py +++ b/src/backend/base/langflow/schema/message.py @@ -102,6 +102,19 @@ class Message(Data): return AIMessage(content=text) # type: ignore + @classmethod + def from_lc_message(cls, lc_message: BaseMessage) -> "Message": + if lc_message.type == "human": + sender = "User" + elif lc_message.type == "ai": + sender = "Machine" + elif lc_message.type == "system": + sender = "System" + else: + sender = lc_message.type + + return cls(text=lc_message.content, sender=sender, sender_name=sender) + @classmethod def from_data(cls, data: "Data") -> "Message": """