diff --git a/docs/docs/Integrations/Notion/Conversational_Notion_Agent.json b/docs/docs/Integrations/Notion/Conversational_Notion_Agent.json index e977769bc..8c0126b59 100644 --- a/docs/docs/Integrations/Notion/Conversational_Notion_Agent.json +++ b/docs/docs/Integrations/Notion/Conversational_Notion_Agent.json @@ -1,3137 +1,3668 @@ { - "id": "e070f0be-edc4-4512-bb0f-e53307062a26", - "data": { - "nodes": [ - { - "id": "AddContentToPage-ZezUn", - "type": "genericNode", - "position": { - "x": 1416.217259177943, - "y": 1709.6205867919527 - }, - "data": { - "type": "AddContentToPage", - "node": { - "template": { - "_type": "Component", - "block_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "block_id", - "value": "", - "display_name": "Page/Block ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the page/block to add the content.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import json\nfrom typing import Dict, Any, Union\nfrom markdown import markdown\nfrom bs4 import BeautifulSoup\nimport requests\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom pydantic import BaseModel, Field\n\n\nclass AddContentToPage(LCToolComponent):\n display_name: str = \"Add Content to Page \"\n description: str = \"Convert markdown text to Notion blocks and append them to a Notion page.\"\n documentation: str = \"https://developers.notion.com/reference/patch-block-children\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n MultilineInput(\n name=\"markdown_text\",\n display_name=\"Markdown Text\",\n info=\"The markdown text to convert to Notion blocks.\",\n ),\n StrInput(\n name=\"block_id\",\n display_name=\"Page/Block ID\",\n info=\"The ID of the page/block to add the content.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class AddContentToPageSchema(BaseModel):\n markdown_text: str = Field(..., description=\"The markdown text to convert to Notion blocks.\")\n block_id: str = Field(..., description=\"The ID of the page/block to add the content.\")\n\n def run_model(self) -> Data:\n result = self._add_content_to_page(self.markdown_text, self.block_id)\n return Data(data=result, text=json.dumps(result))\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"add_content_to_notion_page\",\n description=\"Convert markdown text to Notion blocks and append them to a Notion page.\",\n func=self._add_content_to_page,\n args_schema=self.AddContentToPageSchema,\n )\n\n def _add_content_to_page(self, markdown_text: str, block_id: str) -> Union[Dict[str, Any], str]:\n try:\n html_text = markdown(markdown_text)\n soup = BeautifulSoup(html_text, \"html.parser\")\n blocks = self.process_node(soup)\n\n url = f\"https://api.notion.com/v1/blocks/{block_id}/children\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"children\": blocks,\n }\n\n response = requests.patch(url, headers=headers, json=data)\n response.raise_for_status()\n\n return response.json()\n except requests.exceptions.RequestException as e:\n error_message = f\"Error: Failed to add content to Notion page. {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n except Exception as e:\n return f\"Error: An unexpected error occurred while adding content to Notion page. {str(e)}\"\n\n def process_node(self, node):\n blocks = []\n if isinstance(node, str):\n text = node.strip()\n if text:\n if text.startswith(\"#\"):\n heading_level = text.count(\"#\", 0, 6)\n heading_text = text[heading_level:].strip()\n if heading_level == 1:\n blocks.append(self.create_block(\"heading_1\", heading_text))\n elif heading_level == 2:\n blocks.append(self.create_block(\"heading_2\", heading_text))\n elif heading_level == 3:\n blocks.append(self.create_block(\"heading_3\", heading_text))\n else:\n blocks.append(self.create_block(\"paragraph\", text))\n elif node.name == \"h1\":\n blocks.append(self.create_block(\"heading_1\", node.get_text(strip=True)))\n elif node.name == \"h2\":\n blocks.append(self.create_block(\"heading_2\", node.get_text(strip=True)))\n elif node.name == \"h3\":\n blocks.append(self.create_block(\"heading_3\", node.get_text(strip=True)))\n elif node.name == \"p\":\n code_node = node.find(\"code\")\n if code_node:\n code_text = code_node.get_text()\n language, code = self.extract_language_and_code(code_text)\n blocks.append(self.create_block(\"code\", code, language=language))\n elif self.is_table(str(node)):\n blocks.extend(self.process_table(node))\n else:\n blocks.append(self.create_block(\"paragraph\", node.get_text(strip=True)))\n elif node.name == \"ul\":\n blocks.extend(self.process_list(node, \"bulleted_list_item\"))\n elif node.name == \"ol\":\n blocks.extend(self.process_list(node, \"numbered_list_item\"))\n elif node.name == \"blockquote\":\n blocks.append(self.create_block(\"quote\", node.get_text(strip=True)))\n elif node.name == \"hr\":\n blocks.append(self.create_block(\"divider\", \"\"))\n elif node.name == \"img\":\n blocks.append(self.create_block(\"image\", \"\", image_url=node.get(\"src\")))\n elif node.name == \"a\":\n blocks.append(self.create_block(\"bookmark\", node.get_text(strip=True), link_url=node.get(\"href\")))\n elif node.name == \"table\":\n blocks.extend(self.process_table(node))\n\n for child in node.children:\n if isinstance(child, str):\n continue\n blocks.extend(self.process_node(child))\n\n return blocks\n\n def extract_language_and_code(self, code_text):\n lines = code_text.split(\"\\n\")\n language = lines[0].strip()\n code = \"\\n\".join(lines[1:]).strip()\n return language, code\n\n def is_code_block(self, text):\n return text.startswith(\"```\")\n\n def extract_code_block(self, text):\n lines = text.split(\"\\n\")\n language = lines[0].strip(\"`\").strip()\n code = \"\\n\".join(lines[1:]).strip(\"`\").strip()\n return language, code\n\n def is_table(self, text):\n rows = text.split(\"\\n\")\n if len(rows) < 2:\n return False\n\n has_separator = False\n for i, row in enumerate(rows):\n if \"|\" in row:\n cells = [cell.strip() for cell in row.split(\"|\")]\n cells = [cell for cell in cells if cell] # Remove empty cells\n if i == 1 and all(set(cell) <= set(\"-|\") for cell in cells):\n has_separator = True\n elif not cells:\n return False\n\n return has_separator and len(rows) >= 3\n\n def process_list(self, node, list_type):\n blocks = []\n for item in node.find_all(\"li\"):\n item_text = item.get_text(strip=True)\n checked = item_text.startswith(\"[x]\")\n is_checklist = item_text.startswith(\"[ ]\") or checked\n\n if is_checklist:\n item_text = item_text.replace(\"[x]\", \"\").replace(\"[ ]\", \"\").strip()\n blocks.append(self.create_block(\"to_do\", item_text, checked=checked))\n else:\n blocks.append(self.create_block(list_type, item_text))\n return blocks\n\n def process_table(self, node):\n blocks = []\n header_row = node.find(\"thead\").find(\"tr\") if node.find(\"thead\") else None\n body_rows = node.find(\"tbody\").find_all(\"tr\") if node.find(\"tbody\") else []\n\n if header_row or body_rows:\n table_width = max(\n len(header_row.find_all([\"th\", \"td\"])) if header_row else 0,\n max(len(row.find_all([\"th\", \"td\"])) for row in body_rows),\n )\n\n table_block = self.create_block(\"table\", \"\", table_width=table_width, has_column_header=bool(header_row))\n blocks.append(table_block)\n\n if header_row:\n header_cells = [cell.get_text(strip=True) for cell in header_row.find_all([\"th\", \"td\"])]\n header_row_block = self.create_block(\"table_row\", header_cells)\n blocks.append(header_row_block)\n\n for row in body_rows:\n cells = [cell.get_text(strip=True) for cell in row.find_all([\"th\", \"td\"])]\n row_block = self.create_block(\"table_row\", cells)\n blocks.append(row_block)\n\n return blocks\n\n def create_block(self, block_type: str, content: str, **kwargs) -> Dict[str, Any]:\n block: dict[str, Any] = {\n \"object\": \"block\",\n \"type\": block_type,\n block_type: {},\n }\n\n if block_type in [\n \"paragraph\",\n \"heading_1\",\n \"heading_2\",\n \"heading_3\",\n \"bulleted_list_item\",\n \"numbered_list_item\",\n \"quote\",\n ]:\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n elif block_type == \"to_do\":\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n block[block_type][\"checked\"] = kwargs.get(\"checked\", False)\n elif block_type == \"code\":\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n block[block_type][\"language\"] = kwargs.get(\"language\", \"plain text\")\n elif block_type == \"image\":\n block[block_type] = {\"type\": \"external\", \"external\": {\"url\": kwargs.get(\"image_url\", \"\")}}\n elif block_type == \"divider\":\n pass\n elif block_type == \"bookmark\":\n block[block_type][\"url\"] = kwargs.get(\"link_url\", \"\")\n elif block_type == \"table\":\n block[block_type][\"table_width\"] = kwargs.get(\"table_width\", 0)\n block[block_type][\"has_column_header\"] = kwargs.get(\"has_column_header\", False)\n block[block_type][\"has_row_header\"] = kwargs.get(\"has_row_header\", False)\n elif block_type == \"table_row\":\n block[block_type][\"cells\"] = [[{\"type\": \"text\", \"text\": {\"content\": cell}} for cell in content]]\n\n return block\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "markdown_text": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "markdown_text", - "value": "", - "display_name": "Markdown Text", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The markdown text to convert to Notion blocks.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - } - }, - "description": "Convert markdown text to Notion blocks and append them to a Notion page.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "Add Content to Page ", - "documentation": "https://developers.notion.com/reference/patch-block-children", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "markdown_text", - "block_id", - "notion_secret" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "AddContentToPage-ZezUn", - "description": "Convert markdown text to Notion blocks and append them to a Notion page.", - "display_name": "Add Content to Page " - }, - "selected": false, - "width": 384, - "height": 330, - "dragging": false, - "positionAbsolute": { - "x": 1416.217259177943, - "y": 1709.6205867919527 - } - }, - { - "id": "NotionPageCreator-6SCB5", - "type": "genericNode", - "position": { - "x": 1413.9782390799146, - "y": 2051.645785494985 - }, - "data": { - "type": "NotionPageCreator", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import json\nfrom typing import Dict, Any, Union\nimport requests\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionPageCreator(LCToolComponent):\n display_name: str = \"Create Page \"\n description: str = \"A component for creating Notion pages.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/page-create\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n MultilineInput(\n name=\"properties_json\",\n display_name=\"Properties (JSON)\",\n info=\"The properties of the new page as a JSON string.\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageCreatorSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database.\")\n properties_json: str = Field(..., description=\"The properties of the new page as a JSON string.\")\n\n def run_model(self) -> Data:\n result = self._create_notion_page(self.database_id, self.properties_json)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the created page data\n output = \"Created page properties:\\n\"\n for prop_name, prop_value in result.get(\"properties\", {}).items():\n output += f\"{prop_name}: {prop_value}\\n\"\n return Data(text=output, data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"create_notion_page\",\n description=\"Create a new page in a Notion database. IMPORTANT: Use the tool to check the Database properties for more details before using this tool.\",\n func=self._create_notion_page,\n args_schema=self.NotionPageCreatorSchema,\n )\n\n def _create_notion_page(self, database_id: str, properties_json: str) -> Union[Dict[str, Any], str]:\n if not database_id or not properties_json:\n return \"Invalid input. Please provide 'database_id' and 'properties_json'.\"\n\n try:\n properties = json.loads(properties_json)\n except json.JSONDecodeError as e:\n return f\"Invalid properties format. Please provide a valid JSON string. Error: {str(e)}\"\n\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"parent\": {\"database_id\": database_id},\n \"properties\": properties,\n }\n\n try:\n response = requests.post(\"https://api.notion.com/v1/pages\", headers=headers, json=data)\n response.raise_for_status()\n result = response.json()\n return result\n except requests.exceptions.RequestException as e:\n error_message = f\"Failed to create Notion page. Error: {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n\n def __call__(self, *args, **kwargs):\n return self._create_notion_page(*args, **kwargs)\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "database_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "database_id", - "value": "", - "display_name": "Database ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the Notion database.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "properties_json": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "properties_json", - "value": "", - "display_name": "Properties (JSON)", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The properties of the new page as a JSON string.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - } - }, - "description": "A component for creating Notion pages.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "Create Page ", - "documentation": "https://docs.langflow.org/integrations/notion/page-create", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "database_id", - "notion_secret", - "properties_json" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionPageCreator-6SCB5", - "description": "A component for creating Notion pages.", - "display_name": "Create Page " - }, - "selected": false, - "width": 384, - "height": 302, - "dragging": false, - "positionAbsolute": { - "x": 1413.9782390799146, - "y": 2051.645785494985 - } - }, - { - "id": "NotionDatabaseProperties-aeWil", - "type": "genericNode", - "position": { - "x": 1004.5753613670959, - "y": 1713.914531491452 - }, - "data": { - "type": "NotionDatabaseProperties", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import requests\nfrom typing import Dict, Union\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionDatabaseProperties(LCToolComponent):\n display_name: str = \"List Database Properties \"\n description: str = \"Retrieve properties of a Notion database.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/list-database-properties\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionDatabasePropertiesSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database.\")\n\n def run_model(self) -> Data:\n result = self._fetch_database_properties(self.database_id)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the properties\n return Data(text=str(result), data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_database_properties\",\n description=\"Retrieve properties of a Notion database. Input should include the database ID.\",\n func=self._fetch_database_properties,\n args_schema=self.NotionDatabasePropertiesSchema,\n )\n\n def _fetch_database_properties(self, database_id: str) -> Union[Dict, str]:\n url = f\"https://api.notion.com/v1/databases/{database_id}\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\", # Use the latest supported version\n }\n try:\n response = requests.get(url, headers=headers)\n response.raise_for_status()\n data = response.json()\n properties = data.get(\"properties\", {})\n return properties\n except requests.exceptions.RequestException as e:\n return f\"Error fetching Notion database properties: {str(e)}\"\n except ValueError as e:\n return f\"Error parsing Notion API response: {str(e)}\"\n except Exception as e:\n return f\"An unexpected error occurred: {str(e)}\"\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "database_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "database_id", - "value": "", - "display_name": "Database ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the Notion database.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - } - }, - "description": "Retrieve properties of a Notion database.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "List Database Properties ", - "documentation": "https://docs.langflow.org/integrations/notion/list-database-properties", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "database_id", - "notion_secret" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionDatabaseProperties-aeWil", - "description": "Retrieve properties of a Notion database.", - "display_name": "List Database Properties " - }, - "selected": false, - "width": 384, - "height": 302, - "dragging": false, - "positionAbsolute": { - "x": 1004.5753613670959, - "y": 1713.914531491452 - } - }, - { - "id": "NotionListPages-znA3w", - "type": "genericNode", - "position": { - "x": 1006.1848442547046, - "y": 2022.7880909242833 - }, - "data": { - "type": "NotionListPages", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import requests\nimport json\nfrom typing import Dict, Any, List, Optional\nfrom pydantic import BaseModel, Field\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionListPages(LCToolComponent):\n display_name: str = \"List Pages \"\n description: str = (\n \"Query a Notion database with filtering and sorting. \"\n \"The input should be a JSON string containing the 'filter' and 'sorts' objects. \"\n \"Example input:\\n\"\n '{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}'\n )\n documentation: str = \"https://docs.langflow.org/integrations/notion/list-pages\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database to query.\",\n ),\n MultilineInput(\n name=\"query_json\",\n display_name=\"Database query (JSON)\",\n info=\"A JSON string containing the filters and sorts that will be used for querying the database. Leave empty for no filters or sorts.\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n class NotionListPagesSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database to query.\")\n query_json: Optional[str] = Field(\n default=\"\",\n description=\"A JSON string containing the filters and sorts for querying the database. Leave empty for no filters or sorts.\",\n )\n\n def run_model(self) -> List[Data]:\n result = self._query_notion_database(self.database_id, self.query_json)\n\n if isinstance(result, str):\n # An error occurred, return it as a single record\n return [Data(text=result)]\n\n records = []\n combined_text = f\"Pages found: {len(result)}\\n\\n\"\n\n for page in result:\n page_data = {\n \"id\": page[\"id\"],\n \"url\": page[\"url\"],\n \"created_time\": page[\"created_time\"],\n \"last_edited_time\": page[\"last_edited_time\"],\n \"properties\": page[\"properties\"],\n }\n\n text = (\n f\"id: {page['id']}\\n\"\n f\"url: {page['url']}\\n\"\n f\"created_time: {page['created_time']}\\n\"\n f\"last_edited_time: {page['last_edited_time']}\\n\"\n f\"properties: {json.dumps(page['properties'], indent=2)}\\n\\n\"\n )\n\n combined_text += text\n records.append(Data(text=text, **page_data))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_list_pages\",\n description=self.description,\n func=self._query_notion_database,\n args_schema=self.NotionListPagesSchema,\n )\n\n def _query_notion_database(self, database_id: str, query_json: Optional[str] = None) -> List[Dict[str, Any]] | str:\n url = f\"https://api.notion.com/v1/databases/{database_id}/query\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n query_payload = {}\n if query_json and query_json.strip():\n try:\n query_payload = json.loads(query_json)\n except json.JSONDecodeError as e:\n return f\"Invalid JSON format for query: {str(e)}\"\n\n try:\n response = requests.post(url, headers=headers, json=query_payload)\n response.raise_for_status()\n results = response.json()\n return results[\"results\"]\n except requests.exceptions.RequestException as e:\n return f\"Error querying Notion database: {str(e)}\"\n except KeyError:\n return \"Unexpected response format from Notion API\"\n except Exception as e:\n return f\"An unexpected error occurred: {str(e)}\"\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "database_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "database_id", - "value": "", - "display_name": "Database ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the Notion database to query.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "query_json": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "query_json", - "value": "", - "display_name": "Database query (JSON)", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "A JSON string containing the filters and sorts that will be used for querying the database. Leave empty for no filters or sorts.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - } - }, - "description": "Query a Notion database with filtering and sorting. The input should be a JSON string containing the 'filter' and 'sorts' objects. Example input:\n{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "List Pages ", - "documentation": "https://docs.langflow.org/integrations/notion/list-pages", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "notion_secret", - "database_id", - "query_json" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionListPages-znA3w", - "description": "Query a Notion database with filtering and sorting. The input should be a JSON string containing the 'filter' and 'sorts' objects. Example input:\n{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}", - "display_name": "List Pages " - }, - "selected": false, - "width": 384, - "height": 470, - "dragging": false, - "positionAbsolute": { - "x": 1006.1848442547046, - "y": 2022.7880909242833 - } - }, - { - "id": "NotionUserList-C3eGn", - "type": "genericNode", - "position": { - "x": 2260.15497405973, - "y": 1717.4551881467207 - }, - "data": { - "type": "NotionUserList", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import requests\nfrom typing import List, Dict\nfrom pydantic import BaseModel\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionUserList(LCToolComponent):\n display_name = \"List Users \"\n description = \"Retrieve users from Notion.\"\n documentation = \"https://docs.langflow.org/integrations/notion/list-users\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionUserListSchema(BaseModel):\n pass\n\n def run_model(self) -> List[Data]:\n users = self._list_users()\n records = []\n combined_text = \"\"\n\n for user in users:\n output = \"User:\\n\"\n for key, value in user.items():\n output += f\"{key.replace('_', ' ').title()}: {value}\\n\"\n output += \"________________________\\n\"\n\n combined_text += output\n records.append(Data(text=output, data=user))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_list_users\",\n description=\"Retrieve users from Notion.\",\n func=self._list_users,\n args_schema=self.NotionUserListSchema,\n )\n\n def _list_users(self) -> List[Dict]:\n url = \"https://api.notion.com/v1/users\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n response = requests.get(url, headers=headers)\n response.raise_for_status()\n\n data = response.json()\n results = data[\"results\"]\n\n users = []\n for user in results:\n user_data = {\n \"id\": user[\"id\"],\n \"type\": user[\"type\"],\n \"name\": user.get(\"name\", \"\"),\n \"avatar_url\": user.get(\"avatar_url\", \"\"),\n }\n users.append(user_data)\n\n return users\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - } - }, - "description": "Retrieve users from Notion.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "List Users ", - "documentation": "https://docs.langflow.org/integrations/notion/list-users", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "notion_secret" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionUserList-C3eGn", - "description": "Retrieve users from Notion.", - "display_name": "List Users " - }, - "selected": true, - "width": 384, - "height": 302, - "dragging": false, - "positionAbsolute": { - "x": 2260.15497405973, - "y": 1717.4551881467207 - } - }, - { - "id": "NotionPageContent-SlL21", - "type": "genericNode", - "position": { - "x": 1826.4242329724448, - "y": 1715.6365113286927 - }, - "data": { - "type": "NotionPageContent", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import requests\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionPageContent(LCToolComponent):\n display_name = \"Page Content Viewer \"\n description = \"Retrieve the content of a Notion page as plain text.\"\n documentation = \"https://docs.langflow.org/integrations/notion/page-content-viewer\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"page_id\",\n display_name=\"Page ID\",\n info=\"The ID of the Notion page to retrieve.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageContentSchema(BaseModel):\n page_id: str = Field(..., description=\"The ID of the Notion page to retrieve.\")\n\n def run_model(self) -> Data:\n result = self._retrieve_page_content(self.page_id)\n if isinstance(result, str) and result.startswith(\"Error:\"):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the content\n return Data(text=result, data={\"content\": result})\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_page_content\",\n description=\"Retrieve the content of a Notion page as plain text.\",\n func=self._retrieve_page_content,\n args_schema=self.NotionPageContentSchema,\n )\n\n def _retrieve_page_content(self, page_id: str) -> str:\n blocks_url = f\"https://api.notion.com/v1/blocks/{page_id}/children?page_size=100\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\",\n }\n try:\n blocks_response = requests.get(blocks_url, headers=headers)\n blocks_response.raise_for_status()\n blocks_data = blocks_response.json()\n return self.parse_blocks(blocks_data.get(\"results\", []))\n except requests.exceptions.RequestException as e:\n error_message = f\"Error: Failed to retrieve Notion page content. {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n except Exception as e:\n return f\"Error: An unexpected error occurred while retrieving Notion page content. {str(e)}\"\n\n def parse_blocks(self, blocks: list) -> str:\n content = \"\"\n for block in blocks:\n block_type = block.get(\"type\")\n if block_type in [\"paragraph\", \"heading_1\", \"heading_2\", \"heading_3\", \"quote\"]:\n content += self.parse_rich_text(block[block_type].get(\"rich_text\", [])) + \"\\n\\n\"\n elif block_type in [\"bulleted_list_item\", \"numbered_list_item\"]:\n content += self.parse_rich_text(block[block_type].get(\"rich_text\", [])) + \"\\n\"\n elif block_type == \"to_do\":\n content += self.parse_rich_text(block[\"to_do\"].get(\"rich_text\", [])) + \"\\n\"\n elif block_type == \"code\":\n content += self.parse_rich_text(block[\"code\"].get(\"rich_text\", [])) + \"\\n\\n\"\n elif block_type == \"image\":\n content += f\"[Image: {block['image'].get('external', {}).get('url', 'No URL')}]\\n\\n\"\n elif block_type == \"divider\":\n content += \"---\\n\\n\"\n return content.strip()\n\n def parse_rich_text(self, rich_text: list) -> str:\n return \"\".join(segment.get(\"plain_text\", \"\") for segment in rich_text)\n\n def __call__(self, *args, **kwargs):\n return self._retrieve_page_content(*args, **kwargs)\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "page_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "page_id", - "value": "", - "display_name": "Page ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the Notion page to retrieve.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - } - }, - "description": "Retrieve the content of a Notion page as plain text.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "Page Content Viewer ", - "documentation": "https://docs.langflow.org/integrations/notion/page-content-viewer", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "page_id", - "notion_secret" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionPageContent-SlL21", - "description": "Retrieve the content of a Notion page as plain text.", - "display_name": "Page Content Viewer " - }, - "selected": false, - "width": 384, - "height": 330, - "dragging": false, - "positionAbsolute": { - "x": 1826.4242329724448, - "y": 1715.6365113286927 - } - }, - { - "id": "NotionSearch-VS2mI", - "type": "genericNode", - "position": { - "x": 2258.1166047519732, - "y": 2034.3959294952945 - }, - "data": { - "type": "NotionSearch", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import requests\nfrom typing import Dict, Any, List\nfrom pydantic import BaseModel, Field\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, DropdownInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionSearch(LCToolComponent):\n display_name: str = \"Search \"\n description: str = \"Searches all pages and databases that have been shared with an integration. The search field can be an empty value to show all values from that search\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/search\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n StrInput(\n name=\"query\",\n display_name=\"Search Query\",\n info=\"The text that the API compares page and database titles against.\",\n ),\n DropdownInput(\n name=\"filter_value\",\n display_name=\"Filter Type\",\n info=\"Limits the results to either only pages or only databases.\",\n options=[\"page\", \"database\"],\n value=\"page\",\n ),\n DropdownInput(\n name=\"sort_direction\",\n display_name=\"Sort Direction\",\n info=\"The direction to sort the results.\",\n options=[\"ascending\", \"descending\"],\n value=\"descending\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionSearchSchema(BaseModel):\n query: str = Field(..., description=\"The search query text.\")\n filter_value: str = Field(default=\"page\", description=\"Filter type: 'page' or 'database'.\")\n sort_direction: str = Field(default=\"descending\", description=\"Sort direction: 'ascending' or 'descending'.\")\n\n def run_model(self) -> List[Data]:\n results = self._search_notion(self.query, self.filter_value, self.sort_direction)\n records = []\n combined_text = f\"Results found: {len(results)}\\n\\n\"\n\n for result in results:\n result_data = {\n \"id\": result[\"id\"],\n \"type\": result[\"object\"],\n \"last_edited_time\": result[\"last_edited_time\"],\n }\n\n if result[\"object\"] == \"page\":\n result_data[\"title_or_url\"] = result[\"url\"]\n text = f\"id: {result['id']}\\ntitle_or_url: {result['url']}\\n\"\n elif result[\"object\"] == \"database\":\n if \"title\" in result and isinstance(result[\"title\"], list) and len(result[\"title\"]) > 0:\n result_data[\"title_or_url\"] = result[\"title\"][0][\"plain_text\"]\n text = f\"id: {result['id']}\\ntitle_or_url: {result['title'][0]['plain_text']}\\n\"\n else:\n result_data[\"title_or_url\"] = \"N/A\"\n text = f\"id: {result['id']}\\ntitle_or_url: N/A\\n\"\n\n text += f\"type: {result['object']}\\nlast_edited_time: {result['last_edited_time']}\\n\\n\"\n combined_text += text\n records.append(Data(text=text, data=result_data))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_search\",\n description=\"Search Notion pages and databases. Input should include the search query and optionally filter type and sort direction.\",\n func=self._search_notion,\n args_schema=self.NotionSearchSchema,\n )\n\n def _search_notion(\n self, query: str, filter_value: str = \"page\", sort_direction: str = \"descending\"\n ) -> List[Dict[str, Any]]:\n url = \"https://api.notion.com/v1/search\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"query\": query,\n \"filter\": {\"value\": filter_value, \"property\": \"object\"},\n \"sort\": {\"direction\": sort_direction, \"timestamp\": \"last_edited_time\"},\n }\n\n response = requests.post(url, headers=headers, json=data)\n response.raise_for_status()\n\n results = response.json()\n return results[\"results\"]\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "filter_value": { - "trace_as_metadata": true, - "options": [ - "page", - "database" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "filter_value", - "value": "page", - "display_name": "Filter Type", - "advanced": true, - "dynamic": false, - "info": "Limits the results to either only pages or only databases.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "query": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "query", - "value": "", - "display_name": "Search Query", - "advanced": true, - "dynamic": false, - "info": "The text that the API compares page and database titles against.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "sort_direction": { - "trace_as_metadata": true, - "options": [ - "ascending", - "descending" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sort_direction", - "value": "descending", - "display_name": "Sort Direction", - "advanced": true, - "dynamic": false, - "info": "The direction to sort the results.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - } - }, - "description": "Searches all pages and databases that have been shared with an integration. The search field can be an empty value to show all values from that search", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "Search ", - "documentation": "https://docs.langflow.org/integrations/notion/search", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "notion_secret", - "query", - "filter_value", - "sort_direction" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionSearch-VS2mI", - "description": "Searches all pages and databases that have been shared with an integration.", - "display_name": "Search " - }, - "selected": false, - "width": 384, - "height": 386, - "dragging": false, - "positionAbsolute": { - "x": 2258.1166047519732, - "y": 2034.3959294952945 - } - }, - { - "id": "NotionPageUpdate-6FyYd", - "type": "genericNode", - "position": { - "x": 1827.0574354713603, - "y": 2055.9948126656136 - }, - "data": { - "type": "NotionPageUpdate", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import json\nimport requests\nfrom typing import Dict, Any, Union\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom loguru import logger\nfrom langflow.io import Output\n\nclass NotionPageUpdate(LCToolComponent):\n display_name: str = \"Update Page Property \"\n description: str = \"Update the properties of a Notion page.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/page-update\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"page_id\",\n display_name=\"Page ID\",\n info=\"The ID of the Notion page to update.\",\n ),\n MultilineInput(\n name=\"properties\",\n display_name=\"Properties\",\n info=\"The properties to update on the page (as a JSON string or a dictionary).\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageUpdateSchema(BaseModel):\n page_id: str = Field(..., description=\"The ID of the Notion page to update.\")\n properties: Union[str, Dict[str, Any]] = Field(\n ..., description=\"The properties to update on the page (as a JSON string or a dictionary).\"\n )\n\n def run_model(self) -> Data:\n result = self._update_notion_page(self.page_id, self.properties)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the updated page data\n output = \"Updated page properties:\\n\"\n for prop_name, prop_value in result.get(\"properties\", {}).items():\n output += f\"{prop_name}: {prop_value}\\n\"\n return Data(text=output, data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"update_notion_page\",\n description=\"Update the properties of a Notion page. IMPORTANT: Use the tool to check the Database properties for more details before using this tool.\",\n func=self._update_notion_page,\n args_schema=self.NotionPageUpdateSchema,\n )\n\n def _update_notion_page(self, page_id: str, properties: Union[str, Dict[str, Any]]) -> Union[Dict[str, Any], str]:\n url = f\"https://api.notion.com/v1/pages/{page_id}\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\", # Use the latest supported version\n }\n\n # Parse properties if it's a string\n if isinstance(properties, str):\n try:\n parsed_properties = json.loads(properties)\n except json.JSONDecodeError as e:\n error_message = f\"Invalid JSON format for properties: {str(e)}\"\n logger.error(error_message)\n return error_message\n\n else:\n parsed_properties = properties\n\n data = {\"properties\": parsed_properties}\n\n try:\n logger.info(f\"Sending request to Notion API: URL: {url}, Data: {json.dumps(data)}\")\n response = requests.patch(url, headers=headers, json=data)\n response.raise_for_status()\n updated_page = response.json()\n\n logger.info(f\"Successfully updated Notion page. Response: {json.dumps(updated_page)}\")\n return updated_page\n except requests.exceptions.HTTPError as e:\n error_message = f\"HTTP Error occurred: {str(e)}\"\n if e.response is not None:\n error_message += f\"\\nStatus code: {e.response.status_code}\"\n error_message += f\"\\nResponse body: {e.response.text}\"\n logger.error(error_message)\n return error_message\n except requests.exceptions.RequestException as e:\n error_message = f\"An error occurred while making the request: {str(e)}\"\n logger.error(error_message)\n return error_message\n except Exception as e:\n error_message = f\"An unexpected error occurred: {str(e)}\"\n logger.error(error_message)\n return error_message\n\n def __call__(self, *args, **kwargs):\n return self._update_notion_page(*args, **kwargs)\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "notion_secret": { - "load_from_db": true, - "required": true, - "placeholder": "", - "show": true, - "name": "notion_secret", - "value": "", - "display_name": "Notion Secret", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The Notion integration token.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "page_id": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "page_id", - "value": "", - "display_name": "Page ID", - "advanced": true, - "dynamic": false, - "info": "The ID of the Notion page to update.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "properties": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "properties", - "value": "", - "display_name": "Properties", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The properties to update on the page (as a JSON string or a dictionary).", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - } - }, - "description": "Update the properties of a Notion page.", - "icon": "NotionDirectoryLoader", - "base_classes": [ - "Data", - "Tool" - ], - "display_name": "Update Page Property ", - "documentation": "https://docs.langflow.org/integrations/notion/page-update", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "example_output", - "display_name": "Data", - "method": "run_model", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Tool" - ], - "selected": "Tool", - "name": "example_tool_output", - "display_name": "Tool", - "method": "build_tool", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "page_id", - "properties", - "notion_secret" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "NotionPageUpdate-6FyYd", - "description": "Update the properties of a Notion page.", - "display_name": "Update Page Property " - }, - "selected": false, - "width": 384, - "height": 302, - "dragging": false, - "positionAbsolute": { - "x": 1827.0574354713603, - "y": 2055.9948126656136 - } - }, - { - "id": "ToolCallingAgent-50Gcd", - "type": "genericNode", - "position": { - "x": 2186.0530739759893, - "y": 612.1744804997304 - }, - "data": { - "type": "ToolCallingAgent", - "node": { - "template": { - "_type": "Component", - "chat_history": { - "trace_as_metadata": true, - "list": true, - "trace_as_input": true, - "required": false, - "placeholder": "", - "show": true, - "name": "chat_history", - "value": "", - "display_name": "Chat History", - "advanced": false, - "input_types": [ - "Data" - ], - "dynamic": false, - "info": "", - "title_case": false, - "type": "other", - "_input_type": "DataInput" - }, - "llm": { - "trace_as_metadata": true, - "list": false, - "required": true, - "placeholder": "", - "show": true, - "name": "llm", - "value": "", - "display_name": "Language Model", - "advanced": false, - "input_types": [ - "LanguageModel" - ], - "dynamic": false, - "info": "", - "title_case": false, - "type": "other", - "_input_type": "HandleInput" - }, - "tools": { - "trace_as_metadata": true, - "list": true, - "required": false, - "placeholder": "", - "show": true, - "name": "tools", - "value": "", - "display_name": "Tools", - "advanced": false, - "input_types": [ - "Tool", - "BaseTool" - ], - "dynamic": false, - "info": "", - "title_case": false, - "type": "other", - "_input_type": "HandleInput" - }, - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "from typing import Optional, List\n\nfrom langchain.agents import create_tool_calling_agent\nfrom langchain_core.prompts import ChatPromptTemplate, PromptTemplate, HumanMessagePromptTemplate\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.inputs import MultilineInput\nfrom langflow.inputs.inputs import HandleInput, DataInput\nfrom langflow.schema import Data\n\n\nclass ToolCallingAgentComponent(LCToolsAgentComponent):\n display_name: str = \"Tool Calling Agent\"\n description: str = \"Agent that uses tools\"\n icon = \"LangChain\"\n beta = True\n name = \"ToolCallingAgent\"\n\n inputs = LCToolsAgentComponent._base_inputs + [\n HandleInput(name=\"llm\", display_name=\"Language Model\", input_types=[\"LanguageModel\"], required=True),\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"System Prompt\",\n info=\"System prompt for the agent.\",\n value=\"You are a helpful assistant\",\n ),\n MultilineInput(\n name=\"user_prompt\", display_name=\"Prompt\", info=\"This prompt must contain 'input' key.\", value=\"{input}\"\n ),\n DataInput(name=\"chat_history\", display_name=\"Chat History\", is_list=True, advanced=True),\n ]\n\n def get_chat_history_data(self) -> Optional[List[Data]]:\n return self.chat_history\n\n def create_agent_runnable(self):\n if \"input\" not in self.user_prompt:\n raise ValueError(\"Prompt must contain 'input' key.\")\n messages = [\n (\"system\", self.system_prompt),\n (\"placeholder\", \"{chat_history}\"),\n HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=[\"input\"], template=self.user_prompt)),\n (\"placeholder\", \"{agent_scratchpad}\"),\n ]\n prompt = ChatPromptTemplate.from_messages(messages)\n return create_tool_calling_agent(self.llm, self.tools, prompt)\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "handle_parsing_errors": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "handle_parsing_errors", - "value": true, - "display_name": "Handle Parse Errors", - "advanced": true, - "dynamic": false, - "info": "", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - }, - "input_value": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "input_value", - "value": "", - "display_name": "Input", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "max_iterations": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "max_iterations", - "value": 15, - "display_name": "Max Iterations", - "advanced": true, - "dynamic": false, - "info": "", - "title_case": false, - "type": "int", - "_input_type": "IntInput" - }, - "system_prompt": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "system_prompt", - "value": "", - "display_name": "System Prompt", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "System prompt for the agent.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - }, - "user_prompt": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "user_prompt", - "value": "{input}", - "display_name": "Prompt", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "This prompt must contain 'input' key.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - }, - "verbose": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "verbose", - "value": true, - "display_name": "Verbose", - "advanced": true, - "dynamic": false, - "info": "", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - } - }, - "description": "Agent that uses tools", - "icon": "LangChain", - "base_classes": [ - "AgentExecutor", - "Message" - ], - "display_name": "Tool Calling Agent", - "documentation": "", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "AgentExecutor" - ], - "selected": "AgentExecutor", - "name": "agent", - "display_name": "Agent", - "method": "build_agent", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - }, - { - "types": [ - "Message" - ], - "selected": "Message", - "name": "response", - "display_name": "Response", - "method": "message_response", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "input_value", - "handle_parsing_errors", - "verbose", - "max_iterations", - "tools", - "llm", - "system_prompt", - "user_prompt", - "chat_history" - ], - "beta": true, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "ToolCallingAgent-50Gcd" - }, - "selected": false, - "width": 384, - "height": 532, - "dragging": false, - "positionAbsolute": { - "x": 2186.0530739759893, - "y": 612.1744804997304 - } - }, - { - "id": "ChatOutput-TSCup", - "type": "genericNode", - "position": { - "x": 2649.190603849412, - "y": 841.0466487848925 - }, - "data": { - "type": "ChatOutput", - "node": { - "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 BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\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 = \"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=\"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 (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\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, - "name": "data_template", - "value": "{text}", - "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_type": "MessageTextInput" - }, - "input_value": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "input_value", - "value": "", - "display_name": "Text", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "Message to be passed as output.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "sender": { - "trace_as_metadata": true, - "options": [ - "Machine", - "User" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender", - "value": "Machine", - "display_name": "Sender Type", - "advanced": true, - "dynamic": false, - "info": "Type of sender.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "sender_name": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender_name", - "value": "AI", - "display_name": "Sender Name", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "Name of the sender.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "session_id": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "session_id", - "value": "", - "display_name": "Session ID", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "should_store_message": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "should_store_message", - "value": true, - "display_name": "Store Messages", - "advanced": true, - "dynamic": false, - "info": "Store the message in the history.", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - } - }, - "description": "Display a chat message in the Playground.", - "icon": "ChatOutput", - "base_classes": [ - "Message" - ], - "display_name": "Chat Output", - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "data_template" - ], - "beta": false, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "ChatOutput-TSCup" - }, - "selected": false, - "width": 384, - "height": 302, - "positionAbsolute": { - "x": 2649.190603849412, - "y": 841.0466487848925 - }, - "dragging": false - }, - { - "id": "ChatInput-bcq6D", - "type": "genericNode", - "position": { - "x": 557.6262725075026, - "y": 724.8518930903978 - }, - "data": { - "type": "ChatInput", - "node": { - "template": { - "_type": "Component", - "files": { - "trace_as_metadata": true, - "file_path": "", - "fileTypes": [ - "txt", - "md", - "mdx", - "csv", - "json", - "yaml", - "yml", - "xml", - "html", - "htm", - "pdf", - "docx", - "py", - "sh", - "sql", - "js", - "ts", - "tsx", - "jpg", - "jpeg", - "png", - "bmp", - "image" - ], - "list": true, - "required": false, - "placeholder": "", - "show": true, - "name": "files", - "value": "", - "display_name": "Files", - "advanced": true, - "dynamic": false, - "info": "Files to be sent with the message.", - "title_case": false, - "type": "file", - "_input_type": "FileInput" - }, - "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 BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_NAME_USER\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=\"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\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\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": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "input_value", - "value": "list users", - "display_name": "Text", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "Message to be passed as input.", - "title_case": false, - "type": "str", - "_input_type": "MultilineInput" - }, - "sender": { - "trace_as_metadata": true, - "options": [ - "Machine", - "User" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender", - "value": "User", - "display_name": "Sender Type", - "advanced": true, - "dynamic": false, - "info": "Type of sender.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "sender_name": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender_name", - "value": "User", - "display_name": "Sender Name", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "Name of the sender.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "session_id": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "session_id", - "value": "", - "display_name": "Session ID", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "should_store_message": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "should_store_message", - "value": true, - "display_name": "Store Messages", - "advanced": true, - "dynamic": false, - "info": "Store the message in the history.", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - } - }, - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "files" - ], - "beta": false, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "ChatInput-bcq6D" - }, - "selected": false, - "width": 384, - "height": 302, - "positionAbsolute": { - "x": 557.6262725075026, - "y": 724.8518930903978 - }, - "dragging": false - }, - { - "id": "ToolkitComponent-2lNG0", - "type": "genericNode", - "position": { - "x": 1731.8884789245508, - "y": 1378.7846304343796 - }, - "data": { - "type": "ToolkitComponent", - "node": { - "template": { - "_type": "Component", - "tools": { - "trace_as_metadata": true, - "list": true, - "required": false, - "placeholder": "", - "show": true, - "name": "tools", - "value": "", - "display_name": "Tools", - "advanced": false, - "input_types": [ - "Tool" - ], - "dynamic": false, - "info": "List of tools to combine.", - "title_case": false, - "type": "other", - "_input_type": "HandleInput" - }, - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "from typing import List\r\nfrom langflow.custom import Component\r\nfrom langflow.inputs import HandleInput, MessageTextInput\r\nfrom langflow.template import Output\r\nfrom langflow.field_typing import Tool, Embeddings\r\nfrom langchain.tools.base import BaseTool, StructuredTool\r\nfrom langflow.schema import Data\r\n\r\nclass ToolkitComponent(Component):\r\n display_name = \"Toolkit\"\r\n description = \"Combines multiple tools into a single list of tools.\"\r\n icon = \"pocket-knife\"\r\n\r\n inputs = [\r\n HandleInput(\r\n name=\"tools\",\r\n display_name=\"Tools\",\r\n input_types=[\"Tool\"],\r\n info=\"List of tools to combine.\",\r\n is_list=True,\r\n ),\r\n ]\r\n\r\n outputs = [\r\n Output(display_name=\"Tools\", name=\"generated_tools\", method=\"generate_toolkit\"),\r\n Output(display_name=\"Tool Data\", name=\"tool_data\", method=\"generate_tool_data\"),\r\n ]\r\n\r\n def generate_toolkit(self) -> List[BaseTool]:\r\n combined_tools = []\r\n name_count = {}\r\n for index, tool in enumerate(self.tools):\r\n self.log(f\"Processing tool {index}: {type(tool)}\")\r\n if isinstance(tool, (BaseTool, StructuredTool)):\r\n processed_tool = tool\r\n elif hasattr(tool, 'build_tool'):\r\n processed_tool = tool.build_tool()\r\n else:\r\n self.log(f\"Unsupported tool type: {type(tool)}. Attempting to process anyway.\")\r\n processed_tool = tool\r\n\r\n original_name = getattr(processed_tool, 'name', f\"UnnamedTool_{index}\")\r\n self.log(f\"Original tool name: {original_name}\")\r\n\r\n if original_name not in name_count:\r\n name_count[original_name] = 0\r\n final_name = original_name\r\n else:\r\n name_count[original_name] += 1\r\n final_name = f\"{original_name}_{name_count[original_name]}\"\r\n\r\n if hasattr(processed_tool, 'name'):\r\n processed_tool.name = final_name\r\n\r\n self.log(f\"Final tool name: {final_name}\")\r\n\r\n if isinstance(processed_tool, StructuredTool) and hasattr(processed_tool, 'args_schema'):\r\n processed_tool.args_schema.name = f\"{final_name}_Schema\"\r\n\r\n combined_tools.append(processed_tool)\r\n\r\n debug_info = \"\\n\".join([f\"Tool {i}: {getattr(tool, 'name', f'UnnamedTool_{i}')} (Original: {getattr(tool, '_original_name', 'N/A')}) - Type: {type(tool)}\" for i, tool in enumerate(combined_tools)])\r\n self.log(\"Final toolkit composition:\")\r\n self.log(debug_info)\r\n\r\n\r\n self.status = combined_tools\r\n return combined_tools\r\n\r\n def generate_tool_data(self) -> List[Data]:\r\n tool_data = []\r\n for tool in self.generate_toolkit():\r\n tool_data.append(Data(\r\n data={\r\n \"name\": getattr(tool, 'name', 'Unnamed Tool'),\r\n \"description\": getattr(tool, 'description', 'No description available')\r\n }\r\n ))\r\n return tool_data", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - } - }, - "description": "Combines multiple tools into a single list of tools.", - "icon": "pocket-knife", - "base_classes": [ - "BaseTool", - "Data" - ], - "display_name": "Toolkit", - "documentation": "", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "BaseTool" - ], - "selected": "BaseTool", - "name": "generated_tools", - "display_name": "Tools", - "method": "generate_toolkit", - "value": "__UNDEFINED__", - "cache": true - }, - { - "types": [ - "Data" - ], - "selected": "Data", - "name": "tool_data", - "display_name": "Tool Data", - "method": "generate_tool_data", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - } - ], - "field_order": [ - "tools" - ], - "beta": false, - "edited": true, - "lf_version": "1.0.17" - }, - "id": "ToolkitComponent-2lNG0" - }, - "selected": false, - "width": 384, - "height": 292, - "dragging": false, - "positionAbsolute": { - "x": 1731.8884789245508, - "y": 1378.7846304343796 - } - }, - { - "id": "OpenAIModel-BJWIg", - "type": "genericNode", - "position": { - "x": 1718.9773974162958, - "y": 603.4642741725065 - }, - "data": { - "type": "OpenAIModel", - "node": { - "template": { - "_type": "Component", - "api_key": { - "load_from_db": true, - "required": false, - "placeholder": "", - "show": true, - "name": "api_key", - "value": "", - "display_name": "OpenAI API Key", - "advanced": false, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", - "title_case": false, - "password": true, - "type": "str", - "_input_type": "SecretStrInput" - }, - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "import operator\nfrom functools import reduce\n\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = LCModelComponent._base_inputs + [\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n range_spec=RangeSpec(min=0, max=128000),\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schema is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature if temperature is not None else 0.1,\n seed=seed,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "input_value": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "input_value", - "value": "", - "display_name": "Input", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "", - "title_case": false, - "type": "str", - "_input_type": "MessageInput" - }, - "json_mode": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "json_mode", - "value": false, - "display_name": "JSON Mode", - "advanced": true, - "dynamic": false, - "info": "If True, it will output JSON regardless of passing a schema.", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - }, - "max_tokens": { - "trace_as_metadata": true, - "range_spec": { - "step_type": "float", - "min": 0, - "max": 128000, - "step": 0.1 - }, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "max_tokens", - "value": "", - "display_name": "Max Tokens", - "advanced": true, - "dynamic": false, - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - "title_case": false, - "type": "int", - "_input_type": "IntInput" - }, - "model_kwargs": { - "trace_as_input": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "model_kwargs", - "value": {}, - "display_name": "Model Kwargs", - "advanced": true, - "dynamic": false, - "info": "", - "title_case": false, - "type": "dict", - "_input_type": "DictInput" - }, - "model_name": { - "trace_as_metadata": true, - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo", - "gpt-3.5-turbo-0125" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "model_name", - "value": "gpt-4o", - "display_name": "Model Name", - "advanced": false, - "dynamic": false, - "info": "", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "openai_api_base": { - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "openai_api_base", - "value": "", - "display_name": "OpenAI API Base", - "advanced": true, - "dynamic": false, - "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.", - "title_case": false, - "type": "str", - "_input_type": "StrInput" - }, - "output_schema": { - "trace_as_input": true, - "list": true, - "required": false, - "placeholder": "", - "show": true, - "name": "output_schema", - "value": {}, - "display_name": "Schema", - "advanced": true, - "dynamic": false, - "info": "The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.", - "title_case": false, - "type": "dict", - "_input_type": "DictInput" - }, - "seed": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "seed", - "value": 1, - "display_name": "Seed", - "advanced": true, - "dynamic": false, - "info": "The seed controls the reproducibility of the job.", - "title_case": false, - "type": "int", - "_input_type": "IntInput" - }, - "stream": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "stream", - "value": false, - "display_name": "Stream", - "advanced": true, - "dynamic": false, - "info": "Stream the response from the model. Streaming works only in Chat.", - "title_case": false, - "type": "bool", - "_input_type": "BoolInput" - }, - "system_message": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "system_message", - "value": "", - "display_name": "System Message", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "System message to pass to the model.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "temperature": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "temperature", - "value": "0.2", - "display_name": "Temperature", - "advanced": true, - "dynamic": false, - "info": "", - "title_case": false, - "type": "float", - "_input_type": "FloatInput" - } - }, - "description": "Generates text using OpenAI LLMs.", - "icon": "OpenAI", - "base_classes": [ - "LanguageModel", - "Message" - ], - "display_name": "OpenAI", - "documentation": "", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Message" - ], - "selected": "Message", - "name": "text_output", - "display_name": "Text", - "method": "text_response", - "value": "__UNDEFINED__", - "cache": true - }, - { - "types": [ - "LanguageModel" - ], - "selected": "LanguageModel", - "name": "model_output", - "display_name": "Language Model", - "method": "build_model", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "input_value", - "system_message", - "stream", - "max_tokens", - "model_kwargs", - "json_mode", - "output_schema", - "model_name", - "openai_api_base", - "api_key", - "temperature", - "seed" - ], - "beta": false, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "OpenAIModel-BJWIg" - }, - "selected": false, - "width": 384, - "height": 433, - "positionAbsolute": { - "x": 1718.9773974162958, - "y": 603.4642741725065 - }, - "dragging": false - }, - { - "id": "Memory-CTQWu", - "type": "genericNode", - "position": { - "x": 1240.7186213296432, - "y": 1059.5754404393747 - }, - "data": { - "type": "Memory", - "node": { - "template": { - "_type": "Component", - "memory": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "memory", - "value": "", - "display_name": "External Memory", - "advanced": true, - "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", - "_input_type": "HandleInput" - }, - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "from langchain.memory import ConversationBufferMemory\n\nfrom langflow.custom import Component\nfrom langflow.field_typing import BaseChatMemory\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 LCBuiltinChatMemory, get_messages\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\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=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\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=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\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 # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\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.flow_id, session_id=self.session_id)\n return ConversationBufferMemory(chat_memory=chat_memory)\n", - "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, - "name": "n_messages", - "value": 100, - "display_name": "Number of Messages", - "advanced": true, - "dynamic": false, - "info": "Number of messages to retrieve.", - "title_case": false, - "type": "int", - "_input_type": "IntInput" - }, - "order": { - "trace_as_metadata": true, - "options": [ - "Ascending", - "Descending" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "order", - "value": "Ascending", - "display_name": "Order", - "advanced": true, - "dynamic": false, - "info": "Order of the messages.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "sender": { - "trace_as_metadata": true, - "options": [ - "Machine", - "User", - "Machine and User" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender", - "value": "Machine and User", - "display_name": "Sender Type", - "advanced": true, - "dynamic": false, - "info": "Filter by sender type.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - }, - "sender_name": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "sender_name", - "value": "", - "display_name": "Sender Name", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "Filter by sender name.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "session_id": { - "trace_as_input": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "session_id", - "value": "", - "display_name": "Session ID", - "advanced": true, - "input_types": [ - "Message" - ], - "dynamic": false, - "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "title_case": false, - "type": "str", - "_input_type": "MessageTextInput" - }, - "template": { - "trace_as_input": true, - "multiline": true, - "trace_as_metadata": true, - "load_from_db": false, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "template", - "value": "{sender_name}: {text}", - "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", - "_input_type": "MultilineInput" - } - }, - "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": "", - "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, - "hidden": true - }, - { - "types": [ - "BaseChatMemory" - ], - "selected": "BaseChatMemory", - "name": "lc_memory", - "display_name": "Memory", - "method": "build_lc_memory", - "value": "__UNDEFINED__", - "cache": true, - "hidden": true - } - ], - "field_order": [ - "memory", - "sender", - "sender_name", - "n_messages", - "session_id", - "order", - "template" - ], - "beta": false, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "Memory-CTQWu" - }, - "selected": false, - "width": 384, - "height": 244, - "dragging": false, - "positionAbsolute": { - "x": 1240.7186213296432, - "y": 1059.5754404393747 - } - }, - { - "id": "Prompt-0dWZu", - "type": "genericNode", - "position": { - "x": 1227.4862876736101, - "y": 616.3826667128244 - }, - "data": { - "type": "Prompt", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "template": { - "trace_as_input": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "name": "template", - "value": "\nYou are a Notion Agent, an AI assistant designed to help users interact with their Notion workspace. Your role is to understand user requests, utilize the appropriate Notion tools to fulfill these requests, and communicate clearly with the user throughout the process.\n\nGeneral Guidelines:\n\n1. Carefully analyze each user request to determine which tool(s) you need to use.\n\n2. Before using any tool, ensure you have all the necessary information. If you need more details, ask the user clear and concise questions.\n\n3. When using a tool, provide a brief explanation to the user about what you're doing and why.\n\n4. After using a tool, interpret the results for the user in a clear, concise manner.\n\n5. If a task requires multiple steps, outline your plan to the user before proceeding.\n\n6. If you encounter an error or limitation, explain it to the user and suggest possible solutions or alternative approaches.\n\n7. Always maintain a helpful and professional tone in your interactions.\n\n8. Be proactive in offering suggestions or alternatives if the user's initial request can't be fulfilled exactly as stated.\n\n9. When providing information or results, focus on relevance and clarity. Summarize when necessary, but provide details when they're important.\n\n10. If a user's request is unclear or could be interpreted in multiple ways, ask for clarification before proceeding.\n\n11. After completing a task, summarize what was accomplished and suggest any relevant next steps or additional actions the user might want to take.\n\n12. If a user asks about capabilities you don't have or tools you can't access, clearly explain your limitations and suggest alternative ways to assist if possible.\n\nRemember, your primary goal is to assist the user effectively with their Notion-related tasks using the provided tools. Always strive for clarity, accuracy, and helpfulness in your interactions. Adapt your communication style to the user's level of technical understanding and familiarity with Notion.\n\nNow, you're ready to assist the user\n\nToday is: {CURRENT_DATE}\n", - "display_name": "Template", - "advanced": false, - "dynamic": false, - "info": "", - "title_case": false, - "type": "prompt", - "_input_type": "PromptInput" - }, - "CURRENT_DATE": { - "field_type": "str", - "required": false, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "CURRENT_DATE", - "display_name": "CURRENT_DATE", - "advanced": false, - "input_types": [ - "Message", - "Text" - ], - "dynamic": false, - "info": "", - "load_from_db": false, - "title_case": false, - "type": "str" - } - }, - "description": "Create a prompt template with dynamic variables.", - "icon": "prompts", - "is_input": null, - "is_output": null, - "is_composition": null, - "base_classes": [ - "Message" - ], - "name": "", - "display_name": "Prompt", - "documentation": "", - "custom_fields": { - "template": [ - "CURRENT_DATE" - ] - }, - "output_types": [], - "full_path": null, - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Message" - ], - "selected": "Message", - "name": "prompt", - "hidden": null, - "display_name": "Prompt Message", - "method": "build_prompt", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "template" - ], - "beta": false, - "error": null, - "edited": false, - "lf_version": "1.0.17" - }, - "id": "Prompt-0dWZu" - }, - "selected": false, - "width": 384, - "height": 416, - "positionAbsolute": { - "x": 1227.4862876736101, - "y": 616.3826667128244 - }, - "dragging": false - }, - { - "id": "CurrentDateComponent-NSNQ8", - "type": "genericNode", - "position": { - "x": 1092.5108512311297, - "y": 868.3249850335523 - }, - "data": { - "type": "CurrentDateComponent", - "node": { - "template": { - "_type": "Component", - "code": { - "type": "code", - "required": true, - "placeholder": "", - "list": false, - "show": true, - "multiline": true, - "value": "from datetime import datetime\r\nfrom zoneinfo import ZoneInfo\r\nfrom typing import List\r\n\r\nfrom langflow.custom import Component\r\nfrom langflow.io import DropdownInput, Output\r\nfrom langflow.schema.message import Message\r\n\r\nclass CurrentDateComponent(Component):\r\n display_name = \"Current Date 🕰️\"\r\n description = \"Returns the current date and time in the selected timezone.\"\r\n icon = \"clock\"\r\n\r\n inputs = [\r\n DropdownInput(\r\n name=\"timezone\",\r\n display_name=\"Timezone\",\r\n options=[\r\n \"UTC\",\r\n \"US/Eastern\",\r\n \"US/Central\",\r\n \"US/Mountain\",\r\n \"US/Pacific\",\r\n \"Europe/London\",\r\n \"Europe/Paris\",\r\n \"Asia/Tokyo\",\r\n \"Australia/Sydney\",\r\n \"America/Sao_Paulo\",\r\n \"America/Cuiaba\",\r\n ],\r\n value=\"UTC\",\r\n info=\"Select the timezone for the current date and time.\",\r\n ),\r\n ]\r\n\r\n outputs = [\r\n Output(display_name=\"Current Date\", name=\"current_date\", method=\"get_current_date\"),\r\n ]\r\n\r\n def get_current_date(self) -> Message:\r\n try:\r\n tz = ZoneInfo(self.timezone)\r\n current_date = datetime.now(tz).strftime(\"%Y-%m-%d %H:%M:%S %Z\")\r\n result = f\"Current date and time in {self.timezone}: {current_date}\"\r\n self.status = result\r\n return Message(text=result)\r\n except Exception as e:\r\n error_message = f\"Error: {str(e)}\"\r\n self.status = error_message\r\n return Message(text=error_message)", - "fileTypes": [], - "file_path": "", - "password": false, - "name": "code", - "advanced": true, - "dynamic": true, - "info": "", - "load_from_db": false, - "title_case": false - }, - "timezone": { - "trace_as_metadata": true, - "options": [ - "UTC", - "US/Eastern", - "US/Central", - "US/Mountain", - "US/Pacific", - "Europe/London", - "Europe/Paris", - "Asia/Tokyo", - "Australia/Sydney", - "America/Sao_Paulo", - "America/Cuiaba" - ], - "combobox": false, - "required": false, - "placeholder": "", - "show": true, - "name": "timezone", - "value": "UTC", - "display_name": "Timezone", - "advanced": false, - "dynamic": false, - "info": "Select the timezone for the current date and time.", - "title_case": false, - "type": "str", - "_input_type": "DropdownInput" - } - }, - "description": "Returns the current date and time in the selected timezone.", - "icon": "clock", - "base_classes": [ - "Message" - ], - "display_name": "Current Date", - "documentation": "", - "custom_fields": {}, - "output_types": [], - "pinned": false, - "conditional_paths": [], - "frozen": false, - "outputs": [ - { - "types": [ - "Message" - ], - "selected": "Message", - "name": "current_date", - "display_name": "Current Date", - "method": "get_current_date", - "value": "__UNDEFINED__", - "cache": true - } - ], - "field_order": [ - "timezone" - ], - "beta": false, - "edited": true, - "official": false, - "lf_version": "1.0.17" - }, - "id": "CurrentDateComponent-NSNQ8", - "showNode": false - }, - "selected": false, - "width": 96, - "height": 96, - "dragging": false, - "positionAbsolute": { - "x": 1092.5108512311297, - "y": 868.3249850335523 - } + "data": { + "edges": [ + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-my2y0", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ToolCallingAgent-VyZhN", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-my2y0{œdataTypeœ:œChatInputœ,œidœ:œChatInput-my2y0œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ToolCallingAgent-VyZhN{œfieldNameœ:œinput_valueœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-my2y0", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-my2y0œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "ToolCallingAgent-VyZhN", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionPageUpdate", + "id": "NotionPageUpdate-DWOeO", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionPageUpdate-DWOeO{œdataTypeœ:œNotionPageUpdateœ,œidœ:œNotionPageUpdate-DWOeOœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionPageUpdate-DWOeO", + "sourceHandle": "{œdataTypeœ:œNotionPageUpdateœ,œidœ:œNotionPageUpdate-DWOeOœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionPageCreator", + "id": "NotionPageCreator-Gdgtd", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionPageCreator-Gdgtd{œdataTypeœ:œNotionPageCreatorœ,œidœ:œNotionPageCreator-Gdgtdœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionPageCreator-Gdgtd", + "sourceHandle": "{œdataTypeœ:œNotionPageCreatorœ,œidœ:œNotionPageCreator-Gdgtdœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "AddContentToPage", + "id": "AddContentToPage-3iQ3m", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-AddContentToPage-3iQ3m{œdataTypeœ:œAddContentToPageœ,œidœ:œAddContentToPage-3iQ3mœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "AddContentToPage-3iQ3m", + "sourceHandle": "{œdataTypeœ:œAddContentToPageœ,œidœ:œAddContentToPage-3iQ3mœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionDatabaseProperties", + "id": "NotionDatabaseProperties-F3Vjj", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionDatabaseProperties-F3Vjj{œdataTypeœ:œNotionDatabasePropertiesœ,œidœ:œNotionDatabaseProperties-F3Vjjœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionDatabaseProperties-F3Vjj", + "sourceHandle": "{œdataTypeœ:œNotionDatabasePropertiesœ,œidœ:œNotionDatabaseProperties-F3Vjjœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionListPages", + "id": "NotionListPages-9cC7f", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionListPages-9cC7f{œdataTypeœ:œNotionListPagesœ,œidœ:œNotionListPages-9cC7fœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionListPages-9cC7f", + "sourceHandle": "{œdataTypeœ:œNotionListPagesœ,œidœ:œNotionListPages-9cC7fœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionPageContent", + "id": "NotionPageContent-didQ9", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionPageContent-didQ9{œdataTypeœ:œNotionPageContentœ,œidœ:œNotionPageContent-didQ9œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionPageContent-didQ9", + "sourceHandle": "{œdataTypeœ:œNotionPageContentœ,œidœ:œNotionPageContent-didQ9œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionUserList", + "id": "NotionUserList-4C1Rj", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionUserList-4C1Rj{œdataTypeœ:œNotionUserListœ,œidœ:œNotionUserList-4C1Rjœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionUserList-4C1Rj", + "sourceHandle": "{œdataTypeœ:œNotionUserListœ,œidœ:œNotionUserList-4C1Rjœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "NotionSearch", + "id": "NotionSearch-Qrabo", + "name": "example_tool_output", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "ToolkitComponent-mfg6v", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-NotionSearch-Qrabo{œdataTypeœ:œNotionSearchœ,œidœ:œNotionSearch-Qraboœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-mfg6v{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "NotionSearch-Qrabo", + "sourceHandle": "{œdataTypeœ:œNotionSearchœ,œidœ:œNotionSearch-Qraboœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", + "target": "ToolkitComponent-mfg6v", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-mfg6vœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "OpenAIModel", + "id": "OpenAIModel-gIFGV", + "name": "model_output", + "output_types": [ + "LanguageModel" + ] + }, + "targetHandle": { + "fieldName": "llm", + "id": "ToolCallingAgent-VyZhN", + "inputTypes": [ + "LanguageModel" + ], + "type": "other" + } + }, + "id": "reactflow__edge-OpenAIModel-gIFGV{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gIFGVœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-ToolCallingAgent-VyZhN{œfieldNameœ:œllmœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "selected": false, + "source": "OpenAIModel-gIFGV", + "sourceHandle": "{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gIFGVœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}", + "target": "ToolCallingAgent-VyZhN", + "targetHandle": "{œfieldNameœ:œllmœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-S7cEu", + "name": "prompt", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "system_prompt", + "id": "ToolCallingAgent-VyZhN", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-Prompt-S7cEu{œdataTypeœ:œPromptœ,œidœ:œPrompt-S7cEuœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-ToolCallingAgent-VyZhN{œfieldNameœ:œsystem_promptœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "Prompt-S7cEu", + "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-S7cEuœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", + "target": "ToolCallingAgent-VyZhN", + "targetHandle": "{œfieldNameœ:œsystem_promptœ,œidœ:œToolCallingAgent-VyZhNœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-4Jm4h", + "name": "dataframe", + "output_types": [ + "DataFrame" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "TypeConverterComponent-I2ppC", + "inputTypes": [ + "Message", + "Data", + "DataFrame" + ], + "type": "other" + } + }, + "id": "xy-edge__Memory-4Jm4h{œdataTypeœ:œMemoryœ,œidœ:œMemory-4Jm4hœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-I2ppC{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-I2ppCœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "source": "Memory-4Jm4h", + "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-4Jm4hœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}", + "target": "TypeConverterComponent-I2ppC", + "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-I2ppCœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-I2ppC", + "name": "message_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "OpenAIModel-gIFGV", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__TypeConverterComponent-I2ppC{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-I2ppCœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-gIFGV{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gIFGVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "TypeConverterComponent-I2ppC", + "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-I2ppCœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "OpenAIModel-gIFGV", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gIFGVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + } + ], + "nodes": [ + { + "data": { + "description": "Convert markdown text to Notion blocks and append them to a Notion page.", + "display_name": "Add Content to Page ", + "id": "AddContentToPage-3iQ3m", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert markdown text to Notion blocks and append them to a Notion page.", + "display_name": "Add Content to Page ", + "documentation": "https://developers.notion.com/reference/patch-block-children", + "edited": true, + "field_order": [ + "markdown_text", + "block_id", + "notion_secret" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "block_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Page/Block ID", + "dynamic": false, + "info": "The ID of the page/block to add the content.", + "list": false, + "load_from_db": false, + "name": "block_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "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": "import json\nfrom typing import Dict, Any, Union\nfrom markdown import markdown\nfrom bs4 import BeautifulSoup\nimport requests\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom pydantic import BaseModel, Field\n\n\nclass AddContentToPage(LCToolComponent):\n display_name: str = \"Add Content to Page \"\n description: str = \"Convert markdown text to Notion blocks and append them to a Notion page.\"\n documentation: str = \"https://developers.notion.com/reference/patch-block-children\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n MultilineInput(\n name=\"markdown_text\",\n display_name=\"Markdown Text\",\n info=\"The markdown text to convert to Notion blocks.\",\n ),\n StrInput(\n name=\"block_id\",\n display_name=\"Page/Block ID\",\n info=\"The ID of the page/block to add the content.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class AddContentToPageSchema(BaseModel):\n markdown_text: str = Field(..., description=\"The markdown text to convert to Notion blocks.\")\n block_id: str = Field(..., description=\"The ID of the page/block to add the content.\")\n\n def run_model(self) -> Data:\n result = self._add_content_to_page(self.markdown_text, self.block_id)\n return Data(data=result, text=json.dumps(result))\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"add_content_to_notion_page\",\n description=\"Convert markdown text to Notion blocks and append them to a Notion page.\",\n func=self._add_content_to_page,\n args_schema=self.AddContentToPageSchema,\n )\n\n def _add_content_to_page(self, markdown_text: str, block_id: str) -> Union[Dict[str, Any], str]:\n try:\n html_text = markdown(markdown_text)\n soup = BeautifulSoup(html_text, \"html.parser\")\n blocks = self.process_node(soup)\n\n url = f\"https://api.notion.com/v1/blocks/{block_id}/children\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"children\": blocks,\n }\n\n response = requests.patch(url, headers=headers, json=data)\n response.raise_for_status()\n\n return response.json()\n except requests.exceptions.RequestException as e:\n error_message = f\"Error: Failed to add content to Notion page. {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n except Exception as e:\n return f\"Error: An unexpected error occurred while adding content to Notion page. {str(e)}\"\n\n def process_node(self, node):\n blocks = []\n if isinstance(node, str):\n text = node.strip()\n if text:\n if text.startswith(\"#\"):\n heading_level = text.count(\"#\", 0, 6)\n heading_text = text[heading_level:].strip()\n if heading_level == 1:\n blocks.append(self.create_block(\"heading_1\", heading_text))\n elif heading_level == 2:\n blocks.append(self.create_block(\"heading_2\", heading_text))\n elif heading_level == 3:\n blocks.append(self.create_block(\"heading_3\", heading_text))\n else:\n blocks.append(self.create_block(\"paragraph\", text))\n elif node.name == \"h1\":\n blocks.append(self.create_block(\"heading_1\", node.get_text(strip=True)))\n elif node.name == \"h2\":\n blocks.append(self.create_block(\"heading_2\", node.get_text(strip=True)))\n elif node.name == \"h3\":\n blocks.append(self.create_block(\"heading_3\", node.get_text(strip=True)))\n elif node.name == \"p\":\n code_node = node.find(\"code\")\n if code_node:\n code_text = code_node.get_text()\n language, code = self.extract_language_and_code(code_text)\n blocks.append(self.create_block(\"code\", code, language=language))\n elif self.is_table(str(node)):\n blocks.extend(self.process_table(node))\n else:\n blocks.append(self.create_block(\"paragraph\", node.get_text(strip=True)))\n elif node.name == \"ul\":\n blocks.extend(self.process_list(node, \"bulleted_list_item\"))\n elif node.name == \"ol\":\n blocks.extend(self.process_list(node, \"numbered_list_item\"))\n elif node.name == \"blockquote\":\n blocks.append(self.create_block(\"quote\", node.get_text(strip=True)))\n elif node.name == \"hr\":\n blocks.append(self.create_block(\"divider\", \"\"))\n elif node.name == \"img\":\n blocks.append(self.create_block(\"image\", \"\", image_url=node.get(\"src\")))\n elif node.name == \"a\":\n blocks.append(self.create_block(\"bookmark\", node.get_text(strip=True), link_url=node.get(\"href\")))\n elif node.name == \"table\":\n blocks.extend(self.process_table(node))\n\n for child in node.children:\n if isinstance(child, str):\n continue\n blocks.extend(self.process_node(child))\n\n return blocks\n\n def extract_language_and_code(self, code_text):\n lines = code_text.split(\"\\n\")\n language = lines[0].strip()\n code = \"\\n\".join(lines[1:]).strip()\n return language, code\n\n def is_code_block(self, text):\n return text.startswith(\"```\")\n\n def extract_code_block(self, text):\n lines = text.split(\"\\n\")\n language = lines[0].strip(\"`\").strip()\n code = \"\\n\".join(lines[1:]).strip(\"`\").strip()\n return language, code\n\n def is_table(self, text):\n rows = text.split(\"\\n\")\n if len(rows) < 2:\n return False\n\n has_separator = False\n for i, row in enumerate(rows):\n if \"|\" in row:\n cells = [cell.strip() for cell in row.split(\"|\")]\n cells = [cell for cell in cells if cell] # Remove empty cells\n if i == 1 and all(set(cell) <= set(\"-|\") for cell in cells):\n has_separator = True\n elif not cells:\n return False\n\n return has_separator and len(rows) >= 3\n\n def process_list(self, node, list_type):\n blocks = []\n for item in node.find_all(\"li\"):\n item_text = item.get_text(strip=True)\n checked = item_text.startswith(\"[x]\")\n is_checklist = item_text.startswith(\"[ ]\") or checked\n\n if is_checklist:\n item_text = item_text.replace(\"[x]\", \"\").replace(\"[ ]\", \"\").strip()\n blocks.append(self.create_block(\"to_do\", item_text, checked=checked))\n else:\n blocks.append(self.create_block(list_type, item_text))\n return blocks\n\n def process_table(self, node):\n blocks = []\n header_row = node.find(\"thead\").find(\"tr\") if node.find(\"thead\") else None\n body_rows = node.find(\"tbody\").find_all(\"tr\") if node.find(\"tbody\") else []\n\n if header_row or body_rows:\n table_width = max(\n len(header_row.find_all([\"th\", \"td\"])) if header_row else 0,\n max(len(row.find_all([\"th\", \"td\"])) for row in body_rows),\n )\n\n table_block = self.create_block(\"table\", \"\", table_width=table_width, has_column_header=bool(header_row))\n blocks.append(table_block)\n\n if header_row:\n header_cells = [cell.get_text(strip=True) for cell in header_row.find_all([\"th\", \"td\"])]\n header_row_block = self.create_block(\"table_row\", header_cells)\n blocks.append(header_row_block)\n\n for row in body_rows:\n cells = [cell.get_text(strip=True) for cell in row.find_all([\"th\", \"td\"])]\n row_block = self.create_block(\"table_row\", cells)\n blocks.append(row_block)\n\n return blocks\n\n def create_block(self, block_type: str, content: str, **kwargs) -> Dict[str, Any]:\n block: dict[str, Any] = {\n \"object\": \"block\",\n \"type\": block_type,\n block_type: {},\n }\n\n if block_type in [\n \"paragraph\",\n \"heading_1\",\n \"heading_2\",\n \"heading_3\",\n \"bulleted_list_item\",\n \"numbered_list_item\",\n \"quote\",\n ]:\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n elif block_type == \"to_do\":\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n block[block_type][\"checked\"] = kwargs.get(\"checked\", False)\n elif block_type == \"code\":\n block[block_type][\"rich_text\"] = [\n {\n \"type\": \"text\",\n \"text\": {\n \"content\": content,\n },\n }\n ]\n block[block_type][\"language\"] = kwargs.get(\"language\", \"plain text\")\n elif block_type == \"image\":\n block[block_type] = {\"type\": \"external\", \"external\": {\"url\": kwargs.get(\"image_url\", \"\")}}\n elif block_type == \"divider\":\n pass\n elif block_type == \"bookmark\":\n block[block_type][\"url\"] = kwargs.get(\"link_url\", \"\")\n elif block_type == \"table\":\n block[block_type][\"table_width\"] = kwargs.get(\"table_width\", 0)\n block[block_type][\"has_column_header\"] = kwargs.get(\"has_column_header\", False)\n block[block_type][\"has_row_header\"] = kwargs.get(\"has_row_header\", False)\n elif block_type == \"table_row\":\n block[block_type][\"cells\"] = [[{\"type\": \"text\", \"text\": {\"content\": cell}} for cell in content]]\n\n return block\n" + }, + "markdown_text": { + "_input_type": "MultilineInput", + "advanced": true, + "display_name": "Markdown Text", + "dynamic": false, + "info": "The markdown text to convert to Notion blocks.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "markdown_text", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + } } - ], - "edges": [ - { - "source": "ChatInput-bcq6D", - "target": "ToolCallingAgent-50Gcd", - "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bcq6Dœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "id": "reactflow__edge-ChatInput-bcq6D{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bcq6Dœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ToolCallingAgent-50Gcd{œfieldNameœ:œinput_valueœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "input_value", - "id": "ToolCallingAgent-50Gcd", - "inputTypes": [ - "Message" - ], - "type": "str" - }, - "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-bcq6D", - "name": "message", - "output_types": [ - "Message" - ] - } - }, - "selected": false, - "className": "" - }, - { - "source": "ToolCallingAgent-50Gcd", - "target": "ChatOutput-TSCup", - "sourceHandle": "{œdataTypeœ:œToolCallingAgentœ,œidœ:œToolCallingAgent-50Gcdœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-TSCupœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "id": "reactflow__edge-ToolCallingAgent-50Gcd{œdataTypeœ:œToolCallingAgentœ,œidœ:œToolCallingAgent-50Gcdœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-TSCup{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-TSCupœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-TSCup", - "inputTypes": [ - "Message" - ], - "type": "str" - }, - "sourceHandle": { - "dataType": "ToolCallingAgent", - "id": "ToolCallingAgent-50Gcd", - "name": "response", - "output_types": [ - "Message" - ] - } - }, - "selected": false, - "className": "" - }, - { - "source": "ToolkitComponent-2lNG0", - "target": "ToolCallingAgent-50Gcd", - "sourceHandle": "{œdataTypeœ:œToolkitComponentœ,œidœ:œToolkitComponent-2lNG0œ,œnameœ:œgenerated_toolsœ,œoutput_typesœ:[œBaseToolœ]}", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œToolœ,œBaseToolœ],œtypeœ:œotherœ}", - "id": "reactflow__edge-ToolkitComponent-2lNG0{œdataTypeœ:œToolkitComponentœ,œidœ:œToolkitComponent-2lNG0œ,œnameœ:œgenerated_toolsœ,œoutput_typesœ:[œBaseToolœ]}-ToolCallingAgent-50Gcd{œfieldNameœ:œtoolsœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œToolœ,œBaseToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolCallingAgent-50Gcd", - "inputTypes": [ - "Tool", - "BaseTool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "ToolkitComponent", - "id": "ToolkitComponent-2lNG0", - "name": "generated_tools", - "output_types": [ - "BaseTool" - ] - } - }, - "selected": false, - "className": "" - }, - { - "source": "NotionPageUpdate-6FyYd", - "sourceHandle": "{œdataTypeœ:œNotionPageUpdateœ,œidœ:œNotionPageUpdate-6FyYdœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionPageUpdate", - "id": "NotionPageUpdate-6FyYd", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionPageUpdate-6FyYd{œdataTypeœ:œNotionPageUpdateœ,œidœ:œNotionPageUpdate-6FyYdœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionPageCreator-6SCB5", - "sourceHandle": "{œdataTypeœ:œNotionPageCreatorœ,œidœ:œNotionPageCreator-6SCB5œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionPageCreator", - "id": "NotionPageCreator-6SCB5", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionPageCreator-6SCB5{œdataTypeœ:œNotionPageCreatorœ,œidœ:œNotionPageCreator-6SCB5œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "AddContentToPage-ZezUn", - "sourceHandle": "{œdataTypeœ:œAddContentToPageœ,œidœ:œAddContentToPage-ZezUnœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "AddContentToPage", - "id": "AddContentToPage-ZezUn", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-AddContentToPage-ZezUn{œdataTypeœ:œAddContentToPageœ,œidœ:œAddContentToPage-ZezUnœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionDatabaseProperties-aeWil", - "sourceHandle": "{œdataTypeœ:œNotionDatabasePropertiesœ,œidœ:œNotionDatabaseProperties-aeWilœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionDatabaseProperties", - "id": "NotionDatabaseProperties-aeWil", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionDatabaseProperties-aeWil{œdataTypeœ:œNotionDatabasePropertiesœ,œidœ:œNotionDatabaseProperties-aeWilœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionListPages-znA3w", - "sourceHandle": "{œdataTypeœ:œNotionListPagesœ,œidœ:œNotionListPages-znA3wœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionListPages", - "id": "NotionListPages-znA3w", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionListPages-znA3w{œdataTypeœ:œNotionListPagesœ,œidœ:œNotionListPages-znA3wœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionPageContent-SlL21", - "sourceHandle": "{œdataTypeœ:œNotionPageContentœ,œidœ:œNotionPageContent-SlL21œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionPageContent", - "id": "NotionPageContent-SlL21", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionPageContent-SlL21{œdataTypeœ:œNotionPageContentœ,œidœ:œNotionPageContent-SlL21œ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionUserList-C3eGn", - "sourceHandle": "{œdataTypeœ:œNotionUserListœ,œidœ:œNotionUserList-C3eGnœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionUserList", - "id": "NotionUserList-C3eGn", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionUserList-C3eGn{œdataTypeœ:œNotionUserListœ,œidœ:œNotionUserList-C3eGnœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "NotionSearch-VS2mI", - "sourceHandle": "{œdataTypeœ:œNotionSearchœ,œidœ:œNotionSearch-VS2mIœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}", - "target": "ToolkitComponent-2lNG0", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "tools", - "id": "ToolkitComponent-2lNG0", - "inputTypes": [ - "Tool" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "NotionSearch", - "id": "NotionSearch-VS2mI", - "name": "example_tool_output", - "output_types": [ - "Tool" - ] - } - }, - "id": "reactflow__edge-NotionSearch-VS2mI{œdataTypeœ:œNotionSearchœ,œidœ:œNotionSearch-VS2mIœ,œnameœ:œexample_tool_outputœ,œoutput_typesœ:[œToolœ]}-ToolkitComponent-2lNG0{œfieldNameœ:œtoolsœ,œidœ:œToolkitComponent-2lNG0œ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "OpenAIModel-BJWIg", - "sourceHandle": "{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-BJWIgœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}", - "target": "ToolCallingAgent-50Gcd", - "targetHandle": "{œfieldNameœ:œllmœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "llm", - "id": "ToolCallingAgent-50Gcd", - "inputTypes": [ - "LanguageModel" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-BJWIg", - "name": "model_output", - "output_types": [ - "LanguageModel" - ] - } - }, - "id": "reactflow__edge-OpenAIModel-BJWIg{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-BJWIgœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-ToolCallingAgent-50Gcd{œfieldNameœ:œllmœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "Memory-CTQWu", - "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-CTQWuœ,œnameœ:œmessagesœ,œoutput_typesœ:[œDataœ]}", - "target": "ToolCallingAgent-50Gcd", - "targetHandle": "{œfieldNameœ:œchat_historyœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}", - "data": { - "targetHandle": { - "fieldName": "chat_history", - "id": "ToolCallingAgent-50Gcd", - "inputTypes": [ - "Data" - ], - "type": "other" - }, - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-CTQWu", - "name": "messages", - "output_types": [ - "Data" - ] - } - }, - "id": "reactflow__edge-Memory-CTQWu{œdataTypeœ:œMemoryœ,œidœ:œMemory-CTQWuœ,œnameœ:œmessagesœ,œoutput_typesœ:[œDataœ]}-ToolCallingAgent-50Gcd{œfieldNameœ:œchat_historyœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}", - "className": "", - "selected": false - }, - { - "source": "Prompt-0dWZu", - "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-0dWZuœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", - "target": "ToolCallingAgent-50Gcd", - "targetHandle": "{œfieldNameœ:œsystem_promptœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "system_prompt", - "id": "ToolCallingAgent-50Gcd", - "inputTypes": [ - "Message" - ], - "type": "str" - }, - "sourceHandle": { - "dataType": "Prompt", - "id": "Prompt-0dWZu", - "name": "prompt", - "output_types": [ - "Message" - ] - } - }, - "id": "reactflow__edge-Prompt-0dWZu{œdataTypeœ:œPromptœ,œidœ:œPrompt-0dWZuœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-ToolCallingAgent-50Gcd{œfieldNameœ:œsystem_promptœ,œidœ:œToolCallingAgent-50Gcdœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "className": "", - "selected": false - }, - { - "source": "CurrentDateComponent-NSNQ8", - "sourceHandle": "{œdataTypeœ:œCurrentDateComponentœ,œidœ:œCurrentDateComponent-NSNQ8œ,œnameœ:œcurrent_dateœ,œoutput_typesœ:[œMessageœ]}", - "target": "Prompt-0dWZu", - "targetHandle": "{œfieldNameœ:œCURRENT_DATEœ,œidœ:œPrompt-0dWZuœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "CURRENT_DATE", - "id": "Prompt-0dWZu", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - }, - "sourceHandle": { - "dataType": "CurrentDateComponent", - "id": "CurrentDateComponent-NSNQ8", - "name": "current_date", - "output_types": [ - "Message" - ] - } - }, - "id": "reactflow__edge-CurrentDateComponent-NSNQ8{œdataTypeœ:œCurrentDateComponentœ,œidœ:œCurrentDateComponent-NSNQ8œ,œnameœ:œcurrent_dateœ,œoutput_typesœ:[œMessageœ]}-Prompt-0dWZu{œfieldNameœ:œCURRENT_DATEœ,œidœ:œPrompt-0dWZuœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "className": "", - "selected": false + }, + "type": "AddContentToPage" + }, + "dragging": false, + "height": 330, + "id": "AddContentToPage-3iQ3m", + "measured": { + "height": 330, + "width": 320 + }, + "position": { + "x": 1416.217259177943, + "y": 1709.6205867919527 + }, + "positionAbsolute": { + "x": 1416.217259177943, + "y": 1709.6205867919527 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "A component for creating Notion pages.", + "display_name": "Create Page ", + "id": "NotionPageCreator-Gdgtd", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "A component for creating Notion pages.", + "display_name": "Create Page ", + "documentation": "https://docs.langflow.org/integrations/notion/page-create", + "edited": true, + "field_order": [ + "database_id", + "notion_secret", + "properties_json" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import json\nfrom typing import Dict, Any, Union\nimport requests\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionPageCreator(LCToolComponent):\n display_name: str = \"Create Page \"\n description: str = \"A component for creating Notion pages.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/page-create\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n MultilineInput(\n name=\"properties_json\",\n display_name=\"Properties (JSON)\",\n info=\"The properties of the new page as a JSON string.\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageCreatorSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database.\")\n properties_json: str = Field(..., description=\"The properties of the new page as a JSON string.\")\n\n def run_model(self) -> Data:\n result = self._create_notion_page(self.database_id, self.properties_json)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the created page data\n output = \"Created page properties:\\n\"\n for prop_name, prop_value in result.get(\"properties\", {}).items():\n output += f\"{prop_name}: {prop_value}\\n\"\n return Data(text=output, data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"create_notion_page\",\n description=\"Create a new page in a Notion database. IMPORTANT: Use the tool to check the Database properties for more details before using this tool.\",\n func=self._create_notion_page,\n args_schema=self.NotionPageCreatorSchema,\n )\n\n def _create_notion_page(self, database_id: str, properties_json: str) -> Union[Dict[str, Any], str]:\n if not database_id or not properties_json:\n return \"Invalid input. Please provide 'database_id' and 'properties_json'.\"\n\n try:\n properties = json.loads(properties_json)\n except json.JSONDecodeError as e:\n return f\"Invalid properties format. Please provide a valid JSON string. Error: {str(e)}\"\n\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"parent\": {\"database_id\": database_id},\n \"properties\": properties,\n }\n\n try:\n response = requests.post(\"https://api.notion.com/v1/pages\", headers=headers, json=data)\n response.raise_for_status()\n result = response.json()\n return result\n except requests.exceptions.RequestException as e:\n error_message = f\"Failed to create Notion page. Error: {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n\n def __call__(self, *args, **kwargs):\n return self._create_notion_page(*args, **kwargs)\n" + }, + "database_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Database ID", + "dynamic": false, + "info": "The ID of the Notion database.", + "list": false, + "load_from_db": false, + "name": "database_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "properties_json": { + "_input_type": "MultilineInput", + "advanced": true, + "display_name": "Properties (JSON)", + "dynamic": false, + "info": "The properties of the new page as a JSON string.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "properties_json", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } } - ], - "viewport": { - "x": 97.72528949998423, - "y": -211.85229348429561, - "zoom": 0.41621432461249197 - } - }, - "description": "This flow creates an AI assistant that interacts with your Notion workspace. It understands natural language requests, performs actions in Notion (like creating pages or searching for information), and provides helpful responses. To use it, simply start a conversation by asking the agent to perform a Notion-related task, and it will guide you through the process, making it easy to manage your Notion workspace through chat.", - "name": "Conversational Notion Agent", - "last_tested_version": "1.0.17", - "endpoint_name": null, - "is_component": false + }, + "type": "NotionPageCreator" + }, + "dragging": false, + "height": 302, + "id": "NotionPageCreator-Gdgtd", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 1413.9782390799146, + "y": 2051.645785494985 + }, + "positionAbsolute": { + "x": 1413.9782390799146, + "y": 2051.645785494985 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieve properties of a Notion database.", + "display_name": "List Database Properties ", + "id": "NotionDatabaseProperties-F3Vjj", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Retrieve properties of a Notion database.", + "display_name": "List Database Properties ", + "documentation": "https://docs.langflow.org/integrations/notion/list-database-properties", + "edited": true, + "field_order": [ + "database_id", + "notion_secret" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import requests\nfrom typing import Dict, Union\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionDatabaseProperties(LCToolComponent):\n display_name: str = \"List Database Properties \"\n description: str = \"Retrieve properties of a Notion database.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/list-database-properties\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionDatabasePropertiesSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database.\")\n\n def run_model(self) -> Data:\n result = self._fetch_database_properties(self.database_id)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the properties\n return Data(text=str(result), data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_database_properties\",\n description=\"Retrieve properties of a Notion database. Input should include the database ID.\",\n func=self._fetch_database_properties,\n args_schema=self.NotionDatabasePropertiesSchema,\n )\n\n def _fetch_database_properties(self, database_id: str) -> Union[Dict, str]:\n url = f\"https://api.notion.com/v1/databases/{database_id}\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\", # Use the latest supported version\n }\n try:\n response = requests.get(url, headers=headers)\n response.raise_for_status()\n data = response.json()\n properties = data.get(\"properties\", {})\n return properties\n except requests.exceptions.RequestException as e:\n return f\"Error fetching Notion database properties: {str(e)}\"\n except ValueError as e:\n return f\"Error parsing Notion API response: {str(e)}\"\n except Exception as e:\n return f\"An unexpected error occurred: {str(e)}\"\n" + }, + "database_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Database ID", + "dynamic": false, + "info": "The ID of the Notion database.", + "list": false, + "load_from_db": false, + "name": "database_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + } + } + }, + "type": "NotionDatabaseProperties" + }, + "dragging": false, + "height": 302, + "id": "NotionDatabaseProperties-F3Vjj", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 1004.5753613670959, + "y": 1713.914531491452 + }, + "positionAbsolute": { + "x": 1004.5753613670959, + "y": 1713.914531491452 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Query a Notion database with filtering and sorting. The input should be a JSON string containing the 'filter' and 'sorts' objects. Example input:\n{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}", + "display_name": "List Pages ", + "id": "NotionListPages-9cC7f", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Query a Notion database with filtering and sorting. The input should be a JSON string containing the 'filter' and 'sorts' objects. Example input:\n{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}", + "display_name": "List Pages ", + "documentation": "https://docs.langflow.org/integrations/notion/list-pages", + "edited": true, + "field_order": [ + "notion_secret", + "database_id", + "query_json" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import requests\nimport json\nfrom typing import Dict, Any, List, Optional\nfrom pydantic import BaseModel, Field\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionListPages(LCToolComponent):\n display_name: str = \"List Pages \"\n description: str = (\n \"Query a Notion database with filtering and sorting. \"\n \"The input should be a JSON string containing the 'filter' and 'sorts' objects. \"\n \"Example input:\\n\"\n '{\"filter\": {\"property\": \"Status\", \"select\": {\"equals\": \"Done\"}}, \"sorts\": [{\"timestamp\": \"created_time\", \"direction\": \"descending\"}]}'\n )\n documentation: str = \"https://docs.langflow.org/integrations/notion/list-pages\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n StrInput(\n name=\"database_id\",\n display_name=\"Database ID\",\n info=\"The ID of the Notion database to query.\",\n ),\n MultilineInput(\n name=\"query_json\",\n display_name=\"Database query (JSON)\",\n info=\"A JSON string containing the filters and sorts that will be used for querying the database. Leave empty for no filters or sorts.\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n class NotionListPagesSchema(BaseModel):\n database_id: str = Field(..., description=\"The ID of the Notion database to query.\")\n query_json: Optional[str] = Field(\n default=\"\",\n description=\"A JSON string containing the filters and sorts for querying the database. Leave empty for no filters or sorts.\",\n )\n\n def run_model(self) -> List[Data]:\n result = self._query_notion_database(self.database_id, self.query_json)\n\n if isinstance(result, str):\n # An error occurred, return it as a single record\n return [Data(text=result)]\n\n records = []\n combined_text = f\"Pages found: {len(result)}\\n\\n\"\n\n for page in result:\n page_data = {\n \"id\": page[\"id\"],\n \"url\": page[\"url\"],\n \"created_time\": page[\"created_time\"],\n \"last_edited_time\": page[\"last_edited_time\"],\n \"properties\": page[\"properties\"],\n }\n\n text = (\n f\"id: {page['id']}\\n\"\n f\"url: {page['url']}\\n\"\n f\"created_time: {page['created_time']}\\n\"\n f\"last_edited_time: {page['last_edited_time']}\\n\"\n f\"properties: {json.dumps(page['properties'], indent=2)}\\n\\n\"\n )\n\n combined_text += text\n records.append(Data(text=text, **page_data))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_list_pages\",\n description=self.description,\n func=self._query_notion_database,\n args_schema=self.NotionListPagesSchema,\n )\n\n def _query_notion_database(self, database_id: str, query_json: Optional[str] = None) -> List[Dict[str, Any]] | str:\n url = f\"https://api.notion.com/v1/databases/{database_id}/query\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n query_payload = {}\n if query_json and query_json.strip():\n try:\n query_payload = json.loads(query_json)\n except json.JSONDecodeError as e:\n return f\"Invalid JSON format for query: {str(e)}\"\n\n try:\n response = requests.post(url, headers=headers, json=query_payload)\n response.raise_for_status()\n results = response.json()\n return results[\"results\"]\n except requests.exceptions.RequestException as e:\n return f\"Error querying Notion database: {str(e)}\"\n except KeyError:\n return \"Unexpected response format from Notion API\"\n except Exception as e:\n return f\"An unexpected error occurred: {str(e)}\"\n" + }, + "database_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Database ID", + "dynamic": false, + "info": "The ID of the Notion database to query.", + "list": false, + "load_from_db": false, + "name": "database_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "query_json": { + "_input_type": "MultilineInput", + "advanced": true, + "display_name": "Database query (JSON)", + "dynamic": false, + "info": "A JSON string containing the filters and sorts that will be used for querying the database. Leave empty for no filters or sorts.", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "query_json", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + } + }, + "type": "NotionListPages" + }, + "dragging": false, + "height": 470, + "id": "NotionListPages-9cC7f", + "measured": { + "height": 470, + "width": 320 + }, + "position": { + "x": 1006.1848442547046, + "y": 2022.7880909242833 + }, + "positionAbsolute": { + "x": 1006.1848442547046, + "y": 2022.7880909242833 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieve users from Notion.", + "display_name": "List Users ", + "id": "NotionUserList-4C1Rj", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Retrieve users from Notion.", + "display_name": "List Users ", + "documentation": "https://docs.langflow.org/integrations/notion/list-users", + "edited": true, + "field_order": [ + "notion_secret" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import requests\nfrom typing import List, Dict\nfrom pydantic import BaseModel\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionUserList(LCToolComponent):\n display_name = \"List Users \"\n description = \"Retrieve users from Notion.\"\n documentation = \"https://docs.langflow.org/integrations/notion/list-users\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionUserListSchema(BaseModel):\n pass\n\n def run_model(self) -> List[Data]:\n users = self._list_users()\n records = []\n combined_text = \"\"\n\n for user in users:\n output = \"User:\\n\"\n for key, value in user.items():\n output += f\"{key.replace('_', ' ').title()}: {value}\\n\"\n output += \"________________________\\n\"\n\n combined_text += output\n records.append(Data(text=output, data=user))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_list_users\",\n description=\"Retrieve users from Notion.\",\n func=self._list_users,\n args_schema=self.NotionUserListSchema,\n )\n\n def _list_users(self) -> List[Dict]:\n url = \"https://api.notion.com/v1/users\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n response = requests.get(url, headers=headers)\n response.raise_for_status()\n\n data = response.json()\n results = data[\"results\"]\n\n users = []\n for user in results:\n user_data = {\n \"id\": user[\"id\"],\n \"type\": user[\"type\"],\n \"name\": user.get(\"name\", \"\"),\n \"avatar_url\": user.get(\"avatar_url\", \"\"),\n }\n users.append(user_data)\n\n return users\n" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + } + } + }, + "type": "NotionUserList" + }, + "dragging": false, + "height": 302, + "id": "NotionUserList-4C1Rj", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 2260.15497405973, + "y": 1717.4551881467207 + }, + "positionAbsolute": { + "x": 2260.15497405973, + "y": 1717.4551881467207 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieve the content of a Notion page as plain text.", + "display_name": "Page Content Viewer ", + "id": "NotionPageContent-didQ9", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Retrieve the content of a Notion page as plain text.", + "display_name": "Page Content Viewer ", + "documentation": "https://docs.langflow.org/integrations/notion/page-content-viewer", + "edited": true, + "field_order": [ + "page_id", + "notion_secret" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import requests\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom langflow.io import Output\n\nclass NotionPageContent(LCToolComponent):\n display_name = \"Page Content Viewer \"\n description = \"Retrieve the content of a Notion page as plain text.\"\n documentation = \"https://docs.langflow.org/integrations/notion/page-content-viewer\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"page_id\",\n display_name=\"Page ID\",\n info=\"The ID of the Notion page to retrieve.\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageContentSchema(BaseModel):\n page_id: str = Field(..., description=\"The ID of the Notion page to retrieve.\")\n\n def run_model(self) -> Data:\n result = self._retrieve_page_content(self.page_id)\n if isinstance(result, str) and result.startswith(\"Error:\"):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the content\n return Data(text=result, data={\"content\": result})\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_page_content\",\n description=\"Retrieve the content of a Notion page as plain text.\",\n func=self._retrieve_page_content,\n args_schema=self.NotionPageContentSchema,\n )\n\n def _retrieve_page_content(self, page_id: str) -> str:\n blocks_url = f\"https://api.notion.com/v1/blocks/{page_id}/children?page_size=100\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Notion-Version\": \"2022-06-28\",\n }\n try:\n blocks_response = requests.get(blocks_url, headers=headers)\n blocks_response.raise_for_status()\n blocks_data = blocks_response.json()\n return self.parse_blocks(blocks_data.get(\"results\", []))\n except requests.exceptions.RequestException as e:\n error_message = f\"Error: Failed to retrieve Notion page content. {str(e)}\"\n if hasattr(e, \"response\") and e.response is not None:\n error_message += f\" Status code: {e.response.status_code}, Response: {e.response.text}\"\n return error_message\n except Exception as e:\n return f\"Error: An unexpected error occurred while retrieving Notion page content. {str(e)}\"\n\n def parse_blocks(self, blocks: list) -> str:\n content = \"\"\n for block in blocks:\n block_type = block.get(\"type\")\n if block_type in [\"paragraph\", \"heading_1\", \"heading_2\", \"heading_3\", \"quote\"]:\n content += self.parse_rich_text(block[block_type].get(\"rich_text\", [])) + \"\\n\\n\"\n elif block_type in [\"bulleted_list_item\", \"numbered_list_item\"]:\n content += self.parse_rich_text(block[block_type].get(\"rich_text\", [])) + \"\\n\"\n elif block_type == \"to_do\":\n content += self.parse_rich_text(block[\"to_do\"].get(\"rich_text\", [])) + \"\\n\"\n elif block_type == \"code\":\n content += self.parse_rich_text(block[\"code\"].get(\"rich_text\", [])) + \"\\n\\n\"\n elif block_type == \"image\":\n content += f\"[Image: {block['image'].get('external', {}).get('url', 'No URL')}]\\n\\n\"\n elif block_type == \"divider\":\n content += \"---\\n\\n\"\n return content.strip()\n\n def parse_rich_text(self, rich_text: list) -> str:\n return \"\".join(segment.get(\"plain_text\", \"\") for segment in rich_text)\n\n def __call__(self, *args, **kwargs):\n return self._retrieve_page_content(*args, **kwargs)\n" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "page_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Page ID", + "dynamic": false, + "info": "The ID of the Notion page to retrieve.", + "list": false, + "load_from_db": false, + "name": "page_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + } + }, + "type": "NotionPageContent" + }, + "dragging": false, + "height": 330, + "id": "NotionPageContent-didQ9", + "measured": { + "height": 330, + "width": 320 + }, + "position": { + "x": 1826.4242329724448, + "y": 1715.6365113286927 + }, + "positionAbsolute": { + "x": 1826.4242329724448, + "y": 1715.6365113286927 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Searches all pages and databases that have been shared with an integration.", + "display_name": "Search ", + "id": "NotionSearch-Qrabo", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Searches all pages and databases that have been shared with an integration. The search field can be an empty value to show all values from that search", + "display_name": "Search ", + "documentation": "https://docs.langflow.org/integrations/notion/search", + "edited": true, + "field_order": [ + "notion_secret", + "query", + "filter_value", + "sort_direction" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import requests\nfrom typing import Dict, Any, List\nfrom pydantic import BaseModel, Field\nfrom langflow.io import Output\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, DropdownInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\n\n\nclass NotionSearch(LCToolComponent):\n display_name: str = \"Search \"\n description: str = \"Searches all pages and databases that have been shared with an integration. The search field can be an empty value to show all values from that search\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/search\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n StrInput(\n name=\"query\",\n display_name=\"Search Query\",\n info=\"The text that the API compares page and database titles against.\",\n ),\n DropdownInput(\n name=\"filter_value\",\n display_name=\"Filter Type\",\n info=\"Limits the results to either only pages or only databases.\",\n options=[\"page\", \"database\"],\n value=\"page\",\n ),\n DropdownInput(\n name=\"sort_direction\",\n display_name=\"Sort Direction\",\n info=\"The direction to sort the results.\",\n options=[\"ascending\", \"descending\"],\n value=\"descending\",\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionSearchSchema(BaseModel):\n query: str = Field(..., description=\"The search query text.\")\n filter_value: str = Field(default=\"page\", description=\"Filter type: 'page' or 'database'.\")\n sort_direction: str = Field(default=\"descending\", description=\"Sort direction: 'ascending' or 'descending'.\")\n\n def run_model(self) -> List[Data]:\n results = self._search_notion(self.query, self.filter_value, self.sort_direction)\n records = []\n combined_text = f\"Results found: {len(results)}\\n\\n\"\n\n for result in results:\n result_data = {\n \"id\": result[\"id\"],\n \"type\": result[\"object\"],\n \"last_edited_time\": result[\"last_edited_time\"],\n }\n\n if result[\"object\"] == \"page\":\n result_data[\"title_or_url\"] = result[\"url\"]\n text = f\"id: {result['id']}\\ntitle_or_url: {result['url']}\\n\"\n elif result[\"object\"] == \"database\":\n if \"title\" in result and isinstance(result[\"title\"], list) and len(result[\"title\"]) > 0:\n result_data[\"title_or_url\"] = result[\"title\"][0][\"plain_text\"]\n text = f\"id: {result['id']}\\ntitle_or_url: {result['title'][0]['plain_text']}\\n\"\n else:\n result_data[\"title_or_url\"] = \"N/A\"\n text = f\"id: {result['id']}\\ntitle_or_url: N/A\\n\"\n\n text += f\"type: {result['object']}\\nlast_edited_time: {result['last_edited_time']}\\n\\n\"\n combined_text += text\n records.append(Data(text=text, data=result_data))\n\n self.status = records\n return records\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"notion_search\",\n description=\"Search Notion pages and databases. Input should include the search query and optionally filter type and sort direction.\",\n func=self._search_notion,\n args_schema=self.NotionSearchSchema,\n )\n\n def _search_notion(\n self, query: str, filter_value: str = \"page\", sort_direction: str = \"descending\"\n ) -> List[Dict[str, Any]]:\n url = \"https://api.notion.com/v1/search\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\",\n }\n\n data = {\n \"query\": query,\n \"filter\": {\"value\": filter_value, \"property\": \"object\"},\n \"sort\": {\"direction\": sort_direction, \"timestamp\": \"last_edited_time\"},\n }\n\n response = requests.post(url, headers=headers, json=data)\n response.raise_for_status()\n\n results = response.json()\n return results[\"results\"]\n" + }, + "filter_value": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "display_name": "Filter Type", + "dynamic": false, + "info": "Limits the results to either only pages or only databases.", + "name": "filter_value", + "options": [ + "page", + "database" + ], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "page" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "query": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Search Query", + "dynamic": false, + "info": "The text that the API compares page and database titles against.", + "list": false, + "load_from_db": false, + "name": "query", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "sort_direction": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "display_name": "Sort Direction", + "dynamic": false, + "info": "The direction to sort the results.", + "name": "sort_direction", + "options": [ + "ascending", + "descending" + ], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "descending" + } + } + }, + "type": "NotionSearch" + }, + "dragging": false, + "height": 386, + "id": "NotionSearch-Qrabo", + "measured": { + "height": 386, + "width": 320 + }, + "position": { + "x": 2258.1166047519732, + "y": 2034.3959294952945 + }, + "positionAbsolute": { + "x": 2258.1166047519732, + "y": 2034.3959294952945 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Update the properties of a Notion page.", + "display_name": "Update Page Property ", + "id": "NotionPageUpdate-DWOeO", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Update the properties of a Notion page.", + "display_name": "Update Page Property ", + "documentation": "https://docs.langflow.org/integrations/notion/page-update", + "edited": true, + "field_order": [ + "page_id", + "properties", + "notion_secret" + ], + "frozen": false, + "icon": "NotionDirectoryLoader", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "hidden": true, + "method": "run_model", + "name": "example_output", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "example_tool_output", + "selected": "Tool", + "types": [ + "Tool" + ], + "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": "import json\nimport requests\nfrom typing import Dict, Any, Union\nfrom pydantic import BaseModel, Field\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, StrInput, MultilineInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\nfrom langchain.tools import StructuredTool\nfrom loguru import logger\nfrom langflow.io import Output\n\nclass NotionPageUpdate(LCToolComponent):\n display_name: str = \"Update Page Property \"\n description: str = \"Update the properties of a Notion page.\"\n documentation: str = \"https://docs.langflow.org/integrations/notion/page-update\"\n icon = \"NotionDirectoryLoader\"\n\n inputs = [\n StrInput(\n name=\"page_id\",\n display_name=\"Page ID\",\n info=\"The ID of the Notion page to update.\",\n ),\n MultilineInput(\n name=\"properties\",\n display_name=\"Properties\",\n info=\"The properties to update on the page (as a JSON string or a dictionary).\",\n ),\n SecretStrInput(\n name=\"notion_secret\",\n display_name=\"Notion Secret\",\n info=\"The Notion integration token.\",\n required=True,\n ),\n ]\n outputs = [\n Output(name=\"example_output\", display_name=\"Data\", method=\"run_model\"),\n Output(name=\"example_tool_output\", display_name=\"Tool\", method=\"build_tool\"),\n ]\n\n class NotionPageUpdateSchema(BaseModel):\n page_id: str = Field(..., description=\"The ID of the Notion page to update.\")\n properties: Union[str, Dict[str, Any]] = Field(\n ..., description=\"The properties to update on the page (as a JSON string or a dictionary).\"\n )\n\n def run_model(self) -> Data:\n result = self._update_notion_page(self.page_id, self.properties)\n if isinstance(result, str):\n # An error occurred, return it as text\n return Data(text=result)\n else:\n # Success, return the updated page data\n output = \"Updated page properties:\\n\"\n for prop_name, prop_value in result.get(\"properties\", {}).items():\n output += f\"{prop_name}: {prop_value}\\n\"\n return Data(text=output, data=result)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"update_notion_page\",\n description=\"Update the properties of a Notion page. IMPORTANT: Use the tool to check the Database properties for more details before using this tool.\",\n func=self._update_notion_page,\n args_schema=self.NotionPageUpdateSchema,\n )\n\n def _update_notion_page(self, page_id: str, properties: Union[str, Dict[str, Any]]) -> Union[Dict[str, Any], str]:\n url = f\"https://api.notion.com/v1/pages/{page_id}\"\n headers = {\n \"Authorization\": f\"Bearer {self.notion_secret}\",\n \"Content-Type\": \"application/json\",\n \"Notion-Version\": \"2022-06-28\", # Use the latest supported version\n }\n\n # Parse properties if it's a string\n if isinstance(properties, str):\n try:\n parsed_properties = json.loads(properties)\n except json.JSONDecodeError as e:\n error_message = f\"Invalid JSON format for properties: {str(e)}\"\n logger.error(error_message)\n return error_message\n\n else:\n parsed_properties = properties\n\n data = {\"properties\": parsed_properties}\n\n try:\n logger.info(f\"Sending request to Notion API: URL: {url}, Data: {json.dumps(data)}\")\n response = requests.patch(url, headers=headers, json=data)\n response.raise_for_status()\n updated_page = response.json()\n\n logger.info(f\"Successfully updated Notion page. Response: {json.dumps(updated_page)}\")\n return updated_page\n except requests.exceptions.HTTPError as e:\n error_message = f\"HTTP Error occurred: {str(e)}\"\n if e.response is not None:\n error_message += f\"\\nStatus code: {e.response.status_code}\"\n error_message += f\"\\nResponse body: {e.response.text}\"\n logger.error(error_message)\n return error_message\n except requests.exceptions.RequestException as e:\n error_message = f\"An error occurred while making the request: {str(e)}\"\n logger.error(error_message)\n return error_message\n except Exception as e:\n error_message = f\"An unexpected error occurred: {str(e)}\"\n logger.error(error_message)\n return error_message\n\n def __call__(self, *args, **kwargs):\n return self._update_notion_page(*args, **kwargs)\n" + }, + "notion_secret": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Notion Secret", + "dynamic": false, + "info": "The Notion integration token.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "notion_secret", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "page_id": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Page ID", + "dynamic": false, + "info": "The ID of the Notion page to update.", + "list": false, + "load_from_db": false, + "name": "page_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "properties": { + "_input_type": "MultilineInput", + "advanced": true, + "display_name": "Properties", + "dynamic": false, + "info": "The properties to update on the page (as a JSON string or a dictionary).", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "properties", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + } + }, + "type": "NotionPageUpdate" + }, + "dragging": false, + "height": 302, + "id": "NotionPageUpdate-DWOeO", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 1827.0574354713603, + "y": 2055.9948126656136 + }, + "positionAbsolute": { + "x": 1827.0574354713603, + "y": 2055.9948126656136 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "ToolCallingAgent-VyZhN", + "node": { + "base_classes": [ + "AgentExecutor", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "An agent designed to utilize various tools seamlessly within workflows.", + "display_name": "Tool Calling Agent", + "documentation": "", + "edited": false, + "field_order": [ + "tools", + "input_value", + "handle_parsing_errors", + "verbose", + "max_iterations", + "agent_description", + "llm", + "system_prompt", + "chat_history" + ], + "frozen": false, + "icon": "LangChain", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Agent", + "group_outputs": false, + "hidden": true, + "method": "build_agent", + "name": "agent", + "options": null, + "required_inputs": [], + "selected": "AgentExecutor", + "tool_mode": false, + "types": [ + "AgentExecutor" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Response", + "group_outputs": false, + "method": "message_response", + "name": "response", + "options": null, + "required_inputs": [], + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "agent_description": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "Agent Description [Deprecated]", + "dynamic": false, + "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically. This feature is deprecated and will be removed in future versions.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "agent_description", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "A helpful assistant with access to the following tools:" + }, + "chat_history": { + "_input_type": "DataInput", + "advanced": false, + "display_name": "Chat Memory", + "dynamic": false, + "info": "This input stores the chat history, allowing the agent to remember previous conversations.", + "input_types": [ + "Data" + ], + "list": true, + "list_add_label": "Add More", + "name": "chat_history", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "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 langchain.agents import create_tool_calling_agent\nfrom langchain_core.prompts import ChatPromptTemplate\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.inputs import MessageTextInput\nfrom langflow.inputs.inputs import DataInput, HandleInput\nfrom langflow.schema import Data\n\n\nclass ToolCallingAgentComponent(LCToolsAgentComponent):\n display_name: str = \"Tool Calling Agent\"\n description: str = \"An agent designed to utilize various tools seamlessly within workflows.\"\n icon = \"LangChain\"\n name = \"ToolCallingAgent\"\n\n inputs = [\n *LCToolsAgentComponent._base_inputs,\n HandleInput(\n name=\"llm\",\n display_name=\"Language Model\",\n input_types=[\"LanguageModel\"],\n required=True,\n info=\"Language model that the agent utilizes to perform tasks effectively.\",\n ),\n MessageTextInput(\n name=\"system_prompt\",\n display_name=\"System Prompt\",\n info=\"System prompt to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n ),\n DataInput(\n name=\"chat_history\",\n display_name=\"Chat Memory\",\n is_list=True,\n advanced=True,\n info=\"This input stores the chat history, allowing the agent to remember previous conversations.\",\n ),\n ]\n\n def get_chat_history_data(self) -> list[Data] | None:\n return self.chat_history\n\n def create_agent_runnable(self):\n messages = [\n (\"system\", \"{system_prompt}\"),\n (\"placeholder\", \"{chat_history}\"),\n (\"human\", \"{input}\"),\n (\"placeholder\", \"{agent_scratchpad}\"),\n ]\n prompt = ChatPromptTemplate.from_messages(messages)\n self.validate_tool_names()\n try:\n return create_tool_calling_agent(self.llm, self.tools or [], prompt)\n except NotImplementedError as e:\n message = f\"{self.display_name} does not support tool calling. Please try using a compatible model.\"\n raise NotImplementedError(message) from e\n" + }, + "handle_parsing_errors": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Handle Parse Errors", + "dynamic": false, + "info": "Should the Agent fix errors when reading user input for better processing?", + "list": false, + "list_add_label": "Add More", + "name": "handle_parsing_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "input_value": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input provided by the user for the agent to process.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "llm": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Language Model", + "dynamic": false, + "info": "Language model that the agent utilizes to perform tasks effectively.", + "input_types": [ + "LanguageModel" + ], + "list": false, + "list_add_label": "Add More", + "name": "llm", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "max_iterations": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Iterations", + "dynamic": false, + "info": "The maximum number of attempts the agent can make to complete its task before it stops.", + "list": false, + "list_add_label": "Add More", + "name": "max_iterations", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 15 + }, + "system_prompt": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "System Prompt", + "dynamic": false, + "info": "System prompt to guide the agent's behavior.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "system_prompt", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "tools": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Tools", + "dynamic": false, + "info": "These are the tools that the agent can use to help with tasks.", + "input_types": [ + "Tool" + ], + "list": true, + "list_add_label": "Add More", + "name": "tools", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "verbose": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Verbose", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "name": "verbose", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + } + }, + "tool_mode": false + }, + "type": "ToolCallingAgent" + }, + "dragging": false, + "height": 532, + "id": "ToolCallingAgent-VyZhN", + "measured": { + "height": 532, + "width": 320 + }, + "position": { + "x": 2186.0530739759893, + "y": 612.1744804997304 + }, + "positionAbsolute": { + "x": 2186.0530739759893, + "y": 612.1744804997304 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "ChatOutput-jessc", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "should_store_message", + "sender", + "sender_name", + "session_id", + "data_template", + "background_color", + "chat_icon", + "text_color", + "clean_data" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Output Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "clean_data": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Basic Clean Data", + "dynamic": false, + "info": "Whether to clean the data", + "list": false, + "list_add_label": "Add More", + "name": "clean_data", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs import BoolInput\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\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 minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output 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 # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\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\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" + }, + "data_template": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "data_template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{text}" + }, + "input_value": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Inputs", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_value", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "AI" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "type": "ChatOutput" + }, + "dragging": false, + "height": 302, + "id": "ChatOutput-jessc", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 2649.190603849412, + "y": 841.0466487848925 + }, + "positionAbsolute": { + "x": 2649.190603849412, + "y": 841.0466487848925 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "ChatInput-my2y0", + "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", + "should_store_message", + "sender", + "sender_name", + "session_id", + "files", + "background_color", + "chat_icon", + "text_color" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Chat Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "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.inputs import BoolInput\nfrom langflow.io import (\n DropdownInput,\n FileInput,\n MessageTextInput,\n MultilineInput,\n Output,\n)\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_USER,\n MESSAGE_SENDER_USER,\n)\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 minimized = True\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n input_types=[],\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 temp_file=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=\"Chat Message\", name=\"message\", method=\"message_response\"),\n ]\n\n async def message_response(self) -> Message:\n background_color = self.background_color\n text_color = self.text_color\n icon = self.chat_icon\n\n message = await Message.create(\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={\n \"background_color\": background_color,\n \"text_color\": text_color,\n \"icon\": icon,\n },\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = await 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", + "advanced": true, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "jpg", + "jpeg", + "png", + "bmp", + "image" + ], + "file_path": "", + "info": "Files to be sent with the message.", + "list": true, + "list_add_label": "Add More", + "name": "files", + "placeholder": "", + "required": false, + "show": true, + "temp_file": true, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "input_value": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "Input Text", + "dynamic": false, + "info": "Message to be passed as input.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "list users" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "type": "ChatInput" + }, + "dragging": false, + "height": 302, + "id": "ChatInput-my2y0", + "measured": { + "height": 302, + "width": 320 + }, + "position": { + "x": 557.6262725075026, + "y": 724.8518930903978 + }, + "positionAbsolute": { + "x": 557.6262725075026, + "y": 724.8518930903978 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "ToolkitComponent-mfg6v", + "node": { + "base_classes": [ + "BaseTool", + "Data" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Combines multiple tools into a single list of tools.", + "display_name": "Toolkit", + "documentation": "", + "edited": true, + "field_order": [ + "tools" + ], + "frozen": false, + "icon": "pocket-knife", + "lf_version": "1.0.17", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Tools", + "method": "generate_toolkit", + "name": "generated_tools", + "selected": "BaseTool", + "types": [ + "BaseTool" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool Data", + "hidden": true, + "method": "generate_tool_data", + "name": "tool_data", + "selected": "Data", + "types": [ + "Data" + ], + "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 typing import List\r\nfrom langflow.custom import Component\r\nfrom langflow.inputs import HandleInput, MessageTextInput\r\nfrom langflow.template import Output\r\nfrom langflow.field_typing import Tool, Embeddings\r\nfrom langchain.tools.base import BaseTool, StructuredTool\r\nfrom langflow.schema import Data\r\n\r\nclass ToolkitComponent(Component):\r\n display_name = \"Toolkit\"\r\n description = \"Combines multiple tools into a single list of tools.\"\r\n icon = \"pocket-knife\"\r\n\r\n inputs = [\r\n HandleInput(\r\n name=\"tools\",\r\n display_name=\"Tools\",\r\n input_types=[\"Tool\"],\r\n info=\"List of tools to combine.\",\r\n is_list=True,\r\n ),\r\n ]\r\n\r\n outputs = [\r\n Output(display_name=\"Tools\", name=\"generated_tools\", method=\"generate_toolkit\"),\r\n Output(display_name=\"Tool Data\", name=\"tool_data\", method=\"generate_tool_data\"),\r\n ]\r\n\r\n def generate_toolkit(self) -> List[BaseTool]:\r\n combined_tools = []\r\n name_count = {}\r\n for index, tool in enumerate(self.tools):\r\n self.log(f\"Processing tool {index}: {type(tool)}\")\r\n if isinstance(tool, (BaseTool, StructuredTool)):\r\n processed_tool = tool\r\n elif hasattr(tool, 'build_tool'):\r\n processed_tool = tool.build_tool()\r\n else:\r\n self.log(f\"Unsupported tool type: {type(tool)}. Attempting to process anyway.\")\r\n processed_tool = tool\r\n\r\n original_name = getattr(processed_tool, 'name', f\"UnnamedTool_{index}\")\r\n self.log(f\"Original tool name: {original_name}\")\r\n\r\n if original_name not in name_count:\r\n name_count[original_name] = 0\r\n final_name = original_name\r\n else:\r\n name_count[original_name] += 1\r\n final_name = f\"{original_name}_{name_count[original_name]}\"\r\n\r\n if hasattr(processed_tool, 'name'):\r\n processed_tool.name = final_name\r\n\r\n self.log(f\"Final tool name: {final_name}\")\r\n\r\n if isinstance(processed_tool, StructuredTool) and hasattr(processed_tool, 'args_schema'):\r\n processed_tool.args_schema.name = f\"{final_name}_Schema\"\r\n\r\n combined_tools.append(processed_tool)\r\n\r\n debug_info = \"\\n\".join([f\"Tool {i}: {getattr(tool, 'name', f'UnnamedTool_{i}')} (Original: {getattr(tool, '_original_name', 'N/A')}) - Type: {type(tool)}\" for i, tool in enumerate(combined_tools)])\r\n self.log(\"Final toolkit composition:\")\r\n self.log(debug_info)\r\n\r\n\r\n self.status = combined_tools\r\n return combined_tools\r\n\r\n def generate_tool_data(self) -> List[Data]:\r\n tool_data = []\r\n for tool in self.generate_toolkit():\r\n tool_data.append(Data(\r\n data={\r\n \"name\": getattr(tool, 'name', 'Unnamed Tool'),\r\n \"description\": getattr(tool, 'description', 'No description available')\r\n }\r\n ))\r\n return tool_data" + }, + "tools": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Tools", + "dynamic": false, + "info": "List of tools to combine.", + "input_types": [ + "Tool" + ], + "list": true, + "name": "tools", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + } + } + }, + "type": "ToolkitComponent" + }, + "dragging": false, + "height": 292, + "id": "ToolkitComponent-mfg6v", + "measured": { + "height": 292, + "width": 320 + }, + "position": { + "x": 1731.8884789245508, + "y": 1378.7846304343796 + }, + "positionAbsolute": { + "x": 1731.8884789245508, + "y": 1378.7846304343796 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "OpenAIModel-gIFGV", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "system_message", + "stream", + "max_tokens", + "model_kwargs", + "json_mode", + "model_name", + "openai_api_base", + "api_key", + "temperature", + "seed", + "max_retries", + "timeout" + ], + "frozen": false, + "icon": "OpenAI", + "legacy": false, + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "options": null, + "required_inputs": [], + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "options": null, + "required_inputs": [ + "api_key" + ], + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "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 typing import Any\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import (\n OPENAI_MODEL_NAMES,\n OPENAI_REASONING_MODEL_NAMES,\n)\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\nfrom langflow.logging import logger\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = [\n *LCModelComponent._base_inputs,\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n range_spec=RangeSpec(min=0, max=128000),\n ),\n DictInput(\n name=\"model_kwargs\",\n display_name=\"Model Kwargs\",\n advanced=True,\n info=\"Additional keyword arguments to pass to the model.\",\n ),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[1],\n combobox=True,\n real_time_refresh=True,\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. \"\n \"Defaults to https://api.openai.com/v1. \"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n required=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n show=True,\n ),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n IntInput(\n name=\"max_retries\",\n display_name=\"Max Retries\",\n info=\"The maximum number of retries to make when generating.\",\n advanced=True,\n value=5,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"The timeout for requests to OpenAI completion API.\",\n advanced=True,\n value=700,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n parameters = {\n \"api_key\": SecretStr(self.api_key).get_secret_value() if self.api_key else None,\n \"model_name\": self.model_name,\n \"max_tokens\": self.max_tokens or None,\n \"model_kwargs\": self.model_kwargs or {},\n \"base_url\": self.openai_api_base or \"https://api.openai.com/v1\",\n \"seed\": self.seed,\n \"max_retries\": self.max_retries,\n \"timeout\": self.timeout,\n \"temperature\": self.temperature if self.temperature is not None else 0.1,\n }\n\n logger.info(f\"Model name: {self.model_name}\")\n if self.model_name in OPENAI_REASONING_MODEL_NAMES:\n logger.info(\"Getting reasoning model parameters\")\n parameters.pop(\"temperature\")\n parameters.pop(\"seed\")\n output = ChatOpenAI(**parameters)\n if self.json_mode:\n output = output.bind(response_format={\"type\": \"json_object\"})\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"Get a message from an OpenAI exception.\n\n Args:\n e (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n try:\n from openai import BadRequestError\n except ImportError:\n return None\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\")\n if message:\n return message\n return None\n\n def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_REASONING_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = False\n build_config[\"seed\"][\"show\"] = False\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = True\n build_config[\"seed\"][\"show\"] = True\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "json_mode": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "JSON Mode", + "dynamic": false, + "info": "If True, it will output JSON regardless of passing a schema.", + "list": false, + "list_add_label": "Add More", + "name": "json_mode", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "max_retries": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Retries", + "dynamic": false, + "info": "The maximum number of retries to make when generating.", + "list": false, + "list_add_label": "Add More", + "name": "max_retries", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 5 + }, + "max_tokens": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "list": false, + "list_add_label": "Add More", + "name": "max_tokens", + "placeholder": "", + "range_spec": { + "max": 128000, + "min": 0, + "step": 0.1, + "step_type": "float" + }, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": "" + }, + "model_kwargs": { + "_input_type": "DictInput", + "advanced": true, + "display_name": "Model Kwargs", + "dynamic": false, + "info": "Additional keyword arguments to pass to the model.", + "list": false, + "list_add_label": "Add More", + "name": "model_kwargs", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "dict", + "value": {} + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": true, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo", + "o1" + ], + "options_metadata": [], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o" + }, + "openai_api_base": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "OpenAI API Base", + "dynamic": false, + "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "openai_api_base", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "seed": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Seed", + "dynamic": false, + "info": "The seed controls the reproducibility of the job.", + "list": false, + "list_add_label": "Add More", + "name": "seed", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 1 + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Stream the response from the model. Streaming works only in Chat.", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "System message to pass to the model.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": false, + "display_name": "Temperature", + "dynamic": false, + "info": "", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + }, + "timeout": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Timeout", + "dynamic": false, + "info": "The timeout for requests to OpenAI completion API.", + "list": false, + "list_add_label": "Add More", + "name": "timeout", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 700 + } + }, + "tool_mode": false + }, + "type": "OpenAIModel" + }, + "dragging": false, + "height": 433, + "id": "OpenAIModel-gIFGV", + "measured": { + "height": 433, + "width": 320 + }, + "position": { + "x": 1718.9773974162958, + "y": 603.4642741725065 + }, + "positionAbsolute": { + "x": 1718.9773974162958, + "y": 603.4642741725065 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "Memory-4Jm4h", + "node": { + "base_classes": [ + "DataFrame" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Stores or retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Message History", + "documentation": "", + "edited": false, + "field_order": [ + "mode", + "message", + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template" + ], + "frozen": false, + "icon": "message-square-more", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Messages", + "group_outputs": false, + "hidden": null, + "method": "retrieve_messages_dataframe", + "name": "dataframe", + "options": null, + "required_inputs": null, + "selected": "DataFrame", + "tool_mode": true, + "types": [ + "DataFrame" + ], + "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 typing import Any, cast\n\nfrom langflow.custom import Component\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema import Data, dotdict\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.component_utils import set_current_fields, set_field_display\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\n description = \"Stores or retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n default_keys = [\"mode\", \"memory\"]\n mode_config = {\n \"Store\": [\"message\", \"memory\", \"sender\", \"sender_name\", \"session_id\"],\n \"Retrieve\": [\"n_messages\", \"order\", \"template\", \"memory\"],\n }\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Retrieve\", \"Store\"],\n value=\"Retrieve\",\n info=\"Operation mode: Store messages or Retrieve messages.\",\n real_time_refresh=True,\n ),\n MessageTextInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The chat message to be stored.\",\n tool_mode=True,\n dynamic=True,\n show=False,\n ),\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"Memory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n show=False,\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 show=False,\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 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 tool_mode=True,\n required=True,\n show=False,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n show=False,\n ),\n ]\n\n outputs = [Output(display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True)]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"mode\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n if field_value == \"Store\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Stored Messages\",\n name=\"stored_messages\",\n method=\"store_message\",\n hidden=True,\n dynamic=True,\n )\n ]\n if field_value == \"Retrieve\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n )\n ]\n return frontend_node\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n\n async def store_message(self) -> Message:\n message = Message(text=self.message) if isinstance(self.message, str) else self.message\n\n message.session_id = self.session_id or message.session_id\n message.sender = self.sender or message.sender or MESSAGE_SENDER_AI\n message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI\n\n stored_messages: list[Message] = []\n\n if self.memory:\n self.memory.session_id = message.session_id\n lc_message = message.to_lc_message()\n await self.memory.aadd_messages([lc_message])\n\n stored_messages = await self.memory.aget_messages() or []\n\n stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else []\n\n if message.sender:\n stored_messages = [m for m in stored_messages if m.sender == message.sender]\n else:\n await astore_message(message, flow_id=self.graph.flow_id)\n stored_messages = (\n await aget_messages(\n session_id=message.session_id, sender_name=message.sender_name, sender=message.sender\n )\n or []\n )\n\n if not stored_messages:\n msg = \"No messages were stored. Please ensure that the session ID and sender are properly set.\"\n raise ValueError(msg)\n\n stored_message = stored_messages[0]\n self.status = stored_message\n return stored_message\n\n def update_build_config(\n self,\n build_config: dotdict,\n field_value: Any, # noqa: ARG002\n field_name: str | None = None, # noqa: ARG002\n ) -> dotdict:\n return set_current_fields(\n build_config=build_config,\n action_fields=self.mode_config,\n selected_action=build_config[\"mode\"][\"value\"],\n default_fields=self.default_keys,\n func=set_field_display,\n )\n" + }, + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": [ + "Memory" + ], + "list": false, + "list_add_label": "Add More", + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "message": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Message", + "dynamic": true, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Retrieve" + }, + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "list": false, + "list_add_label": "Add More", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "options_metadata": [], + "placeholder": "", + "required": true, + "show": false, + "title_case": false, + "toggle": false, + "tool_mode": true, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "template": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + } + }, + "tool_mode": false + }, + "type": "Memory" + }, + "dragging": false, + "height": 244, + "id": "Memory-4Jm4h", + "measured": { + "height": 244, + "width": 320 + }, + "position": { + "x": 1240.7186213296432, + "y": 1059.5754404393747 + }, + "positionAbsolute": { + "x": 1240.7186213296432, + "y": 1059.5754404393747 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "Prompt-S7cEu", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": { + "template": [ + "CURRENT_DATE" + ] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "field_order": [ + "template", + "tool_placeholder" + ], + "frozen": false, + "icon": "braces", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Prompt", + "group_outputs": false, + "hidden": null, + "method": "build_prompt", + "name": "prompt", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "CURRENT_DATE": { + "advanced": false, + "display_name": "CURRENT_DATE", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "CURRENT_DATE", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "_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.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n" + }, + "template": { + "_input_type": "PromptInput", + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "prompt", + "value": "\nYou are a Notion Agent, an AI assistant designed to help users interact with their Notion workspace. Your role is to understand user requests, utilize the appropriate Notion tools to fulfill these requests, and communicate clearly with the user throughout the process.\n\nGeneral Guidelines:\n\n1. Carefully analyze each user request to determine which tool(s) you need to use.\n\n2. Before using any tool, ensure you have all the necessary information. If you need more details, ask the user clear and concise questions.\n\n3. When using a tool, provide a brief explanation to the user about what you're doing and why.\n\n4. After using a tool, interpret the results for the user in a clear, concise manner.\n\n5. If a task requires multiple steps, outline your plan to the user before proceeding.\n\n6. If you encounter an error or limitation, explain it to the user and suggest possible solutions or alternative approaches.\n\n7. Always maintain a helpful and professional tone in your interactions.\n\n8. Be proactive in offering suggestions or alternatives if the user's initial request can't be fulfilled exactly as stated.\n\n9. When providing information or results, focus on relevance and clarity. Summarize when necessary, but provide details when they're important.\n\n10. If a user's request is unclear or could be interpreted in multiple ways, ask for clarification before proceeding.\n\n11. After completing a task, summarize what was accomplished and suggest any relevant next steps or additional actions the user might want to take.\n\n12. If a user asks about capabilities you don't have or tools you can't access, clearly explain your limitations and suggest alternative ways to assist if possible.\n\nRemember, your primary goal is to assist the user effectively with their Notion-related tasks using the provided tools. Always strive for clarity, accuracy, and helpfulness in your interactions. Adapt your communication style to the user's level of technical understanding and familiarity with Notion.\n\nNow, you're ready to assist the user\n\nToday is: {CURRENT_DATE}\n" + }, + "tool_placeholder": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Tool Placeholder", + "dynamic": false, + "info": "A placeholder input for tool mode.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "tool_placeholder", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "type": "Prompt" + }, + "dragging": false, + "height": 416, + "id": "Prompt-S7cEu", + "measured": { + "height": 416, + "width": 320 + }, + "position": { + "x": 1227.4862876736101, + "y": 616.3826667128244 + }, + "positionAbsolute": { + "x": 1227.4862876736101, + "y": 616.3826667128244 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "CurrentDateComponent-mEpAU", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Returns the current date and time in the selected timezone.", + "display_name": "Current Date", + "documentation": "", + "edited": true, + "field_order": [ + "timezone" + ], + "frozen": false, + "icon": "clock", + "lf_version": "1.0.17", + "official": false, + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Current Date", + "method": "get_current_date", + "name": "current_date", + "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 datetime import datetime\r\nfrom zoneinfo import ZoneInfo\r\nfrom typing import List\r\n\r\nfrom langflow.custom import Component\r\nfrom langflow.io import DropdownInput, Output\r\nfrom langflow.schema.message import Message\r\n\r\nclass CurrentDateComponent(Component):\r\n display_name = \"Current Date 🕰️\"\r\n description = \"Returns the current date and time in the selected timezone.\"\r\n icon = \"clock\"\r\n\r\n inputs = [\r\n DropdownInput(\r\n name=\"timezone\",\r\n display_name=\"Timezone\",\r\n options=[\r\n \"UTC\",\r\n \"US/Eastern\",\r\n \"US/Central\",\r\n \"US/Mountain\",\r\n \"US/Pacific\",\r\n \"Europe/London\",\r\n \"Europe/Paris\",\r\n \"Asia/Tokyo\",\r\n \"Australia/Sydney\",\r\n \"America/Sao_Paulo\",\r\n \"America/Cuiaba\",\r\n ],\r\n value=\"UTC\",\r\n info=\"Select the timezone for the current date and time.\",\r\n ),\r\n ]\r\n\r\n outputs = [\r\n Output(display_name=\"Current Date\", name=\"current_date\", method=\"get_current_date\"),\r\n ]\r\n\r\n def get_current_date(self) -> Message:\r\n try:\r\n tz = ZoneInfo(self.timezone)\r\n current_date = datetime.now(tz).strftime(\"%Y-%m-%d %H:%M:%S %Z\")\r\n result = f\"Current date and time in {self.timezone}: {current_date}\"\r\n self.status = result\r\n return Message(text=result)\r\n except Exception as e:\r\n error_message = f\"Error: {str(e)}\"\r\n self.status = error_message\r\n return Message(text=error_message)" + }, + "timezone": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "display_name": "Timezone", + "dynamic": false, + "info": "Select the timezone for the current date and time.", + "name": "timezone", + "options": [ + "UTC", + "US/Eastern", + "US/Central", + "US/Mountain", + "US/Pacific", + "Europe/London", + "Europe/Paris", + "Asia/Tokyo", + "Australia/Sydney", + "America/Sao_Paulo", + "America/Cuiaba" + ], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "UTC" + } + } + }, + "showNode": false, + "type": "CurrentDateComponent" + }, + "dragging": false, + "height": 96, + "id": "CurrentDateComponent-mEpAU", + "measured": { + "height": 96, + "width": 192 + }, + "position": { + "x": 1092.5108512311297, + "y": 868.3249850335523 + }, + "positionAbsolute": { + "x": 1092.5108512311297, + "y": 868.3249850335523 + }, + "selected": false, + "type": "genericNode", + "width": 96 + }, + { + "data": { + "id": "TypeConverterComponent-I2ppC", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "processing", + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", + "documentation": "", + "edited": false, + "field_order": [ + "input_data", + "output_type" + ], + "frozen": false, + "icon": "repeat", + "key": "TypeConverterComponent", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message Output", + "group_outputs": false, + "method": "convert_to_message", + "name": "message_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.007568328950209746, + "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 typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import HandleInput, Output, TabInput\nfrom langflow.schema import Data, DataFrame, Message\n\n\ndef convert_to_message(v) -> Message:\n \"\"\"Convert input to Message type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Message: Converted Message object\n \"\"\"\n return v if isinstance(v, Message) else v.to_message()\n\n\ndef convert_to_data(v: DataFrame | Data | Message | dict) -> Data:\n \"\"\"Convert input to Data type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Data: Converted Data object\n \"\"\"\n if isinstance(v, dict):\n return Data(v)\n return v if isinstance(v, Data) else v.to_data()\n\n\ndef convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n DataFrame: Converted DataFrame object\n \"\"\"\n if isinstance(v, dict):\n return DataFrame([v])\n return v if isinstance(v, DataFrame) else v.to_dataframe()\n\n\nclass TypeConverterComponent(Component):\n display_name = \"Type Convert\"\n description = \"Convert between different types (Message, Data, DataFrame)\"\n icon = \"repeat\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Input\",\n input_types=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Accept Message, Data or DataFrame as input\",\n required=True,\n ),\n TabInput(\n name=\"output_type\",\n display_name=\"Output Type\",\n options=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Select the desired output data type\",\n real_time_refresh=True,\n value=\"Message\",\n ),\n ]\n\n outputs = [Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\")]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"output_type\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n\n # Add only the selected output type\n if field_value == \"Message\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\").to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Data Output\", name=\"data_output\", method=\"convert_to_data\").to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\", name=\"dataframe_output\", method=\"convert_to_dataframe\"\n ).to_dict()\n )\n\n return frontend_node\n\n def convert_to_message(self) -> Message:\n \"\"\"Convert input to Message type.\"\"\"\n return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n" + }, + "input_data": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "Accept Message, Data or DataFrame as input", + "input_types": [ + "Message", + "Data", + "DataFrame" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_data", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "output_type": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Output Type", + "dynamic": false, + "info": "Select the desired output data type", + "name": "output_type", + "options": [ + "Message", + "Data", + "DataFrame" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Message" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "TypeConverterComponent" + }, + "id": "TypeConverterComponent-I2ppC", + "measured": { + "height": 261, + "width": 320 + }, + "position": { + "x": 1600.3037160700787, + "y": 1139.5045362063886 + }, + "selected": true, + "type": "genericNode" + } + ], + "viewport": { + "x": -401.8206379384965, + "y": -136.05236574544563, + "zoom": 0.5897759459412302 + } + }, + "description": "This flow creates an AI assistant that interacts with your Notion workspace. It understands natural language requests, performs actions in Notion (like creating pages or searching for information), and provides helpful responses. To use it, simply start a conversation by asking the agent to perform a Notion-related task, and it will guide you through the process, making it easy to manage your Notion workspace through chat.", + "endpoint_name": null, + "id": "c6c53b77-f2f5-46bb-b481-80de2055deef", + "is_component": false, + "last_tested_version": "1.4.2", + "name": "Conversational Notion Agent", + "tags": [] } \ No newline at end of file diff --git a/src/backend/base/langflow/components/helpers/memory.py b/src/backend/base/langflow/components/helpers/memory.py index 5001eda53..1a223e5de 100644 --- a/src/backend/base/langflow/components/helpers/memory.py +++ b/src/backend/base/langflow/components/helpers/memory.py @@ -1,28 +1,50 @@ -from typing import cast +from typing import Any, cast 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 aget_messages -from langflow.schema import Data +from langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput +from langflow.memory import aget_messages, astore_message +from langflow.schema import Data, dotdict from langflow.schema.dataframe import DataFrame from langflow.schema.message import Message -from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER +from langflow.utils.component_utils import set_current_fields, set_field_display +from langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER class MemoryComponent(Component): display_name = "Message History" - description = "Retrieves stored chat messages from Langflow tables or an external memory." + description = "Stores or retrieves stored chat messages from Langflow tables or an external memory." icon = "message-square-more" name = "Memory" + default_keys = ["mode", "memory"] + mode_config = { + "Store": ["message", "memory", "sender", "sender_name", "session_id"], + "Retrieve": ["n_messages", "order", "template", "memory"], + } inputs = [ + TabInput( + name="mode", + display_name="Mode", + options=["Retrieve", "Store"], + value="Retrieve", + info="Operation mode: Store messages or Retrieve messages.", + real_time_refresh=True, + ), + MessageTextInput( + name="message", + display_name="Message", + info="The chat message to be stored.", + tool_mode=True, + dynamic=True, + show=False, + ), HandleInput( name="memory", display_name="External Memory", input_types=["Memory"], info="Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + advanced=True, ), DropdownInput( name="sender", @@ -37,6 +59,7 @@ class MemoryComponent(Component): display_name="Sender Name", info="Filter by sender name.", advanced=True, + show=False, ), IntInput( name="n_messages", @@ -44,6 +67,7 @@ class MemoryComponent(Component): value=100, info="Number of messages to retrieve.", advanced=True, + show=False, ), MessageTextInput( name="session_id", @@ -59,6 +83,8 @@ class MemoryComponent(Component): info="Order of the messages.", advanced=True, tool_mode=True, + required=True, + show=False, ), MultilineInput( name="template", @@ -67,14 +93,34 @@ class MemoryComponent(Component): "It can contain the keys {text}, {sender} or any other key in the message data.", value="{sender_name}: {text}", advanced=True, + show=False, ), ] - outputs = [ - Output(display_name="Data", name="messages", method="retrieve_messages"), - Output(display_name="Message", name="messages_text", method="retrieve_messages_as_text"), - Output(display_name="DataFrame", name="dataframe", method="as_dataframe"), - ] + outputs = [Output(display_name="Messages", name="dataframe", method="retrieve_messages_dataframe", dynamic=True)] + + def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict: + """Dynamically show only the relevant output based on the selected output type.""" + if field_name == "mode": + # Start with empty outputs + frontend_node["outputs"] = [] + if field_value == "Store": + frontend_node["outputs"] = [ + Output( + display_name="Stored Messages", + name="stored_messages", + method="store_message", + hidden=True, + dynamic=True, + ) + ] + if field_value == "Retrieve": + frontend_node["outputs"] = [ + Output( + display_name="Messages", name="dataframe", method="retrieve_messages_dataframe", dynamic=True + ) + ] + return frontend_node async def retrieve_messages(self) -> Data: sender = self.sender @@ -118,12 +164,7 @@ class MemoryComponent(Component): self.status = stored return cast(Data, stored) - async def retrieve_messages_as_text(self) -> Message: - stored_text = data_to_text(self.template, await self.retrieve_messages()) - self.status = stored_text - return Message(text=stored_text) - - async def as_dataframe(self) -> DataFrame: + async def retrieve_messages_dataframe(self) -> DataFrame: """Convert the retrieved messages into a DataFrame. Returns: @@ -131,3 +172,54 @@ class MemoryComponent(Component): """ messages = await self.retrieve_messages() return DataFrame(messages) + + async def store_message(self) -> Message: + message = Message(text=self.message) if isinstance(self.message, str) else self.message + + message.session_id = self.session_id or message.session_id + message.sender = self.sender or message.sender or MESSAGE_SENDER_AI + message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI + + stored_messages: list[Message] = [] + + if self.memory: + self.memory.session_id = message.session_id + lc_message = message.to_lc_message() + await self.memory.aadd_messages([lc_message]) + + stored_messages = await self.memory.aget_messages() or [] + + stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else [] + + if message.sender: + stored_messages = [m for m in stored_messages if m.sender == message.sender] + else: + await astore_message(message, flow_id=self.graph.flow_id) + stored_messages = ( + await aget_messages( + session_id=message.session_id, sender_name=message.sender_name, sender=message.sender + ) + or [] + ) + + if not stored_messages: + msg = "No messages were stored. Please ensure that the session ID and sender are properly set." + raise ValueError(msg) + + stored_message = stored_messages[0] + self.status = stored_message + return stored_message + + def update_build_config( + self, + build_config: dotdict, + field_value: Any, # noqa: ARG002 + field_name: str | None = None, # noqa: ARG002 + ) -> dotdict: + return set_current_fields( + build_config=build_config, + action_fields=self.mode_config, + selected_action=build_config["mode"]["value"], + default_fields=self.default_keys, + func=set_field_display, + ) diff --git a/src/backend/base/langflow/components/helpers/store_message.py b/src/backend/base/langflow/components/helpers/store_message.py index 687093219..40c4dc574 100644 --- a/src/backend/base/langflow/components/helpers/store_message.py +++ b/src/backend/base/langflow/components/helpers/store_message.py @@ -12,6 +12,7 @@ class MessageStoreComponent(Component): description = "Stores a chat message or text into Langflow tables or an external memory." icon = "message-square-text" name = "StoreMessage" + legacy = True inputs = [ MessageTextInput( diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json b/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json index 96f580283..22e4f2072 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json @@ -1,42 +1,13 @@ { "data": { "edges": [ - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-80v92", - "name": "messages_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "CHAT_HISTORY", - "id": "Prompt-TBdwC", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Memory-80v92{œdataTypeœ:œMemoryœ,œidœ:œMemory-80v92œ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-TBdwC{œfieldNameœ:œCHAT_HISTORYœ,œidœ:œPrompt-TBdwCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "Memory-80v92", - "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-80v92œ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-TBdwC", - "targetHandle": "{œfieldNameœ: œCHAT_HISTORYœ, œidœ: œPrompt-TBdwCœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "URL", - "id": "URL-VNBsH", + "id": "URL-YeUZT", "name": "text", "output_types": [ "Message" @@ -44,7 +15,7 @@ }, "targetHandle": { "fieldName": "EXAMPLE_COMPONENTS", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "inputTypes": [ "Message", "Text" @@ -52,12 +23,12 @@ "type": "str" } }, - "id": "reactflow__edge-URL-VNBsH{œdataTypeœ:œURLœ,œidœ:œURL-VNBsHœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-TBdwC{œfieldNameœ:œEXAMPLE_COMPONENTSœ,œidœ:œPrompt-TBdwCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-URL-YeUZT{œdataTypeœ:œURLœ,œidœ:œURL-YeUZTœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-Ap5W5{œfieldNameœ:œEXAMPLE_COMPONENTSœ,œidœ:œPrompt-Ap5W5œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "URL-VNBsH", - "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-VNBsHœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-TBdwC", - "targetHandle": "{œfieldNameœ: œEXAMPLE_COMPONENTSœ, œidœ: œPrompt-TBdwCœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "URL-YeUZT", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-YeUZTœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Ap5W5", + "targetHandle": "{œfieldNameœ: œEXAMPLE_COMPONENTSœ, œidœ: œPrompt-Ap5W5œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -65,7 +36,7 @@ "data": { "sourceHandle": { "dataType": "URL", - "id": "URL-7lpSF", + "id": "URL-57sgJ", "name": "text", "output_types": [ "Message" @@ -73,7 +44,7 @@ }, "targetHandle": { "fieldName": "BASE_COMPONENT_CODE", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "inputTypes": [ "Message", "Text" @@ -81,12 +52,12 @@ "type": "str" } }, - "id": "reactflow__edge-URL-7lpSF{œdataTypeœ:œURLœ,œidœ:œURL-7lpSFœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-TBdwC{œfieldNameœ:œBASE_COMPONENT_CODEœ,œidœ:œPrompt-TBdwCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-URL-57sgJ{œdataTypeœ:œURLœ,œidœ:œURL-57sgJœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-Ap5W5{œfieldNameœ:œBASE_COMPONENT_CODEœ,œidœ:œPrompt-Ap5W5œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "URL-7lpSF", - "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-7lpSFœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-TBdwC", - "targetHandle": "{œfieldNameœ: œBASE_COMPONENT_CODEœ, œidœ: œPrompt-TBdwCœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "URL-57sgJ", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-57sgJœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Ap5W5", + "targetHandle": "{œfieldNameœ: œBASE_COMPONENT_CODEœ, œidœ: œPrompt-Ap5W5œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -94,7 +65,7 @@ "data": { "sourceHandle": { "dataType": "URL", - "id": "URL-V6Rnb", + "id": "URL-yrON5", "name": "text", "output_types": [ "Message" @@ -102,7 +73,7 @@ }, "targetHandle": { "fieldName": "CUSTOM_COMPONENT_CODE", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "inputTypes": [ "Message", "Text" @@ -110,12 +81,12 @@ "type": "str" } }, - "id": "reactflow__edge-URL-V6Rnb{œdataTypeœ:œURLœ,œidœ:œURL-V6Rnbœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-TBdwC{œfieldNameœ:œCUSTOM_COMPONENT_CODEœ,œidœ:œPrompt-TBdwCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-URL-yrON5{œdataTypeœ:œURLœ,œidœ:œURL-yrON5œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-Ap5W5{œfieldNameœ:œCUSTOM_COMPONENT_CODEœ,œidœ:œPrompt-Ap5W5œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "URL-V6Rnb", - "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-V6Rnbœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-TBdwC", - "targetHandle": "{œfieldNameœ: œCUSTOM_COMPONENT_CODEœ, œidœ: œPrompt-TBdwCœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "URL-yrON5", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-yrON5œ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Ap5W5", + "targetHandle": "{œfieldNameœ: œCUSTOM_COMPONENT_CODEœ, œidœ: œPrompt-Ap5W5œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -123,7 +94,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "name": "prompt", "output_types": [ "Message" @@ -131,19 +102,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "AnthropicModel-Ud197", + "id": "AnthropicModel-I7I40", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-TBdwC{œdataTypeœ:œPromptœ,œidœ:œPrompt-TBdwCœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-Ud197{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-Ud197œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-Ap5W5{œdataTypeœ:œPromptœ,œidœ:œPrompt-Ap5W5œ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-I7I40{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-I7I40œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-TBdwC", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-TBdwCœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "AnthropicModel-Ud197", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-Ud197œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-Ap5W5", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Ap5W5œ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "AnthropicModel-I7I40", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-I7I40œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -151,7 +122,7 @@ "data": { "sourceHandle": { "dataType": "AnthropicModel", - "id": "AnthropicModel-Ud197", + "id": "AnthropicModel-I7I40", "name": "text_output", "output_types": [ "Message" @@ -159,7 +130,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-lY6GY", + "id": "ChatOutput-Z5a3C", "inputTypes": [ "Data", "DataFrame", @@ -168,18 +139,20 @@ "type": "other" } }, - "id": "reactflow__edge-AnthropicModel-Ud197{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-Ud197œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-lY6GY{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-lY6GYœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AnthropicModel-I7I40{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-I7I40œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Z5a3C{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Z5a3Cœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "AnthropicModel-Ud197", - "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-Ud197œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-lY6GY", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-lY6GYœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "AnthropicModel-I7I40", + "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-I7I40œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-Z5a3C", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Z5a3Cœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" }, { + "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-1jL7Z", + "id": "ChatInput-gwwtq", "name": "message", "output_types": [ "Message" @@ -187,7 +160,7 @@ }, "targetHandle": { "fieldName": "USER_INPUT", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "inputTypes": [ "Message", "Text" @@ -195,11 +168,65 @@ "type": "str" } }, - "id": "xy-edge__ChatInput-1jL7Z{œdataTypeœ:œChatInputœ,œidœ:œChatInput-1jL7Zœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-TBdwC{œfieldNameœ:œUSER_INPUTœ,œidœ:œPrompt-TBdwCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-1jL7Z", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-1jL7Zœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-TBdwC", - "targetHandle": "{œfieldNameœ: œUSER_INPUTœ, œidœ: œPrompt-TBdwCœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-ChatInput-gwwtq{œdataTypeœ:œChatInputœ,œidœ:œChatInput-gwwtqœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-Ap5W5{œfieldNameœ:œUSER_INPUTœ,œidœ:œPrompt-Ap5W5œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-gwwtq", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-gwwtqœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Ap5W5", + "targetHandle": "{œfieldNameœ: œUSER_INPUTœ, œidœ: œPrompt-Ap5W5œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-Ftk3s", + "name": "dataframe", + "output_types": [ + "DataFrame" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "TypeConverterComponent-Vb89B", + "inputTypes": [ + "Message", + "Data", + "DataFrame" + ], + "type": "other" + } + }, + "id": "xy-edge__Memory-Ftk3s{œdataTypeœ:œMemoryœ,œidœ:œMemory-Ftk3sœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-Vb89B{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-Vb89Bœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "source": "Memory-Ftk3s", + "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-Ftk3sœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "TypeConverterComponent-Vb89B", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-Vb89Bœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-Vb89B", + "name": "message_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "CHAT_HISTORY", + "id": "Prompt-Ap5W5", + "inputTypes": [ + "Message", + "Text" + ], + "type": "str" + } + }, + "id": "xy-edge__TypeConverterComponent-Vb89B{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-Vb89Bœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-Ap5W5{œfieldNameœ:œCHAT_HISTORYœ,œidœ:œPrompt-Ap5W5œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "TypeConverterComponent-Vb89B", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-Vb89Bœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Ap5W5", + "targetHandle": "{œfieldNameœ: œCHAT_HISTORYœ, œidœ: œPrompt-Ap5W5œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -207,7 +234,7 @@ "data": { "description": "Retrieves stored chat messages from Langflow tables or an external memory.", "display_name": "Chat Memory", - "id": "Memory-80v92", + "id": "Memory-Ftk3s", "node": { "base_classes": [ "Data", @@ -216,7 +243,7 @@ "beta": false, "conditional_paths": [], "custom_fields": {}, - "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "description": "Stores or retrieves stored chat messages from Langflow tables or an external memory.", "display_name": "Chat Memory", "documentation": "", "edited": false, @@ -239,37 +266,9 @@ { "allows_loop": false, "cache": true, - "display_name": "Data", + "display_name": "Messages", "group_outputs": false, - "method": "retrieve_messages", - "name": "messages", - "selected": "Data", - "tool_mode": true, - "types": [ - "Data" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Message", - "group_outputs": false, - "method": "retrieve_messages_as_text", - "name": "messages_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "DataFrame", - "group_outputs": false, - "method": "as_dataframe", + "method": "retrieve_messages_dataframe", "name": "dataframe", "selected": "DataFrame", "tool_mode": true, @@ -298,11 +297,11 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import cast\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 aget_messages\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\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=[\"Memory\"],\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=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\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=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\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 tool_mode=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"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=\"Data\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Message\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, await self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n async def as_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n" + "value": "from typing import Any, cast\n\nfrom langflow.custom import Component\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema import Data, dotdict\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.component_utils import set_current_fields, set_field_display\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\n description = \"Stores or retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n default_keys = [\"mode\", \"memory\"]\n mode_config = {\n \"Store\": [\"message\", \"memory\", \"sender\", \"sender_name\", \"session_id\"],\n \"Retrieve\": [\"n_messages\", \"order\", \"template\", \"memory\"],\n }\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Retrieve\", \"Store\"],\n value=\"Retrieve\",\n info=\"Operation mode: Store messages or Retrieve messages.\",\n real_time_refresh=True,\n ),\n MessageTextInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The chat message to be stored.\",\n tool_mode=True,\n dynamic=True,\n show=False,\n ),\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"Memory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n show=False,\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 show=False,\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 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 tool_mode=True,\n required=True,\n show=False,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n show=False,\n ),\n ]\n\n outputs = [Output(display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True)]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"mode\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n if field_value == \"Store\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Stored Messages\",\n name=\"stored_messages\",\n method=\"store_message\",\n hidden=True,\n dynamic=True,\n )\n ]\n if field_value == \"Retrieve\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n )\n ]\n return frontend_node\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n\n async def store_message(self) -> Message:\n message = Message(text=self.message) if isinstance(self.message, str) else self.message\n\n message.session_id = self.session_id or message.session_id\n message.sender = self.sender or message.sender or MESSAGE_SENDER_AI\n message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI\n\n stored_messages: list[Message] = []\n\n if self.memory:\n self.memory.session_id = message.session_id\n lc_message = message.to_lc_message()\n await self.memory.aadd_messages([lc_message])\n\n stored_messages = await self.memory.aget_messages() or []\n\n stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else []\n\n if message.sender:\n stored_messages = [m for m in stored_messages if m.sender == message.sender]\n else:\n await astore_message(message, flow_id=self.graph.flow_id)\n stored_messages = (\n await aget_messages(\n session_id=message.session_id, sender_name=message.sender_name, sender=message.sender\n )\n or []\n )\n\n if not stored_messages:\n msg = \"No messages were stored. Please ensure that the session ID and sender are properly set.\"\n raise ValueError(msg)\n\n stored_message = stored_messages[0]\n self.status = stored_message\n return stored_message\n\n def update_build_config(\n self,\n build_config: dotdict,\n field_value: Any, # noqa: ARG002\n field_name: str | None = None, # noqa: ARG002\n ) -> dotdict:\n return set_current_fields(\n build_config=build_config,\n action_fields=self.mode_config,\n selected_action=build_config[\"mode\"][\"value\"],\n default_fields=self.default_keys,\n func=set_field_display,\n )\n" }, "memory": { "_input_type": "HandleInput", - "advanced": false, + "advanced": true, "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", @@ -319,6 +318,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Message", + "dynamic": true, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Retrieve" + }, "n_messages": { "_input_type": "IntInput", "advanced": true, @@ -348,7 +391,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, @@ -452,10 +495,10 @@ }, "dragging": false, "height": 262, - "id": "Memory-80v92", + "id": "Memory-Ftk3s", "measured": { "height": 262, - "width": 360 + "width": 320 }, "position": { "x": 1832.6206210738642, @@ -473,7 +516,7 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "node": { "base_classes": [ "Message" @@ -701,10 +744,10 @@ }, "dragging": false, "height": 685, - "id": "Prompt-TBdwC", + "id": "Prompt-Ap5W5", "measured": { "height": 685, - "width": 360 + "width": 320 }, "position": { "x": 2214.0288118788944, @@ -720,7 +763,7 @@ }, { "data": { - "id": "note-sVpiV", + "id": "note-Wm994", "node": { "description": "# Fetch Components code \n\nUsing the URL component we are extracting from Github, the code from a few classes to provide as example to the LLM. \n\nThis ensures we are always up to date with recent information from the codebase.", "display_name": "", @@ -731,10 +774,10 @@ }, "dragging": false, "height": 324, - "id": "note-sVpiV", + "id": "note-Wm994", "measured": { "height": 324, - "width": 328 + "width": 325 }, "position": { "x": 1428.2696830085167, @@ -750,7 +793,7 @@ }, { "data": { - "id": "note-56K9d", + "id": "note-laDOf", "node": { "description": "# 🛠️ Custom Component Generator 🚀\n\nHi! I'm here to help you create custom components for Langflow. Think of me as your technical partner who can help turn your ideas into working components! \n\n## 🎯 How to Work With Me\n\n### 1. 💭 Tell Me What You Want to Build\nSimply describe what you want your component to do in plain English. For example:\n- \"I need a component that sends Slack messages\"\n- \"I want to create a tool that can process CSV files\"\n- \"I need something that can translate text\"\n\n### 2. 📚 Share Any Relevant Information\nIf you're working with a specific:\n- 🔑 API or service (just share the documentation link or main endpoints)\n- 📄 File format\n- 🔄 Data structure\n- 🔧 Existing component you want to modify\n\n### 3. 🎨 Let Me Help Design It\nI'll help by:\n- 📊 Breaking down complex requirements into manageable pieces\n- 💡 Suggesting the best way to structure inputs and outputs\n- ⚙️ Creating the component code\n- 📝 Explaining how to use it\n\n### 4. 🔄 Iterative Refinement\nWe can then:\n- ✅ Test and refine the component\n- ⭐ Add features\n- 🔧 Modify behavior\n- 🛡️ Improve error handling\n- 📖 Add documentation\n\n## 🚀 What I Can Help With\n\nI can help create components that:\n- 📊 Process different file types (CSV, JSON, Excel, etc.)\n- 🔌 Integrate with external APIs\n- 🔄 Transform data\n- 🔀 Route messages\n- 🌐 Handle web requests\n- 🎯 Parse structured data\n- ✨ And much more!\n\n## 💡 Tips for Best Results\n\n1. **Be Specific** 🎯: The more details you provide about what you want to accomplish, the better I can help.\n\n2. **Share Examples** 📋: If you have example data or specific use cases, share them.\n\n3. **Ask Questions** ❓: Don't hesitate to ask for clarification or modifications.\n\nJust start by telling me what kind of component you'd like to create, and I'll guide you through the process! \n\nReady to build something awesome? 🚀 Let's get started!", "display_name": "", @@ -761,10 +804,10 @@ }, "dragging": false, "height": 573, - "id": "note-56K9d", + "id": "note-laDOf", "measured": { "height": 573, - "width": 623 + "width": 620 }, "position": { "x": 1002.7334076860311, @@ -785,7 +828,7 @@ }, { "data": { - "id": "URL-7lpSF", + "id": "URL-57sgJ", "node": { "base_classes": [ "Data", @@ -960,10 +1003,10 @@ }, "dragging": false, "height": 365, - "id": "URL-7lpSF", + "id": "URL-57sgJ", "measured": { "height": 365, - "width": 360 + "width": 320 }, "position": { "x": 1428.8876734924713, @@ -979,7 +1022,7 @@ }, { "data": { - "id": "URL-VNBsH", + "id": "URL-YeUZT", "node": { "base_classes": [ "Data", @@ -1160,10 +1203,10 @@ }, "dragging": false, "height": 661, - "id": "URL-VNBsH", + "id": "URL-YeUZT", "measured": { "height": 661, - "width": 360 + "width": 320 }, "position": { "x": 1831.5895760156684, @@ -1179,7 +1222,7 @@ }, { "data": { - "id": "URL-V6Rnb", + "id": "URL-yrON5", "node": { "base_classes": [ "Data", @@ -1354,10 +1397,10 @@ }, "dragging": false, "height": 365, - "id": "URL-V6Rnb", + "id": "URL-yrON5", "measured": { "height": 365, - "width": 360 + "width": 320 }, "position": { "x": 1429.5084407373513, @@ -1373,7 +1416,7 @@ }, { "data": { - "id": "AnthropicModel-Ud197", + "id": "AnthropicModel-I7I40", "node": { "base_classes": [ "LanguageModel", @@ -1458,7 +1501,7 @@ "dynamic": false, "info": "Your Anthropic API key.", "input_types": [], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1467,7 +1510,7 @@ "show": true, "title_case": false, "type": "str", - "value": "ANTHROPIC_API_KEY" + "value": "" }, "base_url": { "_input_type": "MessageTextInput", @@ -1579,7 +1622,7 @@ "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "claude-3-5-sonnet-20241022" + "value": "claude-opus-4-20250514" }, "prefill": { "_input_type": "MessageTextInput", @@ -1700,10 +1743,10 @@ "type": "AnthropicModel" }, "dragging": false, - "id": "AnthropicModel-Ud197", + "id": "AnthropicModel-I7I40", "measured": { - "height": 750, - "width": 360 + "height": 507, + "width": 320 }, "position": { "x": 2585.5577736139826, @@ -1714,7 +1757,7 @@ }, { "data": { - "id": "ChatInput-1jL7Z", + "id": "ChatInput-gwwtq", "node": { "base_classes": [ "Message" @@ -2012,10 +2055,10 @@ "type": "ChatInput" }, "dragging": false, - "id": "ChatInput-1jL7Z", + "id": "ChatInput-gwwtq", "measured": { - "height": 74, - "width": 216 + "height": 48, + "width": 192 }, "position": { "x": 1444.5468159526824, @@ -2026,7 +2069,7 @@ }, { "data": { - "id": "ChatOutput-lY6GY", + "id": "ChatOutput-Z5a3C", "node": { "base_classes": [ "Message" @@ -2322,10 +2365,10 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-lY6GY", + "id": "ChatOutput-Z5a3C", "measured": { - "height": 74, - "width": 216 + "height": 48, + "width": 192 }, "position": { "x": 2978.7260774697843, @@ -2333,19 +2376,145 @@ }, "selected": false, "type": "genericNode" + }, + { + "data": { + "id": "TypeConverterComponent-Vb89B", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "processing", + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", + "documentation": "", + "edited": false, + "field_order": [ + "input_data", + "output_type" + ], + "frozen": false, + "icon": "repeat", + "key": "TypeConverterComponent", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message Output", + "group_outputs": false, + "method": "convert_to_message", + "name": "message_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.007568328950209746, + "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 typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import HandleInput, Output, TabInput\nfrom langflow.schema import Data, DataFrame, Message\n\n\ndef convert_to_message(v) -> Message:\n \"\"\"Convert input to Message type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Message: Converted Message object\n \"\"\"\n return v if isinstance(v, Message) else v.to_message()\n\n\ndef convert_to_data(v: DataFrame | Data | Message | dict) -> Data:\n \"\"\"Convert input to Data type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Data: Converted Data object\n \"\"\"\n if isinstance(v, dict):\n return Data(v)\n return v if isinstance(v, Data) else v.to_data()\n\n\ndef convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n DataFrame: Converted DataFrame object\n \"\"\"\n if isinstance(v, dict):\n return DataFrame([v])\n return v if isinstance(v, DataFrame) else v.to_dataframe()\n\n\nclass TypeConverterComponent(Component):\n display_name = \"Type Convert\"\n description = \"Convert between different types (Message, Data, DataFrame)\"\n icon = \"repeat\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Input\",\n input_types=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Accept Message, Data or DataFrame as input\",\n required=True,\n ),\n TabInput(\n name=\"output_type\",\n display_name=\"Output Type\",\n options=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Select the desired output data type\",\n real_time_refresh=True,\n value=\"Message\",\n ),\n ]\n\n outputs = [Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\")]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"output_type\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n\n # Add only the selected output type\n if field_value == \"Message\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\").to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Data Output\", name=\"data_output\", method=\"convert_to_data\").to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\", name=\"dataframe_output\", method=\"convert_to_dataframe\"\n ).to_dict()\n )\n\n return frontend_node\n\n def convert_to_message(self) -> Message:\n \"\"\"Convert input to Message type.\"\"\"\n return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n" + }, + "input_data": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "Accept Message, Data or DataFrame as input", + "input_types": [ + "Message", + "Data", + "DataFrame" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_data", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "output_type": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Output Type", + "dynamic": false, + "info": "Select the desired output data type", + "name": "output_type", + "options": [ + "Message", + "Data", + "DataFrame" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Message" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "TypeConverterComponent" + }, + "id": "TypeConverterComponent-Vb89B", + "measured": { + "height": 261, + "width": 320 + }, + "position": { + "x": 2158.2258554896966, + "y": 1267.3003018282393 + }, + "selected": true, + "type": "genericNode" } ], "viewport": { - "x": -213.05377487995963, - "y": 163.9059453667432, - "zoom": 0.46721297887336727 + "x": -793.1993033062734, + "y": -123.88385899329182, + "zoom": 0.6019755995423787 } }, "description": "Generates well-structured code for custom components following Langflow's specifications.", "endpoint_name": null, - "id": "4e7e8184-1a8c-4e04-ae99-b9c9ba48be53", + "id": "58e796f9-ab44-4962-bb29-7571312e75b2", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.2", "name": "Custom Component Generator", "tags": [ "coding", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json index 563ee62b6..63894033f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json @@ -2600,6 +2600,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -2707,7 +2751,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, @@ -3304,6 +3348,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -3411,7 +3499,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json index ead9caff6..e0a9febd9 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json @@ -408,6 +408,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -511,7 +555,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json b/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json index ab35c2933..9d50f7fda 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json @@ -1624,6 +1624,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1716,7 +1760,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json b/src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json index 1bf233925..d96e8a788 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json @@ -1502,6 +1502,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1611,7 +1655,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json index 1b5ec50f8..f841ad5ef 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json @@ -1474,6 +1474,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1566,7 +1610,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json b/src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json index eb412a53f..0b90f4010 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Meeting Summary.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-5Y1de", + "id": "Prompt-Vzk5z", "name": "prompt", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-ac6TO", + "id": "OpenAIModel-adSHw", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-5Y1de{œdataTypeœ:œPromptœ,œidœ:œPrompt-5Y1deœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-ac6TO{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-ac6TOœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-Vzk5z{œdataTypeœ:œPromptœ,œidœ:œPrompt-Vzk5zœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-adSHw{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-adSHwœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-5Y1de", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-5Y1deœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-ac6TO", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-ac6TOœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-Vzk5z", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-Vzk5zœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-adSHw", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-adSHwœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-ac6TO", + "id": "OpenAIModel-adSHw", "name": "text_output", "output_types": [ "Message" @@ -43,7 +43,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-g8PGI", + "id": "ChatOutput-NbY9b", "inputTypes": [ "Data", "DataFrame", @@ -52,12 +52,12 @@ "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-ac6TO{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-ac6TOœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-g8PGI{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-g8PGIœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-OpenAIModel-adSHw{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-adSHwœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-NbY9b{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-NbY9bœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "OpenAIModel-ac6TO", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-ac6TOœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-g8PGI", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-g8PGIœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "OpenAIModel-adSHw", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-adSHwœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-NbY9b", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-NbY9bœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -65,7 +65,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-t3KLO", + "id": "OpenAIModel-A8LqG", "name": "text_output", "output_types": [ "Message" @@ -73,7 +73,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-r8oga", + "id": "ChatOutput-paHng", "inputTypes": [ "Data", "DataFrame", @@ -82,41 +82,12 @@ "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-t3KLO{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-t3KLOœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-r8oga{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-r8ogaœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-OpenAIModel-A8LqG{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-A8LqGœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-paHng{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-paHngœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "OpenAIModel-t3KLO", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-t3KLOœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-r8oga", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-r8ogaœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-9SYWU", - "name": "messages_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "history", - "id": "Prompt-7sDZ1", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Memory-9SYWU{œdataTypeœ:œMemoryœ,œidœ:œMemory-9SYWUœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-7sDZ1{œfieldNameœ:œhistoryœ,œidœ:œPrompt-7sDZ1œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "Memory-9SYWU", - "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-9SYWUœ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-7sDZ1", - "targetHandle": "{œfieldNameœ: œhistoryœ, œidœ: œPrompt-7sDZ1œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "OpenAIModel-A8LqG", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-A8LqGœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-paHng", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-paHngœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -124,7 +95,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-2fdX6", + "id": "ChatInput-xfgsq", "name": "message", "output_types": [ "Message" @@ -132,7 +103,7 @@ }, "targetHandle": { "fieldName": "input", - "id": "Prompt-7sDZ1", + "id": "Prompt-iKEvN", "inputTypes": [ "Message", "Text" @@ -140,12 +111,12 @@ "type": "str" } }, - "id": "reactflow__edge-ChatInput-2fdX6{œdataTypeœ:œChatInputœ,œidœ:œChatInput-2fdX6œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-7sDZ1{œfieldNameœ:œinputœ,œidœ:œPrompt-7sDZ1œ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-xfgsq{œdataTypeœ:œChatInputœ,œidœ:œChatInput-xfgsqœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-iKEvN{œfieldNameœ:œinputœ,œidœ:œPrompt-iKEvNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-2fdX6", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-2fdX6œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-7sDZ1", - "targetHandle": "{œfieldNameœ: œinputœ, œidœ: œPrompt-7sDZ1œ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "ChatInput-xfgsq", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-xfgsqœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-iKEvN", + "targetHandle": "{œfieldNameœ: œinputœ, œidœ: œPrompt-iKEvNœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -153,7 +124,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-7sDZ1", + "id": "Prompt-iKEvN", "name": "prompt", "output_types": [ "Message" @@ -161,19 +132,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-t3KLO", + "id": "OpenAIModel-A8LqG", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-7sDZ1{œdataTypeœ:œPromptœ,œidœ:œPrompt-7sDZ1œ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-t3KLO{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-t3KLOœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-iKEvN{œdataTypeœ:œPromptœ,œidœ:œPrompt-iKEvNœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-A8LqG{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-A8LqGœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-7sDZ1", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-7sDZ1œ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-t3KLO", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-t3KLOœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-iKEvN", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-iKEvNœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-A8LqG", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-A8LqGœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -181,7 +152,7 @@ "data": { "sourceHandle": { "dataType": "AssemblyAITranscriptionJobCreator", - "id": "AssemblyAITranscriptionJobCreator-qtRiO", + "id": "AssemblyAITranscriptionJobCreator-hXp0S", "name": "transcript_id", "output_types": [ "Data" @@ -189,26 +160,27 @@ }, "targetHandle": { "fieldName": "transcript_id", - "id": "AssemblyAITranscriptionJobPoller-wdb3c", + "id": "AssemblyAITranscriptionJobPoller-VCkre", "inputTypes": [ "Data" ], "type": "other" } }, - "id": "reactflow__edge-AssemblyAITranscriptionJobCreator-qtRiO{œdataTypeœ:œAssemblyAITranscriptionJobCreatorœ,œidœ:œAssemblyAITranscriptionJobCreator-qtRiOœ,œnameœ:œtranscript_idœ,œoutput_typesœ:[œDataœ]}-AssemblyAITranscriptionJobPoller-wdb3c{œfieldNameœ:œtranscript_idœ,œidœ:œAssemblyAITranscriptionJobPoller-wdb3cœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AssemblyAITranscriptionJobCreator-hXp0S{œdataTypeœ:œAssemblyAITranscriptionJobCreatorœ,œidœ:œAssemblyAITranscriptionJobCreator-hXp0Sœ,œnameœ:œtranscript_idœ,œoutput_typesœ:[œDataœ]}-AssemblyAITranscriptionJobPoller-VCkre{œfieldNameœ:œtranscript_idœ,œidœ:œAssemblyAITranscriptionJobPoller-VCkreœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "AssemblyAITranscriptionJobCreator-qtRiO", - "sourceHandle": "{œdataTypeœ: œAssemblyAITranscriptionJobCreatorœ, œidœ: œAssemblyAITranscriptionJobCreator-qtRiOœ, œnameœ: œtranscript_idœ, œoutput_typesœ: [œDataœ]}", - "target": "AssemblyAITranscriptionJobPoller-wdb3c", - "targetHandle": "{œfieldNameœ: œtranscript_idœ, œidœ: œAssemblyAITranscriptionJobPoller-wdb3cœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}" + "source": "AssemblyAITranscriptionJobCreator-hXp0S", + "sourceHandle": "{œdataTypeœ: œAssemblyAITranscriptionJobCreatorœ, œidœ: œAssemblyAITranscriptionJobCreator-hXp0Sœ, œnameœ: œtranscript_idœ, œoutput_typesœ: [œDataœ]}", + "target": "AssemblyAITranscriptionJobPoller-VCkre", + "targetHandle": "{œfieldNameœ: œtranscript_idœ, œidœ: œAssemblyAITranscriptionJobPoller-VCkreœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "AssemblyAITranscriptionJobPoller", - "id": "AssemblyAITranscriptionJobPoller-wdb3c", + "id": "AssemblyAITranscriptionJobPoller-VCkre", "name": "transcription_result", "output_types": [ "Data" @@ -216,7 +188,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "parser-7Q6Sz", + "id": "parser-uRyvX", "inputTypes": [ "DataFrame", "Data" @@ -224,18 +196,20 @@ "type": "other" } }, - "id": "xy-edge__AssemblyAITranscriptionJobPoller-wdb3c{œdataTypeœ:œAssemblyAITranscriptionJobPollerœ,œidœ:œAssemblyAITranscriptionJobPoller-wdb3cœ,œnameœ:œtranscription_resultœ,œoutput_typesœ:[œDataœ]}-parser-7Q6Sz{œfieldNameœ:œinput_dataœ,œidœ:œparser-7Q6Szœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AssemblyAITranscriptionJobPoller-VCkre{œdataTypeœ:œAssemblyAITranscriptionJobPollerœ,œidœ:œAssemblyAITranscriptionJobPoller-VCkreœ,œnameœ:œtranscription_resultœ,œoutput_typesœ:[œDataœ]}-parser-uRyvX{œfieldNameœ:œinput_dataœ,œidœ:œparser-uRyvXœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "AssemblyAITranscriptionJobPoller-wdb3c", - "sourceHandle": "{œdataTypeœ: œAssemblyAITranscriptionJobPollerœ, œidœ: œAssemblyAITranscriptionJobPoller-wdb3cœ, œnameœ: œtranscription_resultœ, œoutput_typesœ: [œDataœ]}", - "target": "parser-7Q6Sz", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-7Q6Szœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "AssemblyAITranscriptionJobPoller-VCkre", + "sourceHandle": "{œdataTypeœ: œAssemblyAITranscriptionJobPollerœ, œidœ: œAssemblyAITranscriptionJobPoller-VCkreœ, œnameœ: œtranscription_resultœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-uRyvX", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-uRyvXœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" }, { + "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-7Q6Sz", + "id": "parser-uRyvX", "name": "parsed_text", "output_types": [ "Message" @@ -243,7 +217,7 @@ }, "targetHandle": { "fieldName": "transcript", - "id": "Prompt-5Y1de", + "id": "Prompt-Vzk5z", "inputTypes": [ "Message", "Text" @@ -251,17 +225,20 @@ "type": "str" } }, - "id": "xy-edge__parser-7Q6Sz{œdataTypeœ:œparserœ,œidœ:œparser-7Q6Szœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-5Y1de{œfieldNameœ:œtranscriptœ,œidœ:œPrompt-5Y1deœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "parser-7Q6Sz", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-7Q6Szœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-5Y1de", - "targetHandle": "{œfieldNameœ: œtranscriptœ, œidœ: œPrompt-5Y1deœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-parser-uRyvX{œdataTypeœ:œparserœ,œidœ:œparser-uRyvXœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-Vzk5z{œfieldNameœ:œtranscriptœ,œidœ:œPrompt-Vzk5zœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "selected": false, + "source": "parser-uRyvX", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-uRyvXœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-Vzk5z", + "targetHandle": "{œfieldNameœ: œtranscriptœ, œidœ: œPrompt-Vzk5zœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { + "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-7Q6Sz", + "id": "parser-uRyvX", "name": "parsed_text", "output_types": [ "Message" @@ -269,7 +246,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-AZKHq", + "id": "ChatOutput-5pvPL", "inputTypes": [ "Data", "DataFrame", @@ -278,17 +255,71 @@ "type": "str" } }, - "id": "xy-edge__parser-7Q6Sz{œdataTypeœ:œparserœ,œidœ:œparser-7Q6Szœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-AZKHq{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-AZKHqœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", - "source": "parser-7Q6Sz", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-7Q6Szœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-AZKHq", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-AZKHqœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-parser-uRyvX{œdataTypeœ:œparserœ,œidœ:œparser-uRyvXœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-5pvPL{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-5pvPLœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "parser-uRyvX", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-uRyvXœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-5pvPL", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-5pvPLœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-YN8aN", + "name": "dataframe", + "output_types": [ + "DataFrame" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "TypeConverterComponent-MzSbu", + "inputTypes": [ + "Message", + "Data", + "DataFrame" + ], + "type": "other" + } + }, + "id": "xy-edge__Memory-YN8aN{œdataTypeœ:œMemoryœ,œidœ:œMemory-YN8aNœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-MzSbu{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-MzSbuœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "source": "Memory-YN8aN", + "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-YN8aNœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "TypeConverterComponent-MzSbu", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-MzSbuœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-MzSbu", + "name": "message_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "history", + "id": "Prompt-iKEvN", + "inputTypes": [ + "Message", + "Text" + ], + "type": "str" + } + }, + "id": "xy-edge__TypeConverterComponent-MzSbu{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-MzSbuœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-iKEvN{œfieldNameœ:œhistoryœ,œidœ:œPrompt-iKEvNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "TypeConverterComponent-MzSbu", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-MzSbuœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-iKEvN", + "targetHandle": "{œfieldNameœ: œhistoryœ, œidœ: œPrompt-iKEvNœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "AssemblyAITranscriptionJobPoller-wdb3c", + "id": "AssemblyAITranscriptionJobPoller-VCkre", "node": { "base_classes": [ "Data" @@ -338,7 +369,7 @@ "dynamic": false, "info": "Your AssemblyAI API key. You can get one from https://www.assemblyai.com/", "input_types": [], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -346,7 +377,7 @@ "show": true, "title_case": false, "type": "str", - "value": "ASSEMBLYAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -418,9 +449,9 @@ "showNode": true, "type": "AssemblyAITranscriptionJobPoller" }, - "id": "AssemblyAITranscriptionJobPoller-wdb3c", + "id": "AssemblyAITranscriptionJobPoller-VCkre", "measured": { - "height": 294, + "height": 263, "width": 320 }, "position": { @@ -432,7 +463,7 @@ }, { "data": { - "id": "ParseData-LUfjb", + "id": "ParseData-X7r7s", "node": { "base_classes": [ "Data", @@ -581,9 +612,9 @@ "showNode": true, "type": "ParseData" }, - "id": "ParseData-LUfjb", + "id": "ParseData-X7r7s", "measured": { - "height": 342, + "height": 263, "width": 320 }, "position": { @@ -595,7 +626,7 @@ }, { "data": { - "id": "OpenAIModel-iudDZ", + "id": "OpenAIModel-adSHw", "node": { "base_classes": [ "LanguageModel", @@ -689,7 +720,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -973,9 +1004,9 @@ "showNode": true, "type": "OpenAIModel" }, - "id": "OpenAIModel-ac6TO", + "id": "OpenAIModel-adSHw", "measured": { - "height": 525, + "height": 537, "width": 320 }, "position": { @@ -987,7 +1018,7 @@ }, { "data": { - "id": "Prompt-5Y1de", + "id": "Prompt-Vzk5z", "node": { "base_classes": [ "Message" @@ -1125,9 +1156,9 @@ "showNode": true, "type": "Prompt" }, - "id": "Prompt-5Y1de", + "id": "Prompt-Vzk5z", "measured": { - "height": 413, + "height": 365, "width": 320 }, "position": { @@ -1139,7 +1170,7 @@ }, { "data": { - "id": "ChatOutput-g8PGI", + "id": "ChatOutput-NbY9b", "node": { "base_classes": [ "Message" @@ -1427,9 +1458,9 @@ "showNode": true, "type": "ChatOutput" }, - "id": "ChatOutput-g8PGI", + "id": "ChatOutput-NbY9b", "measured": { - "height": 230, + "height": 203, "width": 320 }, "position": { @@ -1441,7 +1472,7 @@ }, { "data": { - "id": "ChatOutput-AZKHq", + "id": "ChatOutput-5pvPL", "node": { "base_classes": [ "Message" @@ -1729,9 +1760,9 @@ "showNode": false, "type": "ChatOutput" }, - "id": "ChatOutput-AZKHq", + "id": "ChatOutput-5pvPL", "measured": { - "height": 66, + "height": 48, "width": 192 }, "position": { @@ -1743,7 +1774,7 @@ }, { "data": { - "id": "OpenAIModel-t3KLO", + "id": "OpenAIModel-A8LqG", "node": { "base_classes": [ "LanguageModel", @@ -1837,7 +1868,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -2121,9 +2152,9 @@ "showNode": true, "type": "OpenAIModel" }, - "id": "OpenAIModel-t3KLO", + "id": "OpenAIModel-A8LqG", "measured": { - "height": 525, + "height": 537, "width": 320 }, "position": { @@ -2135,7 +2166,7 @@ }, { "data": { - "id": "ChatOutput-r8oga", + "id": "ChatOutput-paHng", "node": { "base_classes": [ "Message" @@ -2423,9 +2454,9 @@ "showNode": false, "type": "ChatOutput" }, - "id": "ChatOutput-r8oga", + "id": "ChatOutput-paHng", "measured": { - "height": 66, + "height": 48, "width": 192 }, "position": { @@ -2437,7 +2468,7 @@ }, { "data": { - "id": "Prompt-7sDZ1", + "id": "Prompt-iKEvN", "node": { "base_classes": [ "Message" @@ -2599,9 +2630,9 @@ "showNode": true, "type": "Prompt" }, - "id": "Prompt-7sDZ1", + "id": "Prompt-iKEvN", "measured": { - "height": 465, + "height": 417, "width": 320 }, "position": { @@ -2613,7 +2644,7 @@ }, { "data": { - "id": "Memory-9SYWU", + "id": "Memory-YN8aN", "node": { "base_classes": [ "Data", @@ -2622,7 +2653,7 @@ "beta": false, "conditional_paths": [], "custom_fields": {}, - "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "description": "Stores or retrieves stored chat messages from Langflow tables or an external memory.", "display_name": "Message History", "documentation": "", "edited": false, @@ -2646,37 +2677,9 @@ { "allows_loop": false, "cache": true, - "display_name": "Data", + "display_name": "Messages", "group_outputs": false, - "method": "retrieve_messages", - "name": "messages", - "selected": "Data", - "tool_mode": true, - "types": [ - "Data" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Message", - "group_outputs": false, - "method": "retrieve_messages_as_text", - "name": "messages_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "DataFrame", - "group_outputs": false, - "method": "as_dataframe", + "method": "retrieve_messages_dataframe", "name": "dataframe", "selected": "DataFrame", "tool_mode": true, @@ -2705,11 +2708,11 @@ "show": true, "title_case": false, "type": "code", - "value": "from typing import cast\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 aget_messages\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\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=[\"Memory\"],\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=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\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=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\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 tool_mode=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"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=\"Data\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Message\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, await self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n async def as_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n" + "value": "from typing import Any, cast\n\nfrom langflow.custom import Component\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema import Data, dotdict\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.component_utils import set_current_fields, set_field_display\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\n description = \"Stores or retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n default_keys = [\"mode\", \"memory\"]\n mode_config = {\n \"Store\": [\"message\", \"memory\", \"sender\", \"sender_name\", \"session_id\"],\n \"Retrieve\": [\"n_messages\", \"order\", \"template\", \"memory\"],\n }\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Retrieve\", \"Store\"],\n value=\"Retrieve\",\n info=\"Operation mode: Store messages or Retrieve messages.\",\n real_time_refresh=True,\n ),\n MessageTextInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The chat message to be stored.\",\n tool_mode=True,\n dynamic=True,\n show=False,\n ),\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"Memory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n show=False,\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 show=False,\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 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 tool_mode=True,\n required=True,\n show=False,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n show=False,\n ),\n ]\n\n outputs = [Output(display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True)]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"mode\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n if field_value == \"Store\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Stored Messages\",\n name=\"stored_messages\",\n method=\"store_message\",\n hidden=True,\n dynamic=True,\n )\n ]\n if field_value == \"Retrieve\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n )\n ]\n return frontend_node\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n\n async def store_message(self) -> Message:\n message = Message(text=self.message) if isinstance(self.message, str) else self.message\n\n message.session_id = self.session_id or message.session_id\n message.sender = self.sender or message.sender or MESSAGE_SENDER_AI\n message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI\n\n stored_messages: list[Message] = []\n\n if self.memory:\n self.memory.session_id = message.session_id\n lc_message = message.to_lc_message()\n await self.memory.aadd_messages([lc_message])\n\n stored_messages = await self.memory.aget_messages() or []\n\n stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else []\n\n if message.sender:\n stored_messages = [m for m in stored_messages if m.sender == message.sender]\n else:\n await astore_message(message, flow_id=self.graph.flow_id)\n stored_messages = (\n await aget_messages(\n session_id=message.session_id, sender_name=message.sender_name, sender=message.sender\n )\n or []\n )\n\n if not stored_messages:\n msg = \"No messages were stored. Please ensure that the session ID and sender are properly set.\"\n raise ValueError(msg)\n\n stored_message = stored_messages[0]\n self.status = stored_message\n return stored_message\n\n def update_build_config(\n self,\n build_config: dotdict,\n field_value: Any, # noqa: ARG002\n field_name: str | None = None, # noqa: ARG002\n ) -> dotdict:\n return set_current_fields(\n build_config=build_config,\n action_fields=self.mode_config,\n selected_action=build_config[\"mode\"][\"value\"],\n default_fields=self.default_keys,\n func=set_field_display,\n )\n" }, "memory": { "_input_type": "HandleInput", - "advanced": false, + "advanced": true, "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", @@ -2727,6 +2730,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Message", + "dynamic": true, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Retrieve" + }, "n_messages": { "_input_type": "IntInput", "advanced": true, @@ -2760,7 +2807,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, @@ -2868,9 +2915,9 @@ "showNode": true, "type": "Memory" }, - "id": "Memory-9SYWU", + "id": "Memory-YN8aN", "measured": { - "height": 308, + "height": 217, "width": 320 }, "position": { @@ -2882,7 +2929,7 @@ }, { "data": { - "id": "ChatInput-2fdX6", + "id": "ChatInput-xfgsq", "node": { "base_classes": [ "Message" @@ -3167,9 +3214,9 @@ "showNode": false, "type": "ChatInput" }, - "id": "ChatInput-2fdX6", + "id": "ChatInput-xfgsq", "measured": { - "height": 66, + "height": 48, "width": 192 }, "position": { @@ -3181,7 +3228,7 @@ }, { "data": { - "id": "note-OELAf", + "id": "note-0oxqi", "node": { "description": "### 💡 Add your Assembly AI API key and audio file here", "display_name": "", @@ -3194,7 +3241,7 @@ }, "dragging": false, "height": 324, - "id": "note-OELAf", + "id": "note-0oxqi", "measured": { "height": 324, "width": 455 @@ -3210,7 +3257,7 @@ }, { "data": { - "id": "note-IrqOd", + "id": "note-YC1zc", "node": { "description": "### 💡 Add your Assembly AI API key here", "display_name": "", @@ -3223,7 +3270,7 @@ }, "dragging": false, "height": 324, - "id": "note-IrqOd", + "id": "note-YC1zc", "measured": { "height": 324, "width": 364 @@ -3239,7 +3286,7 @@ }, { "data": { - "id": "note-Yuc3u", + "id": "note-1sAEX", "node": { "description": "### 💡 Add your OpenAI API key here", "display_name": "", @@ -3252,7 +3299,7 @@ }, "dragging": false, "height": 324, - "id": "note-Yuc3u", + "id": "note-1sAEX", "measured": { "height": 324, "width": 334 @@ -3268,7 +3315,7 @@ }, { "data": { - "id": "note-8CPpJ", + "id": "note-aqmS2", "node": { "description": "### 💡 Add your OpenAI API key here", "display_name": "", @@ -3280,7 +3327,7 @@ "type": "note" }, "dragging": false, - "id": "note-8CPpJ", + "id": "note-aqmS2", "measured": { "height": 324, "width": 324 @@ -3294,7 +3341,7 @@ }, { "data": { - "id": "AssemblyAITranscriptionJobCreator-qtRiO", + "id": "AssemblyAITranscriptionJobCreator-hXp0S", "node": { "base_classes": [ "Data" @@ -3353,7 +3400,7 @@ "dynamic": false, "info": "Your AssemblyAI API key. You can get one from https://www.assemblyai.com/", "input_types": [], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -3361,7 +3408,7 @@ "show": true, "title_case": false, "type": "str", - "value": "ASSEMBLYAI_API_KEY" + "value": "" }, "audio_file": { "_input_type": "FileInput", @@ -3611,9 +3658,9 @@ "type": "AssemblyAITranscriptionJobCreator" }, "dragging": false, - "id": "AssemblyAITranscriptionJobCreator-qtRiO", + "id": "AssemblyAITranscriptionJobCreator-hXp0S", "measured": { - "height": 385, + "height": 341, "width": 320 }, "position": { @@ -3625,7 +3672,7 @@ }, { "data": { - "id": "note-VaF3v", + "id": "note-RCLbs", "node": { "description": "# Meeting Summary Generator\n\nThis flow automatically transcribes and summarizes meetings by converting audio recordings into concise summaries using **AssemblyAI** and **OpenAI GPT-4**. \n\n## Prerequisites\n\n- **[AssemblyAI API Key](https://www.assemblyai.com/)**\n- **[OpenAI API Key](https://platform.openai.com/)**\n\n## Quickstart\n\n1. Upload an audio file. Most common audio file formats are [supported](https://github.com/langflow-ai/langflow/blob/main/src/backend/base/langflow/components/assemblyai/assemblyai_start_transcript.py#L27).\n2. To run the summary generator flow, click **Playground**.\n\nThe flow transcribes the audio using **AssemblyAI**.\nThe transcript is formatted for AI processing.\nThe **GPT-4** model extracts key points and insights.\nThe summarized meeting details are displayed in a chat-friendly format.\n\n\n\n", "display_name": "", @@ -3636,7 +3683,7 @@ }, "dragging": false, "height": 612, - "id": "note-VaF3v", + "id": "note-RCLbs", "measured": { "height": 612, "width": 549 @@ -3652,7 +3699,7 @@ }, { "data": { - "id": "parser-7Q6Sz", + "id": "parser-uRyvX", "node": { "base_classes": [ "Message" @@ -3812,30 +3859,156 @@ "type": "parser" }, "dragging": false, - "id": "parser-7Q6Sz", + "id": "parser-uRyvX", "measured": { - "height": 395, + "height": 359, "width": 320 }, "position": { "x": 1335.003903610802, "y": 362.22452849075586 }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "TypeConverterComponent-MzSbu", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "processing", + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", + "documentation": "", + "edited": false, + "field_order": [ + "input_data", + "output_type" + ], + "frozen": false, + "icon": "repeat", + "key": "TypeConverterComponent", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message Output", + "group_outputs": false, + "method": "convert_to_message", + "name": "message_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.007568328950209746, + "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 typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import HandleInput, Output, TabInput\nfrom langflow.schema import Data, DataFrame, Message\n\n\ndef convert_to_message(v) -> Message:\n \"\"\"Convert input to Message type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Message: Converted Message object\n \"\"\"\n return v if isinstance(v, Message) else v.to_message()\n\n\ndef convert_to_data(v: DataFrame | Data | Message | dict) -> Data:\n \"\"\"Convert input to Data type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Data: Converted Data object\n \"\"\"\n if isinstance(v, dict):\n return Data(v)\n return v if isinstance(v, Data) else v.to_data()\n\n\ndef convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n DataFrame: Converted DataFrame object\n \"\"\"\n if isinstance(v, dict):\n return DataFrame([v])\n return v if isinstance(v, DataFrame) else v.to_dataframe()\n\n\nclass TypeConverterComponent(Component):\n display_name = \"Type Convert\"\n description = \"Convert between different types (Message, Data, DataFrame)\"\n icon = \"repeat\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Input\",\n input_types=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Accept Message, Data or DataFrame as input\",\n required=True,\n ),\n TabInput(\n name=\"output_type\",\n display_name=\"Output Type\",\n options=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Select the desired output data type\",\n real_time_refresh=True,\n value=\"Message\",\n ),\n ]\n\n outputs = [Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\")]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"output_type\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n\n # Add only the selected output type\n if field_value == \"Message\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\").to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Data Output\", name=\"data_output\", method=\"convert_to_data\").to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\", name=\"dataframe_output\", method=\"convert_to_dataframe\"\n ).to_dict()\n )\n\n return frontend_node\n\n def convert_to_message(self) -> Message:\n \"\"\"Convert input to Message type.\"\"\"\n return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n" + }, + "input_data": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "Accept Message, Data or DataFrame as input", + "input_types": [ + "Message", + "Data", + "DataFrame" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_data", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "output_type": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Output Type", + "dynamic": false, + "info": "Select the desired output data type", + "name": "output_type", + "options": [ + "Message", + "Data", + "DataFrame" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Message" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "TypeConverterComponent" + }, + "id": "TypeConverterComponent-MzSbu", + "measured": { + "height": 261, + "width": 320 + }, + "position": { + "x": 977.2392682107122, + "y": 1192.6380449385947 + }, "selected": true, "type": "genericNode" } ], "viewport": { - "x": 67.35579508773748, - "y": 126.4630908919309, - "zoom": 0.3907435802257692 + "x": -131.07372789550516, + "y": -274.4531542699592, + "zoom": 0.5495826307500584 } }, "description": "An AI-powered meeting summary generator that transcribes and summarizes meetings using AssemblyAI and OpenAI for quick insights.", "endpoint_name": null, - "id": "3ea130e1-2054-4a51-8fff-9d9bfc8a7947", + "id": "f05e8cbd-e83e-4c66-9c89-29dd57c72739", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.2", "name": "Meeting Summary", "tags": [ "chatbots", 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 656f3fbd0..dbff1c17e 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 @@ -2,39 +2,12 @@ "data": { "edges": [ { - "className": "", - "data": { - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-gWJrq", - "name": "messages_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "memory", - "id": "Prompt-yhdMP", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Memory-gWJrq{œdataTypeœ:œMemoryœ,œidœ:œMemory-gWJrqœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-yhdMP{œfieldNameœ:œmemoryœ,œidœ:œPrompt-yhdMPœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "Memory-gWJrq", - "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-gWJrqœ, œnameœ: œmessages_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-yhdMP", - "targetHandle": "{œfieldNameœ: œmemoryœ, œidœ: œPrompt-yhdMPœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, - { + "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-PEO9d", + "id": "ChatInput-Mqzss", "name": "message", "output_types": [ "Message" @@ -42,26 +15,27 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-63o3Q", + "id": "OpenAIModel-gXjiP", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-PEO9d{œdataTypeœ:œChatInputœ,œidœ:œChatInput-PEO9dœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-63o3Q{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-63o3Qœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-Mqzss{œdataTypeœ:œChatInputœ,œidœ:œChatInput-Mqzssœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-gXjiP{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gXjiPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-PEO9d", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-PEO9dœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-63o3Q", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-63o3Qœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-Mqzss", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-Mqzssœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-gXjiP", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-gXjiPœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { + "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-yhdMP", + "id": "Prompt-mtnlM", "name": "prompt", "output_types": [ "Message" @@ -69,26 +43,27 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "OpenAIModel-63o3Q", + "id": "OpenAIModel-gXjiP", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-yhdMP{œdataTypeœ:œPromptœ,œidœ:œPrompt-yhdMPœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-63o3Q{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-63o3Qœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-mtnlM{œdataTypeœ:œPromptœ,œidœ:œPrompt-mtnlMœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-gXjiP{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-gXjiPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-yhdMP", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-yhdMPœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-63o3Q", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-63o3Qœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-mtnlM", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-mtnlMœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-gXjiP", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-gXjiPœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { + "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-63o3Q", + "id": "OpenAIModel-gXjiP", "name": "text_output", "output_types": [ "Message" @@ -96,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-BIXzI", + "id": "ChatOutput-Er0pP", "inputTypes": [ "Data", "DataFrame", @@ -105,18 +80,74 @@ "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-63o3Q{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-63o3Qœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-BIXzI{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-BIXzIœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-OpenAIModel-gXjiP{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gXjiPœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Er0pP{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Er0pPœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "OpenAIModel-63o3Q", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-63o3Qœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-BIXzI", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-BIXzIœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "OpenAIModel-gXjiP", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-gXjiPœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-Er0pP", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Er0pPœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-6PlBG", + "name": "message_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "memory", + "id": "Prompt-mtnlM", + "inputTypes": [ + "Message", + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-TypeConverterComponent-6PlBG{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-6PlBGœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-mtnlM{œfieldNameœ:œmemoryœ,œidœ:œPrompt-mtnlMœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "selected": false, + "source": "TypeConverterComponent-6PlBG", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-6PlBGœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-mtnlM", + "targetHandle": "{œfieldNameœ: œmemoryœ, œidœ: œPrompt-mtnlMœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + }, + { + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-OBr5W", + "name": "dataframe", + "output_types": [ + "DataFrame" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "TypeConverterComponent-6PlBG", + "inputTypes": [ + "Message", + "Data", + "DataFrame" + ], + "type": "other" + } + }, + "id": "xy-edge__Memory-OBr5W{œdataTypeœ:œMemoryœ,œidœ:œMemory-OBr5Wœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-6PlBG{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-6PlBGœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "source": "Memory-OBr5W", + "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-OBr5Wœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "TypeConverterComponent-6PlBG", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-6PlBGœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" } ], "nodes": [ { "data": { - "id": "ChatInput-PEO9d", + "id": "ChatInput-Mqzss", "node": { "base_classes": [ "Message" @@ -400,7 +431,7 @@ }, "dragging": false, "height": 234, - "id": "ChatInput-PEO9d", + "id": "ChatInput-Mqzss", "measured": { "height": 234, "width": 320 @@ -421,7 +452,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-BIXzI", + "id": "ChatOutput-Er0pP", "node": { "base_classes": [ "Message" @@ -704,7 +735,7 @@ }, "dragging": true, "height": 234, - "id": "ChatOutput-BIXzI", + "id": "ChatOutput-Er0pP", "measured": { "height": 234, "width": 320 @@ -723,7 +754,7 @@ }, { "data": { - "id": "note-jlIQj", + "id": "note-bo9zi", "node": { "description": "# Memory Chatbot\n\nA flexible chatbot implementation featuring advanced conversation memory capabilities. This serves as a foundational tool for building chat experiences with persistent context.\n\n## Core Components\n\n1. **Chat Input**\n - Accepts user messages\n - Configures conversation storage\n - Tracks session identity\n\n2. **Chat Memory**\n - Stores and retrieves up to 100 previous messages\n - Maintains conversation context\n - Tracks separate chat sessions\n - Preserves sender information and message order\n\n3. **Prompt**\n - Creates dynamic prompt templates\n - Integrates memory into conversation flow\n\n4. **OpenAI**\n - Processes user input with context\n - Accesses conversation history\n - Includes options for model configuration and API key setup\n\n5. **Chat Output**\n - Displays formatted responses\n - Maintains conversation flow\n - Syncs with memory storage\n\n## Memory Features\n\n- Stores message history\n- Plans conversation trajectory\n- Differentiates between chat sessions\n- Preserves sender and message metadata\n\n## Quick Start\n\n1. **Initialize** with a clear session ID\n2. **Enter** message in Chat Input\n3. **AI Processes** with context from memory\n4. **Response** appears in Chat Output\n5. Context remains available for follow-ups\n\nThis robust system demonstrates thorough memory integration with minimal complexity. 🧠💬\n", "display_name": "", @@ -734,9 +765,9 @@ }, "dragging": false, "height": 736, - "id": "note-jlIQj", + "id": "note-bo9zi", "measured": { - "height": 733, + "height": 736, "width": 325 }, "position": { @@ -758,7 +789,7 @@ }, { "data": { - "id": "note-oU1IW", + "id": "note-yHt6k", "node": { "description": "## Get Your OpenAI API Key\n\n**Steps**:\n\n1. **Visit** [OpenAI's API Key Page](https://platform.openai.com/api-keys).\n\n2. **Log In/Sign Up**:\n - Log in or create a new OpenAI account.\n\n3. **Generate API Key**:\n - Click \"Create New Secret Key\" to obtain your key.\n\n4. **Store Your Key Securely**:\n - Note it down as it will only display once.\n\n5. **Enter API Key**:\n - Input your key in the OpenAI API Key field within the component setup.\n\nKeep your key safe and manage it responsibly!", "display_name": "", @@ -771,10 +802,10 @@ }, "dragging": false, "height": 325, - "id": "note-oU1IW", + "id": "note-yHt6k", "measured": { - "height": 324, - "width": 325 + "height": 325, + "width": 326 }, "position": { "x": 2727.7060397092964, @@ -790,271 +821,7 @@ }, { "data": { - "id": "Memory-gWJrq", - "node": { - "base_classes": [ - "Data", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Retrieves stored chat messages from Langflow tables or an external memory.", - "display_name": "Chat Memory", - "documentation": "", - "edited": false, - "field_order": [ - "memory", - "sender", - "sender_name", - "n_messages", - "session_id", - "order", - "template" - ], - "frozen": false, - "icon": "message-square-more", - "legacy": false, - "lf_version": "1.0.19.post2", - "metadata": {}, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Data", - "group_outputs": false, - "method": "retrieve_messages", - "name": "messages", - "selected": "Data", - "tool_mode": true, - "types": [ - "Data" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Message", - "group_outputs": false, - "method": "retrieve_messages_as_text", - "name": "messages_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "DataFrame", - "group_outputs": false, - "method": "as_dataframe", - "name": "dataframe", - "selected": "DataFrame", - "tool_mode": true, - "types": [ - "DataFrame" - ], - "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 typing import cast\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 aget_messages\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\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=[\"Memory\"],\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=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\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=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\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 tool_mode=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"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=\"Data\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Message\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, await self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n async def as_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n" - }, - "memory": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "External Memory", - "dynamic": false, - "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": [ - "Memory" - ], - "list": false, - "name": "memory", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "n_messages": { - "_input_type": "IntInput", - "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": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "display_name": "Order", - "dynamic": false, - "info": "Order of the messages.", - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "Ascending" - }, - "sender": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "display_name": "Sender Type", - "dynamic": false, - "info": "Filter by sender type.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "Machine and User" - }, - "sender_name": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "info": "Filter by sender name.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "session_id": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Session ID", - "dynamic": false, - "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "template": { - "_input_type": "MultilineInput", - "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, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "{sender_name}: {text}" - } - }, - "tool_mode": false - }, - "type": "Memory" - }, - "dragging": false, - "height": 264, - "id": "Memory-gWJrq", - "measured": { - "height": 264, - "width": 320 - }, - "position": { - "x": 1947.7805399474369, - "y": 766.1115984799474 - }, - "positionAbsolute": { - "x": 1947.7805399474369, - "y": 766.1115984799474 - }, - "selected": false, - "type": "genericNode", - "width": 320 - }, - { - "data": { - "id": "Prompt-yhdMP", + "id": "Prompt-mtnlM", "node": { "base_classes": [ "Message" @@ -1191,7 +958,7 @@ }, "dragging": false, "height": 347, - "id": "Prompt-yhdMP", + "id": "Prompt-mtnlM", "measured": { "height": 347, "width": 320 @@ -1210,7 +977,7 @@ }, { "data": { - "id": "OpenAIModel-63o3Q", + "id": "OpenAIModel-gXjiP", "node": { "base_classes": [ "LanguageModel", @@ -1304,7 +1071,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -1589,9 +1356,9 @@ "type": "OpenAIModel" }, "dragging": false, - "id": "OpenAIModel-63o3Q", + "id": "OpenAIModel-gXjiP", "measured": { - "height": 653, + "height": 537, "width": 320 }, "position": { @@ -1600,21 +1367,435 @@ }, "selected": false, "type": "genericNode" + }, + { + "data": { + "id": "TypeConverterComponent-6PlBG", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", + "documentation": "", + "edited": false, + "field_order": [ + "input_data", + "output_type" + ], + "frozen": false, + "icon": "repeat", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message Output", + "group_outputs": false, + "method": "convert_to_message", + "name": "message_output", + "selected": "Message", + "tool_mode": true, + "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 typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import HandleInput, Output, TabInput\nfrom langflow.schema import Data, DataFrame, Message\n\n\ndef convert_to_message(v) -> Message:\n \"\"\"Convert input to Message type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Message: Converted Message object\n \"\"\"\n return v if isinstance(v, Message) else v.to_message()\n\n\ndef convert_to_data(v: DataFrame | Data | Message | dict) -> Data:\n \"\"\"Convert input to Data type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Data: Converted Data object\n \"\"\"\n if isinstance(v, dict):\n return Data(v)\n return v if isinstance(v, Data) else v.to_data()\n\n\ndef convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n DataFrame: Converted DataFrame object\n \"\"\"\n if isinstance(v, dict):\n return DataFrame([v])\n return v if isinstance(v, DataFrame) else v.to_dataframe()\n\n\nclass TypeConverterComponent(Component):\n display_name = \"Type Convert\"\n description = \"Convert between different types (Message, Data, DataFrame)\"\n icon = \"repeat\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Input\",\n input_types=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Accept Message, Data or DataFrame as input\",\n required=True,\n ),\n TabInput(\n name=\"output_type\",\n display_name=\"Output Type\",\n options=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Select the desired output data type\",\n real_time_refresh=True,\n value=\"Message\",\n ),\n ]\n\n outputs = [Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\")]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"output_type\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n\n # Add only the selected output type\n if field_value == \"Message\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\").to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Data Output\", name=\"data_output\", method=\"convert_to_data\").to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\", name=\"dataframe_output\", method=\"convert_to_dataframe\"\n ).to_dict()\n )\n\n return frontend_node\n\n def convert_to_message(self) -> Message:\n \"\"\"Convert input to Message type.\"\"\"\n return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n" + }, + "input_data": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "Accept Message, Data or DataFrame as input", + "input_types": [ + "Message", + "Data", + "DataFrame" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_data", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "output_type": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Output Type", + "dynamic": false, + "info": "Select the desired output data type", + "name": "output_type", + "options": [ + "Message", + "Data", + "DataFrame" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Message" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "TypeConverterComponent" + }, + "dragging": false, + "id": "TypeConverterComponent-6PlBG", + "measured": { + "height": 261, + "width": 320 + }, + "position": { + "x": 2015.8179098384944, + "y": 984.8081816210474 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "Memory-OBr5W", + "node": { + "base_classes": [ + "DataFrame" + ], + "beta": false, + "category": "helpers", + "conditional_paths": [], + "custom_fields": {}, + "description": "Stores or retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Message History", + "documentation": "", + "edited": false, + "field_order": [ + "mode", + "message", + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template" + ], + "frozen": false, + "icon": "message-square-more", + "key": "Memory", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Messages", + "group_outputs": false, + "method": "retrieve_messages_dataframe", + "name": "dataframe", + "selected": "DataFrame", + "tool_mode": true, + "types": [ + "DataFrame" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.007568328950209746, + "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 typing import Any, cast\n\nfrom langflow.custom import Component\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema import Data, dotdict\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.component_utils import set_current_fields, set_field_display\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\n description = \"Stores or retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n default_keys = [\"mode\", \"memory\"]\n mode_config = {\n \"Store\": [\"message\", \"memory\", \"sender\", \"sender_name\", \"session_id\"],\n \"Retrieve\": [\"n_messages\", \"order\", \"template\", \"memory\"],\n }\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Retrieve\", \"Store\"],\n value=\"Retrieve\",\n info=\"Operation mode: Store messages or Retrieve messages.\",\n real_time_refresh=True,\n ),\n MessageTextInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The chat message to be stored.\",\n tool_mode=True,\n dynamic=True,\n show=False,\n ),\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"Memory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n show=False,\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 show=False,\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 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 tool_mode=True,\n required=True,\n show=False,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n show=False,\n ),\n ]\n\n outputs = [Output(display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True)]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"mode\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n if field_value == \"Store\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Stored Messages\",\n name=\"stored_messages\",\n method=\"store_message\",\n hidden=True,\n dynamic=True,\n )\n ]\n if field_value == \"Retrieve\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n )\n ]\n return frontend_node\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n\n async def store_message(self) -> Message:\n message = Message(text=self.message) if isinstance(self.message, str) else self.message\n\n message.session_id = self.session_id or message.session_id\n message.sender = self.sender or message.sender or MESSAGE_SENDER_AI\n message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI\n\n stored_messages: list[Message] = []\n\n if self.memory:\n self.memory.session_id = message.session_id\n lc_message = message.to_lc_message()\n await self.memory.aadd_messages([lc_message])\n\n stored_messages = await self.memory.aget_messages() or []\n\n stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else []\n\n if message.sender:\n stored_messages = [m for m in stored_messages if m.sender == message.sender]\n else:\n await astore_message(message, flow_id=self.graph.flow_id)\n stored_messages = (\n await aget_messages(\n session_id=message.session_id, sender_name=message.sender_name, sender=message.sender\n )\n or []\n )\n\n if not stored_messages:\n msg = \"No messages were stored. Please ensure that the session ID and sender are properly set.\"\n raise ValueError(msg)\n\n stored_message = stored_messages[0]\n self.status = stored_message\n return stored_message\n\n def update_build_config(\n self,\n build_config: dotdict,\n field_value: Any, # noqa: ARG002\n field_name: str | None = None, # noqa: ARG002\n ) -> dotdict:\n return set_current_fields(\n build_config=build_config,\n action_fields=self.mode_config,\n selected_action=build_config[\"mode\"][\"value\"],\n default_fields=self.default_keys,\n func=set_field_display,\n )\n" + }, + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": [ + "Memory" + ], + "list": false, + "list_add_label": "Add More", + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "message": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Message", + "dynamic": true, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Retrieve" + }, + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "list": false, + "list_add_label": "Add More", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "options_metadata": [], + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": true, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "template": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "Memory" + }, + "dragging": false, + "id": "Memory-OBr5W", + "measured": { + "height": 217, + "width": 320 + }, + "position": { + "x": 1768.3132819856035, + "y": 639.5677452473304 + }, + "selected": true, + "type": "genericNode" } ], "viewport": { - "x": -1173.6150415418488, - "y": -168.93756272342318, - "zoom": 0.7486095175892006 + "x": -777.2588770106167, + "y": 42.43614864750725, + "zoom": 0.5481262211197415 } }, "description": "Create a chatbot that saves and references previous messages, enabling the model to maintain context throughout the conversation.", "endpoint_name": null, - "gradient": "4", - "icon": "MessagesSquare", - "id": "7d334df6-6cf5-4d09-b6bf-169247b20446", + "id": "753b0efa-9a02-48ce-b9e8-b09244b1c110", "is_component": false, - "last_tested_version": "1.0.19.post2", + "last_tested_version": "1.4.2", "name": "Memory Chatbot", "tags": [ "chatbots", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json b/src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json index aa98bb9a4..3f7263eb3 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json @@ -1234,6 +1234,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1337,7 +1381,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json index 69265210d..9c51acb2e 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json @@ -1609,6 +1609,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1713,7 +1757,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "toggle": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json b/src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json index 4c88367a5..6c4314244 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json @@ -1916,6 +1916,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -2019,7 +2063,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json index 1734b3147..cc380a6d8 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json @@ -1254,6 +1254,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1346,7 +1390,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json b/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json index 23b808741..3a50f9b2a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json @@ -975,6 +975,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1067,7 +1111,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Search agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Search agent.json index 472b8490c..cfb434b8d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Search agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Search agent.json @@ -1351,6 +1351,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1454,7 +1498,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json index cca2a2e0c..a1f6b7a91 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json @@ -617,6 +617,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -709,7 +753,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, @@ -1240,6 +1284,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1332,7 +1420,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, @@ -2689,6 +2777,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -2781,7 +2913,7 @@ "Descending" ], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json index bf9720aa1..5033bc746 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json @@ -1533,6 +1533,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1637,7 +1681,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "toggle": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json index ddf51f189..398a56ccf 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json @@ -1595,6 +1595,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1698,7 +1742,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, 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 42b2c8f6e..db75899c6 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 @@ -1988,6 +1988,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -2091,7 +2135,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, @@ -2687,6 +2731,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -2790,7 +2878,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, @@ -3386,6 +3474,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -3489,7 +3621,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json b/src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json index bec30b2d8..f2103ba56 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json @@ -1439,6 +1439,50 @@ "type": "other", "value": "" }, + "message": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Message", + "dynamic": false, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": true, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Store" + }, "model_kwargs": { "_input_type": "DictInput", "advanced": true, @@ -1542,7 +1586,7 @@ ], "options_metadata": [], "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, "tool_mode": true, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py b/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py index e375d0512..9e68aeb2a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py +++ b/src/backend/base/langflow/initial_setup/starter_projects/memory_chatbot.py @@ -1,6 +1,7 @@ from langflow.components.helpers.memory import MemoryComponent from langflow.components.input_output import ChatInput, ChatOutput from langflow.components.languagemodels import OpenAIModelComponent +from langflow.components.processing.converter import TypeConverterComponent from langflow.components.prompts import PromptComponent from langflow.graph import Graph @@ -13,9 +14,13 @@ def memory_chatbot_graph(template: str | None = None): AI: """ memory_component = MemoryComponent() chat_input = ChatInput() + type_converter = TypeConverterComponent() + type_converter.set(input_data=memory_component.retrieve_messages_dataframe) prompt_component = PromptComponent() prompt_component.set( - template=template, user_message=chat_input.message_response, context=memory_component.retrieve_messages_as_text + template=template, + user_message=chat_input.message_response, + context=type_converter.convert_to_message, ) openai_component = OpenAIModelComponent() openai_component.set(input_value=prompt_component.build_prompt) diff --git a/src/backend/tests/data/MemoryChatbotNoLLM.json b/src/backend/tests/data/MemoryChatbotNoLLM.json index 14e0938a9..89283260f 100644 --- a/src/backend/tests/data/MemoryChatbotNoLLM.json +++ b/src/backend/tests/data/MemoryChatbotNoLLM.json @@ -1,886 +1,1384 @@ { - "id": "26c412c9-9e4a-406d-aadb-ef9a81badb3f", - "data": { - "nodes": [ - { - "data": { - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "id": "Prompt-iWbCC", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "template": [ - "context", - "user_message" - ] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "edited": false, - "field_order": [ - "template" - ], - "frozen": false, - "icon": "prompts", - "output_types": [], - "outputs": [ - { - "cache": true, - "display_name": "Prompt Message", - "method": "build_prompt", - "name": "prompt", - "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.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n" - }, - "context": { - "advanced": false, - "display_name": "context", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Message", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "context", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "template": { - "advanced": false, - "display_name": "Template", - "dynamic": false, - "info": "", - "list": false, - "load_from_db": false, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_input": true, - "type": "prompt", - "value": "{context}\n\nUser: {user_message}\nAI: " - }, - "user_message": { - "advanced": false, - "display_name": "user_message", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Message", - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "user_message", - "password": false, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - } - } - }, - "type": "Prompt" - }, - "dragging": false, - "height": 494, - "id": "Prompt-iWbCC", - "position": { - "x": 1880.8227904110583, - "y": 625.8049209882275 - }, - "positionAbsolute": { - "x": 1880.8227904110583, - "y": 625.8049209882275 - }, - "selected": false, - "type": "genericNode", - "width": 384 + "data": { + "edges": [ + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-8X8Cq", + "name": "dataframe", + "output_types": [ + "DataFrame" + ] + }, + "targetHandle": { + "fieldName": "input_data", + "id": "TypeConverterComponent-koSIz", + "inputTypes": [ + "Message", + "Data", + "DataFrame" + ], + "type": "other" + } + }, + "id": "xy-edge__Memory-8X8Cq{œdataTypeœ:œMemoryœ,œidœ:œMemory-8X8Cqœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-koSIz{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-koSIzœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "selected": false, + "source": "Memory-8X8Cq", + "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-8X8Cqœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}", + "target": "TypeConverterComponent-koSIz", + "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-koSIzœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-koSIz", + "name": "message_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "context", + "id": "Prompt-VSSGR", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__TypeConverterComponent-koSIz{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-koSIzœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-VSSGR{œfieldNameœ:œcontextœ,œidœ:œPrompt-VSSGRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "TypeConverterComponent-koSIz", + "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-koSIzœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-VSSGR", + "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-VSSGRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-vsgM1", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "user_message", + "id": "Prompt-VSSGR", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__ChatInput-vsgM1{œdataTypeœ:œChatInputœ,œidœ:œChatInput-vsgM1œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-VSSGR{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-VSSGRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-vsgM1", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-vsgM1œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-VSSGR", + "targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-VSSGRœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-VSSGR", + "name": "prompt", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-NAw0P", + "inputTypes": [ + "Data", + "DataFrame", + "Message" + ], + "type": "other" + } + }, + "id": "xy-edge__Prompt-VSSGR{œdataTypeœ:œPromptœ,œidœ:œPrompt-VSSGRœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-NAw0P{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-NAw0Pœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "selected": false, + "source": "Prompt-VSSGR", + "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-VSSGRœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", + "target": "ChatOutput-NAw0P", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-NAw0Pœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" + } + ], + "nodes": [ + { + "data": { + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "id": "Prompt-VSSGR", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": { + "template": [ + "context", + "user_message" + ] }, - { - "data": { - "description": "Get chat inputs from the Playground.", - "display_name": "Chat Input", - "id": "ChatInput-CIGht", - "node": { - "template": { - "_type": "Component", - "files": { - "trace_as_metadata": true, - "file_path": "", - "fileTypes": [ - "txt", - "md", - "mdx", - "csv", - "json", - "yaml", - "yml", - "xml", - "html", - "htm", - "pdf", - "docx", - "py", - "sh", - "sql", - "js", - "ts", - "tsx", - "jpg", - "jpeg", - "png", - "bmp", - "image" - ], - "list": true, - "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, - "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 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=\"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=[\"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 (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\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": { - "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.", - "title_case": false, - "type": "str" - }, - "sender": { - "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.", - "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": "User", - "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" - }, - "should_store_message": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "value": true, - "name": "should_store_message", - "display_name": "Store Messages", - "advanced": true, - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "files" - ], - "beta": false, - "edited": false - }, - "type": "ChatInput" - }, - "dragging": false, - "height": 294, - "id": "ChatInput-CIGht", - "position": { - "x": 1275.9262193671882, - "y": 836.1228056896347 - }, - "positionAbsolute": { - "x": 1275.9262193671882, - "y": 836.1228056896347 - }, - "selected": false, - "type": "genericNode", - "width": 384 + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "field_order": [ + "template", + "tool_placeholder" + ], + "frozen": false, + "icon": "braces", + "legacy": false, + "lf_version": "1.4.2", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Prompt", + "group_outputs": false, + "method": "build_prompt", + "name": "prompt", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "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.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n" + }, + "context": { + "advanced": false, + "display_name": "context", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "context", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "template": { + "_input_type": "PromptInput", + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "prompt", + "value": "{context}\n\nUser: {user_message}\nAI: " + }, + "tool_placeholder": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Tool Placeholder", + "dynamic": false, + "info": "A placeholder input for tool mode.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "tool_placeholder", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "user_message": { + "advanced": false, + "display_name": "user_message", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "user_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + } }, - { - "data": { - "description": "Display a chat message in the Playground.", - "display_name": "Chat Output", - "id": "ChatOutput-QA7ej", - "node": { - "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 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=\"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=[\"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 (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\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" - }, - "should_store_message": { - "trace_as_metadata": true, - "list": false, - "required": false, - "placeholder": "", - "show": true, - "value": true, - "name": "should_store_message", - "display_name": "Store Messages", - "advanced": true, - "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": "", - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "data_template" - ], - "beta": false, - "edited": false - }, - "type": "ChatOutput" - }, - "height": 294, - "id": "ChatOutput-QA7ej", - "position": { - "x": 2487.48936094892, - "y": 703.7197762654707 - }, - "selected": true, - "type": "genericNode", - "width": 384, - "positionAbsolute": { - "x": 2487.48936094892, - "y": 703.7197762654707 - }, - "dragging": true + "tool_mode": false + }, + "type": "Prompt" + }, + "dragging": false, + "height": 494, + "id": "Prompt-VSSGR", + "measured": { + "height": 494, + "width": 320 + }, + "position": { + "x": 1880.8227904110583, + "y": 625.8049209882275 + }, + "positionAbsolute": { + "x": 1880.8227904110583, + "y": 625.8049209882275 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "id": "ChatInput-vsgM1", + "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", + "should_store_message", + "sender", + "sender_name", + "session_id", + "files", + "background_color", + "chat_icon", + "text_color" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "lf_version": "1.4.2", + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Chat Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "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.inputs import BoolInput\nfrom langflow.io import (\n DropdownInput,\n FileInput,\n MessageTextInput,\n MultilineInput,\n Output,\n)\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_USER,\n MESSAGE_SENDER_USER,\n)\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 minimized = True\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n input_types=[],\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 temp_file=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=\"Chat Message\", name=\"message\", method=\"message_response\"),\n ]\n\n async def message_response(self) -> Message:\n background_color = self.background_color\n text_color = self.text_color\n icon = self.chat_icon\n\n message = await Message.create(\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={\n \"background_color\": background_color,\n \"text_color\": text_color,\n \"icon\": icon,\n },\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = await 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", + "advanced": true, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "jpg", + "jpeg", + "png", + "bmp", + "image" + ], + "file_path": "", + "info": "Files to be sent with the message.", + "list": true, + "list_add_label": "Add More", + "name": "files", + "placeholder": "", + "required": false, + "show": true, + "temp_file": true, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "input_value": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "Input Text", + "dynamic": false, + "info": "Message to be passed as input.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } }, - { - "data": { - "description": "Retrieves stored chat messages from Langflow tables or an external memory.", - "display_name": "Chat Memory", - "id": "Memory-amN4Z", - "node": { - "base_classes": [ - "BaseChatMemory", - "Data", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Retrieves stored chat messages from Langflow tables or an external memory.", - "display_name": "Chat Memory", - "documentation": "", - "edited": false, - "field_order": [ - "memory", - "sender", - "sender_name", - "n_messages", - "session_id", - "order", - "template" - ], - "frozen": false, - "icon": "message-square-more", - "output_types": [], - "outputs": [ - { - "cache": true, - "display_name": "Messages (Data)", - "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__" - }, - { - "cache": true, - "display_name": "Memory", - "method": "build_lc_memory", - "name": "lc_memory", - "selected": "BaseChatMemory", - "types": [ - "BaseChatMemory" - ], - "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.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)\n" - }, - "memory": { - "advanced": false, - "display_name": "External Memory", - "dynamic": false, - "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": [ - "BaseChatMessageHistory" - ], - "list": false, - "name": "memory", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "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}" - } - } - }, - "type": "Memory" - }, - "dragging": false, - "height": 366, - "id": "Memory-amN4Z", - "position": { - "x": 1308.5775646859402, - "y": 406.95204412025845 - }, - "positionAbsolute": { - "x": 1308.5775646859402, - "y": 406.95204412025845 - }, - "selected": false, - "type": "genericNode", - "width": 384 - } - ], - "edges": [ - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-CIGht", - "name": "message", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "user_message", - "id": "Prompt-iWbCC", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ChatInput-CIGht{œdataTypeœ:œChatInputœ,œidœ:œChatInput-CIGhtœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-iWbCC{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-iWbCCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-CIGht", - "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-CIGhtœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", - "target": "Prompt-iWbCC", - "targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-iWbCCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + "tool_mode": false + }, + "type": "ChatInput" + }, + "dragging": false, + "height": 294, + "id": "ChatInput-vsgM1", + "measured": { + "height": 294, + "width": 320 + }, + "position": { + "x": 1275.9262193671882, + "y": 836.1228056896347 + }, + "positionAbsolute": { + "x": 1275.9262193671882, + "y": 836.1228056896347 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "id": "ChatOutput-NAw0P", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "should_store_message", + "sender", + "sender_name", + "session_id", + "data_template", + "background_color", + "chat_icon", + "text_color", + "clean_data" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "lf_version": "1.4.2", + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Output Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "clean_data": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Basic Clean Data", + "dynamic": false, + "info": "Whether to clean the data", + "list": false, + "list_add_label": "Add More", + "name": "clean_data", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs import BoolInput\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\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 minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output 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 # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\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\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" + }, + "data_template": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "data_template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{text}" + }, + "input_value": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Inputs", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_value", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "AI" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } }, - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "Memory", - "id": "Memory-amN4Z", - "name": "messages_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "context", - "id": "Prompt-iWbCC", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Memory-amN4Z{œdataTypeœ:œMemoryœ,œidœ:œMemory-amN4Zœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-iWbCC{œfieldNameœ:œcontextœ,œidœ:œPrompt-iWbCCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "Memory-amN4Z", - "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-amN4Zœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}", - "target": "Prompt-iWbCC", - "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-iWbCCœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + "tool_mode": false + }, + "type": "ChatOutput" + }, + "dragging": true, + "height": 294, + "id": "ChatOutput-NAw0P", + "measured": { + "height": 294, + "width": 320 + }, + "position": { + "x": 2487.48936094892, + "y": 703.7197762654707 + }, + "positionAbsolute": { + "x": 2487.48936094892, + "y": 703.7197762654707 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Chat Memory", + "id": "Memory-8X8Cq", + "node": { + "base_classes": [ + "DataFrame" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Stores or retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Message History", + "documentation": "", + "edited": false, + "field_order": [ + "mode", + "message", + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template" + ], + "frozen": false, + "icon": "message-square-more", + "legacy": false, + "lf_version": "1.4.2", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Messages", + "group_outputs": false, + "hidden": null, + "method": "retrieve_messages_dataframe", + "name": "dataframe", + "options": null, + "required_inputs": null, + "selected": "DataFrame", + "tool_mode": true, + "types": [ + "DataFrame" + ], + "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 typing import Any, cast\n\nfrom langflow.custom import Component\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema import Data, dotdict\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.component_utils import set_current_fields, set_field_display\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Message History\"\n description = \"Stores or retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n default_keys = [\"mode\", \"memory\"]\n mode_config = {\n \"Store\": [\"message\", \"memory\", \"sender\", \"sender_name\", \"session_id\"],\n \"Retrieve\": [\"n_messages\", \"order\", \"template\", \"memory\"],\n }\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Retrieve\", \"Store\"],\n value=\"Retrieve\",\n info=\"Operation mode: Store messages or Retrieve messages.\",\n real_time_refresh=True,\n ),\n MessageTextInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The chat message to be stored.\",\n tool_mode=True,\n dynamic=True,\n show=False,\n ),\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"Memory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n show=False,\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 show=False,\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 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 tool_mode=True,\n required=True,\n show=False,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n show=False,\n ),\n ]\n\n outputs = [Output(display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True)]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"mode\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n if field_value == \"Store\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Stored Messages\",\n name=\"stored_messages\",\n method=\"store_message\",\n hidden=True,\n dynamic=True,\n )\n ]\n if field_value == \"Retrieve\":\n frontend_node[\"outputs\"] = [\n Output(\n display_name=\"Messages\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n )\n ]\n return frontend_node\n\n async 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 and not hasattr(self.memory, \"aget_messages\"):\n memory_name = type(self.memory).__name__\n err_msg = f\"External Memory object ({memory_name}) must have 'aget_messages' method.\"\n raise AttributeError(err_msg)\n # Check if n_messages is None or 0\n if n_messages == 0:\n stored = []\n elif self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = await self.memory.aget_messages()\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\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 if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = await aget_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 cast(Data, stored)\n\n async def retrieve_messages_dataframe(self) -> DataFrame:\n \"\"\"Convert the retrieved messages into a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the message data.\n \"\"\"\n messages = await self.retrieve_messages()\n return DataFrame(messages)\n\n async def store_message(self) -> Message:\n message = Message(text=self.message) if isinstance(self.message, str) else self.message\n\n message.session_id = self.session_id or message.session_id\n message.sender = self.sender or message.sender or MESSAGE_SENDER_AI\n message.sender_name = self.sender_name or message.sender_name or MESSAGE_SENDER_NAME_AI\n\n stored_messages: list[Message] = []\n\n if self.memory:\n self.memory.session_id = message.session_id\n lc_message = message.to_lc_message()\n await self.memory.aadd_messages([lc_message])\n\n stored_messages = await self.memory.aget_messages() or []\n\n stored_messages = [Message.from_lc_message(m) for m in stored_messages] if stored_messages else []\n\n if message.sender:\n stored_messages = [m for m in stored_messages if m.sender == message.sender]\n else:\n await astore_message(message, flow_id=self.graph.flow_id)\n stored_messages = (\n await aget_messages(\n session_id=message.session_id, sender_name=message.sender_name, sender=message.sender\n )\n or []\n )\n\n if not stored_messages:\n msg = \"No messages were stored. Please ensure that the session ID and sender are properly set.\"\n raise ValueError(msg)\n\n stored_message = stored_messages[0]\n self.status = stored_message\n return stored_message\n\n def update_build_config(\n self,\n build_config: dotdict,\n field_value: Any, # noqa: ARG002\n field_name: str | None = None, # noqa: ARG002\n ) -> dotdict:\n return set_current_fields(\n build_config=build_config,\n action_fields=self.mode_config,\n selected_action=build_config[\"mode\"][\"value\"],\n default_fields=self.default_keys,\n func=set_field_display,\n )\n" + }, + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": [ + "Memory" + ], + "list": false, + "list_add_label": "Add More", + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "message": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Message", + "dynamic": true, + "info": "The chat message to be stored.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "message", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", + "dynamic": false, + "info": "Operation mode: Store messages or Retrieve messages.", + "name": "mode", + "options": [ + "Retrieve", + "Store" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Retrieve" + }, + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "list": false, + "list_add_label": "Add More", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "options_metadata": [], + "placeholder": "", + "required": true, + "show": false, + "title_case": false, + "toggle": false, + "tool_mode": true, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "session_id": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "template": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "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, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + } }, - { - "source": "Prompt-iWbCC", - "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-iWbCCœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", - "target": "ChatOutput-QA7ej", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-QA7ejœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "data": { - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-QA7ej", - "inputTypes": [ - "Message" - ], - "type": "str" - }, - "sourceHandle": { - "dataType": "Prompt", - "id": "Prompt-iWbCC", - "name": "prompt", - "output_types": [ - "Message" - ] - } - }, - "id": "reactflow__edge-Prompt-iWbCC{œdataTypeœ:œPromptœ,œidœ:œPrompt-iWbCCœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-QA7ej{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-QA7ejœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" - } - ], - "viewport": { - "x": -417.45799796990354, - "y": 3.1615551909424653, - "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.", - "name": "MemoryChatbotNoLLM", - "last_tested_version": "1.0.12", - "endpoint_name": null, - "is_component": false + "tool_mode": false + }, + "type": "Memory" + }, + "dragging": false, + "height": 366, + "id": "Memory-8X8Cq", + "measured": { + "height": 366, + "width": 320 + }, + "position": { + "x": 1308.5775646859402, + "y": 406.95204412025845 + }, + "positionAbsolute": { + "x": 1308.5775646859402, + "y": 406.95204412025845 + }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "id": "TypeConverterComponent-koSIz", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "processing", + "conditional_paths": [], + "custom_fields": {}, + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", + "documentation": "", + "edited": false, + "field_order": [ + "input_data", + "output_type" + ], + "frozen": false, + "icon": "repeat", + "key": "TypeConverterComponent", + "legacy": false, + "lf_version": "1.4.2", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message Output", + "group_outputs": false, + "method": "convert_to_message", + "name": "message_output", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.007568328950209746, + "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 typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import HandleInput, Output, TabInput\nfrom langflow.schema import Data, DataFrame, Message\n\n\ndef convert_to_message(v) -> Message:\n \"\"\"Convert input to Message type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Message: Converted Message object\n \"\"\"\n return v if isinstance(v, Message) else v.to_message()\n\n\ndef convert_to_data(v: DataFrame | Data | Message | dict) -> Data:\n \"\"\"Convert input to Data type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n Data: Converted Data object\n \"\"\"\n if isinstance(v, dict):\n return Data(v)\n return v if isinstance(v, Data) else v.to_data()\n\n\ndef convert_to_dataframe(v: DataFrame | Data | Message | dict) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\n\n Args:\n v: Input to convert (Message, Data, DataFrame, or dict)\n\n Returns:\n DataFrame: Converted DataFrame object\n \"\"\"\n if isinstance(v, dict):\n return DataFrame([v])\n return v if isinstance(v, DataFrame) else v.to_dataframe()\n\n\nclass TypeConverterComponent(Component):\n display_name = \"Type Convert\"\n description = \"Convert between different types (Message, Data, DataFrame)\"\n icon = \"repeat\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Input\",\n input_types=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Accept Message, Data or DataFrame as input\",\n required=True,\n ),\n TabInput(\n name=\"output_type\",\n display_name=\"Output Type\",\n options=[\"Message\", \"Data\", \"DataFrame\"],\n info=\"Select the desired output data type\",\n real_time_refresh=True,\n value=\"Message\",\n ),\n ]\n\n outputs = [Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\")]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the selected output type.\"\"\"\n if field_name == \"output_type\":\n # Start with empty outputs\n frontend_node[\"outputs\"] = []\n\n # Add only the selected output type\n if field_value == \"Message\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Message Output\", name=\"message_output\", method=\"convert_to_message\").to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Data Output\", name=\"data_output\", method=\"convert_to_data\").to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\", name=\"dataframe_output\", method=\"convert_to_dataframe\"\n ).to_dict()\n )\n\n return frontend_node\n\n def convert_to_message(self) -> Message:\n \"\"\"Convert input to Message type.\"\"\"\n return convert_to_message(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n return convert_to_data(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n return convert_to_dataframe(self.input_data[0] if isinstance(self.input_data, list) else self.input_data)\n" + }, + "input_data": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "Accept Message, Data or DataFrame as input", + "input_types": [ + "Message", + "Data", + "DataFrame" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_data", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "output_type": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Output Type", + "dynamic": false, + "info": "Select the desired output data type", + "name": "output_type", + "options": [ + "Message", + "Data", + "DataFrame" + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tab", + "value": "Message" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "TypeConverterComponent" + }, + "dragging": false, + "id": "TypeConverterComponent-koSIz", + "measured": { + "height": 261, + "width": 320 + }, + "position": { + "x": 1680.7884314480486, + "y": 378.23790603026777 + }, + "selected": true, + "type": "genericNode" + } + ], + "viewport": { + "x": -810.6674739450368, + "y": -114.59139005551219, + "zoom": 0.6810300764379204 + } + }, + "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": "76f4de62-4bb6-4681-b90f-7be832cd9818", + "is_component": false, + "last_tested_version": "1.4.2", + "name": "MemoryChatbotNoLLM", + "tags": [] } \ No newline at end of file diff --git a/src/backend/tests/unit/build_utils.py b/src/backend/tests/unit/build_utils.py index c1f5dfc96..1385d8e04 100644 --- a/src/backend/tests/unit/build_utils.py +++ b/src/backend/tests/unit/build_utils.py @@ -72,15 +72,21 @@ async def consume_and_assert_stream(response, job_id, timeout=30.0): "Invalid first event. Expected 'vertices_sorted'. Full event stream:\n" + "\n".join(lines) ) ids = parsed["data"]["ids"] - ids.sort() - assert ids == ["ChatInput-CIGht"], "Invalid ids in first event. Full event stream:\n" + "\n".join( + + assert ids == ["ChatInput-vsgM1"], "Invalid ids in first event. Full event stream:\n" + "\n".join( lines ) to_run = parsed["data"]["to_run"] - to_run.sort() - assert to_run == ["ChatInput-CIGht", "ChatOutput-QA7ej", "Memory-amN4Z", "Prompt-iWbCC"], ( - "Invalid to_run list in first event. Full event stream:\n" + "\n".join(lines) + expected_to_run = [ + "ChatInput-vsgM1", + "Prompt-VSSGR", + "TypeConverterComponent-koSIz", + "Memory-8X8Cq", + "ChatOutput-NAw0P", + ] + assert set(to_run) == set(expected_to_run), ( + "Invalid to_run list in the first event. Full event stream:\n" + "\n".join(lines) ) first_event_seen = True # Last event should be end diff --git a/src/backend/tests/unit/graph/graph/test_graph_state_model.py b/src/backend/tests/unit/graph/graph/test_graph_state_model.py index d6dec74bc..adddfe447 100644 --- a/src/backend/tests/unit/graph/graph/test_graph_state_model.py +++ b/src/backend/tests/unit/graph/graph/test_graph_state_model.py @@ -4,6 +4,7 @@ import pytest from langflow.components.helpers.memory import MemoryComponent from langflow.components.input_output import ChatInput, ChatOutput from langflow.components.languagemodels import OpenAIModelComponent +from langflow.components.processing.converter import TypeConverterComponent from langflow.components.prompts import PromptComponent from langflow.graph import Graph from langflow.graph.graph.constants import Finish @@ -22,9 +23,13 @@ AI: """ memory_component = MemoryComponent(_id="chat_memory") memory_component.set(session_id=session_id) chat_input = ChatInput(_id="chat_input") + type_converter = TypeConverterComponent(_id="type_converter") + type_converter.set(input_data=memory_component.retrieve_messages_dataframe) prompt_component = PromptComponent(_id="prompt") prompt_component.set( - template=template, user_message=chat_input.message_response, context=memory_component.retrieve_messages_as_text + template=template, + user_message=chat_input.message_response, + context=type_converter.convert_to_message, ) openai_component = OpenAIModelComponent(_id="openai") openai_component.set( @@ -44,6 +49,7 @@ AI: """ "chat_output", "openai", "prompt", + "type_converter", "chat_memory", ] diff --git a/src/backend/tests/unit/graph/test_graph.py b/src/backend/tests/unit/graph/test_graph.py index 2f34cbd14..159ef6819 100644 --- a/src/backend/tests/unit/graph/test_graph.py +++ b/src/backend/tests/unit/graph/test_graph.py @@ -257,8 +257,12 @@ def test_update_source_handle(): async def test_serialize_graph(): + # Get the actual starter projects and directly await the result starter_projects = await load_starter_projects() - data = starter_projects[0][1]["data"] + project_data = starter_projects[0][1] + data = project_data["data"] + + # Create and test the graph graph = Graph.from_payload(data) assert isinstance(graph, Graph) serialized = graph.dumps() diff --git a/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py b/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py index 2ac738178..343c2ab36 100644 --- a/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py +++ b/src/backend/tests/unit/initial_setup/starter_projects/test_memory_chatbot.py @@ -6,6 +6,7 @@ import pytest from langflow.components.helpers.memory import MemoryComponent from langflow.components.input_output import ChatInput, ChatOutput from langflow.components.languagemodels import OpenAIModelComponent +from langflow.components.processing.converter import TypeConverterComponent from langflow.components.prompts import PromptComponent from langflow.graph import Graph from langflow.graph.graph.constants import Finish @@ -24,9 +25,13 @@ AI: """ memory_component = MemoryComponent(_id="chat_memory") memory_component.set(session_id=session_id) chat_input = ChatInput(_id="chat_input") + type_converter = TypeConverterComponent(_id="type_converter") + type_converter.set(input_data=memory_component.retrieve_messages_dataframe) prompt_component = PromptComponent(_id="prompt") prompt_component.set( - template=template, user_message=chat_input.message_response, context=memory_component.retrieve_messages_as_text + template=template, + user_message=chat_input.message_response, + context=type_converter.convert_to_message, ) openai_component = OpenAIModelComponent(_id="openai") openai_component.set( @@ -38,22 +43,30 @@ AI: """ chat_output.set(input_value=openai_component.text_response) graph = Graph(chat_input, chat_output) - assert graph.in_degree_map == {"chat_output": 1, "prompt": 2, "openai": 1, "chat_input": 0, "chat_memory": 0} + assert graph.in_degree_map == { + "chat_output": 1, + "type_converter": 1, + "prompt": 2, + "openai": 1, + "chat_input": 0, + "chat_memory": 0, + } return graph @pytest.mark.usefixtures("client") def test_memory_chatbot(memory_chatbot_graph): # Now we run step by step - expected_order = deque(["chat_input", "chat_memory", "prompt", "openai", "chat_output"]) + expected_order = deque(["chat_input", "chat_memory", "type_converter", "prompt", "openai", "chat_output"]) assert memory_chatbot_graph.in_degree_map == { "chat_output": 1, + "type_converter": 1, "prompt": 2, "openai": 1, "chat_input": 0, "chat_memory": 0, } - assert memory_chatbot_graph.vertices_layers == [["prompt"], ["openai"], ["chat_output"]] + assert memory_chatbot_graph.vertices_layers == [["type_converter"], ["prompt"], ["openai"], ["chat_output"]] assert memory_chatbot_graph.first_layer == ["chat_input", "chat_memory"] for step in expected_order: @@ -86,8 +99,8 @@ def test_memory_chatbot_dump_structure(memory_chatbot_graph: Graph): description = graph_dict["description"] endpoint_name = graph_dict["endpoint_name"] - assert len(nodes) == 5 - assert len(edges) == 4 + assert len(nodes) == 6 + assert len(edges) == 5 assert description is not None assert endpoint_name is not None @@ -124,7 +137,8 @@ def test_memory_chatbot_dump_components_and_edges(memory_chatbot_graph: Graph): # Check edges expected_edges = [ ("chat_input", "prompt"), - ("chat_memory", "prompt"), + ("chat_memory", "type_converter"), + ("type_converter", "prompt"), ("prompt", "openai"), ("openai", "chat_output"), ] diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 0a5100151..ef4df08b2 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -755,6 +755,7 @@ }, "node_modules/@clack/prompts/node_modules/is-unicode-supported": { "version": "1.3.0", + "extraneous": true, "inBundle": true, "license": "MIT", "engines": { diff --git a/src/frontend/tests/core/features/filterSidebar.spec.ts b/src/frontend/tests/core/features/filterSidebar.spec.ts index fa58bd6e1..9a9520b8e 100644 --- a/src/frontend/tests/core/features/filterSidebar.spec.ts +++ b/src/frontend/tests/core/features/filterSidebar.spec.ts @@ -74,7 +74,6 @@ test( await expect(page.getByTestId("input_outputChat Input")).toBeVisible(); await expect(page.getByTestId("input_outputChat Output")).toBeVisible(); await expect(page.getByTestId("promptsPrompt")).toBeVisible(); - await expect(page.getByTestId("helpersMessage History")).toBeVisible(); await expect(page.getByTestId("langchain_utilitiesCSVAgent")).toBeVisible(); await expect( page.getByTestId("langchain_utilitiesConversationChain"), @@ -99,7 +98,6 @@ test( await expect(page.getByTestId("input_outputChat Input")).not.toBeVisible(); await expect(page.getByTestId("input_outputChat Output")).not.toBeVisible(); await expect(page.getByTestId("promptsPrompt")).not.toBeVisible(); - await expect(page.getByTestId("helpersMessage History")).not.toBeVisible(); await expect( page.getByTestId("agentsTool Calling Agent"), ).not.toBeVisible(); @@ -121,7 +119,6 @@ test( await expect(page.getByTestId("disclosure-tools")).toBeVisible(); await expect(page.getByTestId("dataAPI Request")).toBeVisible(); - await expect(page.getByTestId("helpersMessage History")).toBeVisible(); await expect(page.getByTestId("vectorstoresAstra DB")).toBeVisible(); await expect(page.getByTestId("logicSub Flow [Deprecated]")).toBeVisible(); @@ -138,7 +135,6 @@ test( await page.getByTestId("icon-X").first().click(); await expect(page.getByTestId("dataAPI Request")).not.toBeVisible(); - await expect(page.getByTestId("helpersMessage History")).not.toBeVisible(); await expect(page.getByTestId("vectorstoresAstra DB")).not.toBeVisible(); await expect( page.getByTestId("logicSub Flow [Deprecated]"), diff --git a/src/frontend/tests/core/integrations/Custom Component Generator.spec.ts b/src/frontend/tests/core/integrations/Custom Component Generator.spec.ts index 3c30325b0..ee9c4f27c 100644 --- a/src/frontend/tests/core/integrations/Custom Component Generator.spec.ts +++ b/src/frontend/tests/core/integrations/Custom Component Generator.spec.ts @@ -44,6 +44,7 @@ withEventDeliveryModes( try { await page .getByTestId("anchor-popover-anchor-input-api_key") + .locator("input") .last() .fill(process.env.ANTHROPIC_API_KEY ?? ""); } catch (e) {