From cf44e7ad1473bd1c8d02d5b4b5439a5b7ae9004f Mon Sep 17 00:00:00 2001 From: Eric Hare Date: Tue, 24 Jun 2025 07:52:32 -0700 Subject: [PATCH 1/6] fix: Clean up some more base templates --- .../starter_projects/Document Q&A.json | 1138 ++++++++--------- .../starter_projects/Memory Chatbot.json | 467 ++----- 2 files changed, 607 insertions(+), 998 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json b/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json index 25487f833dc0..9944b070ccfe 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json @@ -1,13 +1,42 @@ { "data": { "edges": [ + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "parser", + "id": "parser-1O59j", + "name": "parsed_text", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "Document", + "id": "Prompt-GjFNQ", + "inputTypes": [ + "Message", + "Text" + ], + "type": "str" + } + }, + "id": "reactflow__edge-parser-1O59j{œdataTypeœ:œparserœ,œidœ:œparser-1O59jœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-GjFNQ{œfieldNameœ:œDocumentœ,œidœ:œPrompt-GjFNQœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "selected": false, + "source": "parser-1O59j", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-1O59jœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-GjFNQ", + "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-GjFNQœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-82ow1", + "id": "ChatInput-l9kmd", "name": "message", "output_types": [ "Message" @@ -15,19 +44,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-Xctjl", + "id": "LanguageModelComponent-IEakg", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-82ow1{œdataTypeœ:œChatInputœ,œidœ:œChatInput-82ow1œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-Xctjl{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Xctjlœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__ChatInput-l9kmd{œdataTypeœ:œChatInputœ,œidœ:œChatInput-l9kmdœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-IEakg{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-IEakgœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-82ow1", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-82ow1œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-Xctjl", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Xctjlœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-l9kmd", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-l9kmdœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-IEakg", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-IEakgœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +64,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-yAr8f", + "id": "Prompt-GjFNQ", "name": "prompt", "output_types": [ "Message" @@ -43,27 +72,27 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "OpenAIModel-Xctjl", + "id": "LanguageModelComponent-IEakg", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-yAr8f{œdataTypeœ:œPromptœ,œidœ:œPrompt-yAr8fœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-Xctjl{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-Xctjlœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__Prompt-GjFNQ{œdataTypeœ:œPromptœ,œidœ:œPrompt-GjFNQœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-IEakg{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-IEakgœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-yAr8f", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-yAr8fœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-Xctjl", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-Xctjlœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-GjFNQ", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-GjFNQœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-IEakg", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-IEakgœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-Xctjl", + "dataType": "LanguageModelComponent", + "id": "LanguageModelComponent-IEakg", "name": "text_output", "output_types": [ "Message" @@ -71,7 +100,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-hSEAB", + "id": "ChatOutput-FO9AH", "inputTypes": [ "Data", "DataFrame", @@ -80,19 +109,20 @@ "type": "str" } }, - "id": "reactflow__edge-OpenAIModel-Xctjl{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Xctjlœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-hSEAB{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-hSEABœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__LanguageModelComponent-IEakg{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-IEakgœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-FO9AH{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-FO9AHœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "OpenAIModel-Xctjl", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Xctjlœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-hSEAB", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-hSEABœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "LanguageModelComponent-IEakg", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-IEakgœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-FO9AH", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-FO9AHœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "File", - "id": "File-3BeiJ", + "id": "File-cLJI5", "name": "dataframe", "output_types": [ "DataFrame" @@ -100,7 +130,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "parser-mO32W", + "id": "parser-1O59j", "inputTypes": [ "DataFrame", "Data" @@ -108,40 +138,12 @@ "type": "other" } }, - "id": "xy-edge__File-3BeiJ{œdataTypeœ:œFileœ,œidœ:œFile-3BeiJœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-mO32W{œfieldNameœ:œinput_dataœ,œidœ:œparser-mO32Wœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", - "selected": false, - "source": "File-3BeiJ", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-3BeiJœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "parser-mO32W", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-mO32Wœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" - }, - { - "animated": false, - "data": { - "sourceHandle": { - "dataType": "parser", - "id": "parser-mO32W", - "name": "parsed_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "Document", - "id": "Prompt-yAr8f", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "xy-edge__parser-mO32W{œdataTypeœ:œparserœ,œidœ:œparser-mO32Wœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-yAr8f{œfieldNameœ:œDocumentœ,œidœ:œPrompt-yAr8fœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "xy-edge__File-cLJI5{œdataTypeœ:œFileœ,œidœ:œFile-cLJI5œ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-1O59j{œfieldNameœ:œinput_dataœ,œidœ:œparser-1O59jœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "parser-mO32W", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-mO32Wœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-yAr8f", - "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-yAr8fœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "File-cLJI5", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-cLJI5œ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "parser-1O59j", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-1O59jœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" } ], "nodes": [ @@ -149,7 +151,7 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-82ow1", + "id": "ChatInput-l9kmd", "node": { "base_classes": [ "Message" @@ -172,7 +174,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.0.19.post2", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -412,12 +414,12 @@ } } }, - "type": "ChatInput", - "selected_output": "message" + "selected_output": "message", + "type": "ChatInput" }, "dragging": false, "height": 234, - "id": "ChatInput-82ow1", + "id": "ChatInput-l9kmd", "measured": { "height": 234, "width": 320 @@ -438,7 +440,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-hSEAB", + "id": "ChatOutput-FO9AH", "node": { "base_classes": [ "Message" @@ -464,7 +466,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.0.19.post2", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -721,7 +723,7 @@ }, "dragging": false, "height": 234, - "id": "ChatOutput-hSEAB", + "id": "ChatOutput-FO9AH", "measured": { "height": 234, "width": 320 @@ -740,7 +742,7 @@ }, { "data": { - "id": "note-7pTJx", + "id": "note-CTOUn", "node": { "description": "# Document Q&A\n\nThis flow loads a file and uses an LLM to answer questions based on content from the loaded document. \n\n## Prerequisites\n\n* An [OpenAI API key](https://platform.openai.com/)\n\n## Quickstart\n\n1. Paste your OpenAI API key in the **OpenAI** model component.\n2. In the **File** component, select a file you want to load.\n3. Open the **Playground** and chat with your document.", "display_name": "", @@ -751,10 +753,10 @@ }, "dragging": false, "height": 509, - "id": "note-7pTJx", + "id": "note-CTOUn", "measured": { "height": 509, - "width": 421 + "width": 420 }, "position": { "x": -306.48794920240215, @@ -775,52 +777,47 @@ }, { "data": { - "id": "File-3BeiJ", + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "id": "Prompt-GjFNQ", "node": { "base_classes": [ - "Data" + "Message" ], "beta": false, "conditional_paths": [], - "custom_fields": {}, - "description": "Loads content from one or more files as a DataFrame.", - "display_name": "File", + "custom_fields": { + "template": [ + "Document" + ] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", "documentation": "", "edited": false, + "error": null, "field_order": [ - "path", - "silent_errors", - "use_multithreading", - "concurrency_multithreading" + "template" ], "frozen": false, - "icon": "file-text", + "full_path": null, + "icon": "braces", + "is_composition": null, + "is_input": null, + "is_output": null, "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, + "name": "", "output_types": [], "outputs": [ { "allows_loop": false, "cache": true, - "display_name": "Loaded Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": "DataFrame", - "tool_mode": true, - "types": [ - "DataFrame" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Raw Content", + "display_name": "Prompt", "group_outputs": false, - "method": "load_files_message", - "name": "message", + "method": "build_prompt", + "name": "prompt", "selected": "Message", "tool_mode": true, "types": [ @@ -831,6 +828,29 @@ ], "pinned": false, "template": { + "Document": { + "advanced": false, + "display_name": "Document", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message", + "Text" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "Document", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, "_type": "Component", "code": { "advanced": true, @@ -848,401 +868,275 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" - }, - "concurrency_multithreading": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Processing Concurrency", - "dynamic": false, - "info": "When multiple files are being processed, the number of files to process concurrently.", - "list": false, - "name": "concurrency_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "int", - "value": 4 + "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom.custom_component.component 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" }, - "delete_server_file_after_processing": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Delete Server File After Processing", + "template": { + "advanced": false, + "display_name": "Template", "dynamic": false, - "info": "If true, the Server File Path will be deleted after processing.", + "info": "", "list": false, - "name": "delete_server_file_after_processing", + "load_from_db": false, + "name": "template", "placeholder": "", "required": false, "show": true, "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": true + "trace_as_input": true, + "type": "prompt", + "value": "Answer user's questions based on the document below:\n\n---\n\n{Document}\n\n---\n\nQuestion:" }, - "file_path": { - "_input_type": "HandleInput", + "tool_placeholder": { + "_input_type": "MessageTextInput", "advanced": true, - "display_name": "Server File Path", + "display_name": "Tool Placeholder", "dynamic": false, - "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", + "info": "A placeholder input for tool mode.", "input_types": [ - "Data", "Message" ], - "list": true, - "name": "file_path", + "list": false, + "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": "other", + "type": "str", "value": "" - }, - "ignore_unspecified_files": { - "_input_type": "BoolInput", + } + }, + "tool_mode": false + }, + "selected_output": "prompt", + "type": "Prompt" + }, + "dragging": false, + "height": 347, + "id": "Prompt-GjFNQ", + "measured": { + "height": 347, + "width": 320 + }, + "position": { + "x": 882.4192413332464, + "y": -63.08797684105531 + }, + "positionAbsolute": { + "x": 895.1947781377585, + "y": -59.89409263992732 + }, + "selected": false, + "type": "genericNode", + "width": 320 + }, + { + "data": { + "id": "parser-1O59j", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "processing", + "conditional_paths": [], + "custom_fields": {}, + "description": "Format a DataFrame or Data object into text using a template. Enable 'Stringify' to convert input into a readable string instead.", + "display_name": "Parser", + "documentation": "", + "edited": false, + "field_order": [ + "mode", + "pattern", + "input_data", + "sep" + ], + "frozen": false, + "icon": "braces", + "key": "parser", + "legacy": false, + "lf_version": "1.4.3", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Parsed Text", + "method": "parse_combined_text", + "name": "parsed_text", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 2.220446049250313e-16, + "template": { + "_type": "Component", + "code": { "advanced": true, - "display_name": "Ignore Unspecified Files", - "dynamic": false, - "info": "If true, Data with no 'file_path' property will be ignored.", + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", "list": false, - "name": "ignore_unspecified_files", + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "ignore_unsupported_extensions": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Ignore Unsupported Extensions", - "dynamic": false, - "info": "If true, files with unsupported extensions will not be processed.", - "list": false, - "name": "ignore_unsupported_extensions", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "code", + "value": "import json\nfrom typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import (\n BoolInput,\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TabInput,\n)\nfrom langflow.schema import Data, DataFrame\nfrom langflow.schema.message import Message\n\n\nclass ParserComponent(Component):\n name = \"parser\"\n display_name = \"Parser\"\n description = (\n \"Format a DataFrame or Data object into text using a template. \"\n \"Enable 'Stringify' to convert input into a readable string instead.\"\n )\n icon = \"braces\"\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Parser\", \"Stringify\"],\n value=\"Parser\",\n info=\"Convert into raw string instead of using a template.\",\n real_time_refresh=True,\n ),\n MultilineInput(\n name=\"pattern\",\n display_name=\"Template\",\n info=(\n \"Use variables within curly brackets to extract column values for DataFrames \"\n \"or key values for Data.\"\n \"For example: `Name: {Name}, Age: {Age}, Country: {Country}`\"\n ),\n value=\"Text: {text}\", # Example default\n dynamic=True,\n show=True,\n required=True,\n ),\n HandleInput(\n name=\"input_data\",\n display_name=\"Data or DataFrame\",\n input_types=[\"DataFrame\", \"Data\"],\n info=\"Accepts either a DataFrame or a Data object.\",\n required=True,\n ),\n MessageTextInput(\n name=\"sep\",\n display_name=\"Separator\",\n advanced=True,\n value=\"\\n\",\n info=\"String used to separate rows/items.\",\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Parsed Text\",\n name=\"parsed_text\",\n info=\"Formatted text output.\",\n method=\"parse_combined_text\",\n ),\n ]\n\n def update_build_config(self, build_config, field_value, field_name=None):\n \"\"\"Dynamically hide/show `template` and enforce requirement based on `stringify`.\"\"\"\n if field_name == \"mode\":\n build_config[\"pattern\"][\"show\"] = self.mode == \"Parser\"\n build_config[\"pattern\"][\"required\"] = self.mode == \"Parser\"\n if field_value:\n clean_data = BoolInput(\n name=\"clean_data\",\n display_name=\"Clean Data\",\n info=(\n \"Enable to clean the data by removing empty rows and lines \"\n \"in each cell of the DataFrame/ Data object.\"\n ),\n value=True,\n advanced=True,\n required=False,\n )\n build_config[\"clean_data\"] = clean_data.to_dict()\n else:\n build_config.pop(\"clean_data\", None)\n\n return build_config\n\n def _clean_args(self):\n \"\"\"Prepare arguments based on input type.\"\"\"\n input_data = self.input_data\n\n match input_data:\n case list() if all(isinstance(item, Data) for item in input_data):\n msg = \"List of Data objects is not supported.\"\n raise ValueError(msg)\n case DataFrame():\n return input_data, None\n case Data():\n return None, input_data\n case dict() if \"data\" in input_data:\n try:\n if \"columns\" in input_data: # Likely a DataFrame\n return DataFrame.from_dict(input_data), None\n # Likely a Data object\n return None, Data(**input_data)\n except (TypeError, ValueError, KeyError) as e:\n msg = f\"Invalid structured input provided: {e!s}\"\n raise ValueError(msg) from e\n case _:\n msg = f\"Unsupported input type: {type(input_data)}. Expected DataFrame or Data.\"\n raise ValueError(msg)\n\n def parse_combined_text(self) -> Message:\n \"\"\"Parse all rows/items into a single text or convert input to string if `stringify` is enabled.\"\"\"\n # Early return for stringify option\n if self.mode == \"Stringify\":\n return self.convert_to_string()\n\n df, data = self._clean_args()\n\n lines = []\n if df is not None:\n for _, row in df.iterrows():\n formatted_text = self.pattern.format(**row.to_dict())\n lines.append(formatted_text)\n elif data is not None:\n formatted_text = self.pattern.format(**data.data)\n lines.append(formatted_text)\n\n combined_text = self.sep.join(lines)\n self.status = combined_text\n return Message(text=combined_text)\n\n def _safe_convert(self, data: Any) -> str:\n \"\"\"Safely convert input data to string.\"\"\"\n try:\n if isinstance(data, str):\n return data\n if isinstance(data, Message):\n return data.get_text()\n if isinstance(data, Data):\n return json.dumps(data.data)\n if isinstance(data, DataFrame):\n if hasattr(self, \"clean_data\") and self.clean_data:\n # Remove empty rows\n data = data.dropna(how=\"all\")\n # Remove empty lines in each cell\n data = data.replace(r\"^\\s*$\", \"\", regex=True)\n # Replace multiple newlines with a single newline\n data = data.replace(r\"\\n+\", \"\\n\", regex=True)\n return data.to_markdown(index=False)\n return str(data)\n except (ValueError, TypeError, AttributeError) as e:\n msg = f\"Error converting data: {e!s}\"\n raise ValueError(msg) from e\n\n def convert_to_string(self) -> Message:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n result = \"\"\n if isinstance(self.input_data, list):\n result = \"\\n\".join([self._safe_convert(item) for item in self.input_data])\n else:\n result = self._safe_convert(self.input_data)\n self.log(f\"Converted to string with length: {len(result)}\")\n\n message = Message(text=result)\n self.status = message\n return message\n" }, - "path": { - "_input_type": "FileInput", + "input_data": { + "_input_type": "HandleInput", "advanced": false, - "display_name": "Files", + "display_name": "Data or DataFrame", "dynamic": false, - "fileTypes": [ - "txt", - "md", - "mdx", - "csv", - "json", - "yaml", - "yml", - "xml", - "html", - "htm", - "pdf", - "docx", - "py", - "sh", - "sql", - "js", - "ts", - "tsx", - "zip", - "tar", - "tgz", - "bz2", - "gz" + "info": "Accepts either a DataFrame or a Data object.", + "input_types": [ + "DataFrame", + "Data" ], - "file_path": [], - "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", - "list": true, - "name": "path", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "file", - "value": "" - }, - "separator": { - "_input_type": "StrInput", - "advanced": true, - "display_name": "Separator", - "dynamic": false, - "info": "Specify the separator to use between multiple outputs in Message format.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "separator", + "name": "input_data", "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, - "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "\n\n" + "type": "other", + "value": "" }, - "silent_errors": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Silent Errors", + "mode": { + "_input_type": "TabInput", + "advanced": false, + "display_name": "Mode", "dynamic": false, - "info": "If true, errors will not raise an exception.", - "list": false, - "name": "silent_errors", + "info": "Convert into raw string instead of using a template.", + "name": "mode", + "options": [ + "Parser", + "Stringify" + ], "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "title_case": false, + "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": false + "type": "tab", + "value": "Parser" }, - "use_multithreading": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "[Deprecated] Use Multithreading", - "dynamic": false, - "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", - "list": false, - "name": "use_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - } - }, - "tool_mode": false - }, - "type": "File", - "selected_output": "dataframe" - }, - "dragging": false, - "height": 232, - "id": "File-3BeiJ", - "measured": { - "height": 232, - "width": 320 - }, - "position": { - "x": 150.6029945346864, - "y": -88.71582365936283 - }, - "positionAbsolute": { - "x": 155.39382083637838, - "y": -82.32805525710685 - }, - "selected": false, - "type": "genericNode", - "width": 320 - }, - { - "data": { - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "id": "Prompt-yAr8f", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "template": [ - "Document" - ] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "edited": false, - "error": null, - "field_order": [ - "template" - ], - "frozen": false, - "full_path": null, - "icon": "braces", - "is_composition": null, - "is_input": null, - "is_output": null, - "legacy": false, - "lf_version": "1.0.19.post2", - "metadata": {}, - "name": "", - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Prompt", - "group_outputs": false, - "method": "build_prompt", - "name": "prompt", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "Document": { + "pattern": { + "_input_type": "MultilineInput", "advanced": false, - "display_name": "Document", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", + "copy_field": false, + "display_name": "Template", + "dynamic": true, + "info": "Use variables within curly brackets to extract column values for DataFrames or key values for Data.For example: `Name: {Name}, Age: {Age}, Country: {Country}`", "input_types": [ - "Message", - "Text" + "Message" ], "list": false, + "list_add_label": "Add More", "load_from_db": false, "multiline": true, - "name": "Document", - "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, + "name": "pattern", "placeholder": "", "required": true, "show": true, "title_case": false, - "type": "code", - "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom.custom_component.component 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": { - "advanced": false, - "display_name": "Template", - "dynamic": false, - "info": "", - "list": false, - "load_from_db": false, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, + "tool_mode": false, "trace_as_input": true, - "type": "prompt", - "value": "Answer user's questions based on the document below:\n\n---\n\n{Document}\n\n---\n\nQuestion:" + "trace_as_metadata": true, + "type": "str", + "value": "Text: {text}" }, - "tool_placeholder": { + "sep": { "_input_type": "MessageTextInput", "advanced": true, - "display_name": "Tool Placeholder", + "display_name": "Separator", "dynamic": false, - "info": "A placeholder input for tool mode.", + "info": "String used to separate rows/items.", "input_types": [ "Message" ], "list": false, + "list_add_label": "Add More", "load_from_db": false, - "name": "tool_placeholder", + "name": "sep", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": true, + "tool_mode": false, "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "" + "value": "\n" } }, "tool_mode": false }, - "type": "Prompt", - "selected_output": "prompt" + "selected_output": "parsed_text", + "showNode": true, + "type": "parser" }, "dragging": false, - "height": 347, - "id": "Prompt-yAr8f", + "id": "parser-1O59j", "measured": { - "height": 347, + "height": 360, "width": 320 }, "position": { - "x": 882.4192413332464, - "y": -63.08797684105531 - }, - "positionAbsolute": { - "x": 895.1947781377585, - "y": -59.89409263992732 + "x": 517.4341822104063, + "y": -211.09448665229442 }, "selected": false, - "type": "genericNode", - "width": 320 + "type": "genericNode" }, { "data": { - "id": "OpenAIModel-Xctjl", + "id": "LanguageModelComponent-IEakg", "node": { "base_classes": [ "LanguageModel", "Message" ], "beta": false, - "category": "models", "conditional_paths": [], "custom_fields": {}, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", "documentation": "", "edited": false, "field_order": [ + "provider", + "model_name", + "api_key", "input_value", "system_message", "stream", - "max_tokens", - "model_kwargs", - "json_mode", - "model_name", - "openai_api_base", - "api_key", - "temperature", - "seed" + "temperature" ], "frozen": false, - "icon": "OpenAI", - "key": "OpenAIModel", + "icon": "brain-circuit", "legacy": false, + "lf_version": "1.4.3", "metadata": { "keywords": [ "model", @@ -1261,6 +1155,8 @@ "group_outputs": false, "method": "text_response", "name": "text_output", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1275,6 +1171,8 @@ "group_outputs": false, "method": "build_model", "name": "model_output", + "options": null, + "required_inputs": null, "selected": "LanguageModel", "tool_mode": true, "types": [ @@ -1284,7 +1182,7 @@ } ], "pinned": false, - "score": 0.14285714285714285, + "priority": 0, "template": { "_type": "Component", "api_key": { @@ -1292,13 +1190,14 @@ "advanced": false, "display_name": "OpenAI API Key", "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", + "info": "Model Provider API key", "input_types": [], "load_from_db": true, "name": "api_key", "password": true, "placeholder": "", - "required": true, + "real_time_refresh": true, + "required": false, "show": true, "title_case": false, "type": "str", @@ -1320,14 +1219,14 @@ "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.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" + "value": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" }, "input_value": { "_input_type": "MessageInput", "advanced": false, "display_name": "Input", "dynamic": false, - "info": "", + "info": "The input text to send to the model", "input_types": [ "Message" ], @@ -1345,92 +1244,14 @@ "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, + "combobox": false, "dialog_inputs": {}, "display_name": "Model Name", "dynamic": false, - "info": "", + "info": "Select the model to use", "name": "model_name", "options": [ "gpt-4o-mini", @@ -1442,62 +1263,61 @@ "gpt-4-turbo", "gpt-4-turbo-preview", "gpt-4", - "gpt-3.5-turbo", - "o1" + "gpt-3.5-turbo" ], "options_metadata": [], "placeholder": "", "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "gpt-4.1-mini" - }, - "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": "" + "value": "gpt-4o-mini" }, - "seed": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Seed", + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", "dynamic": false, - "info": "The seed controls the reproducibility of the job.", - "list": false, - "list_add_label": "Add More", - "name": "seed", + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "GoogleGenerativeAI" + } + ], "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, - "type": "int", - "value": 1 + "type": "str", + "value": "OpenAI" }, "stream": { "_input_type": "BoolInput", "advanced": true, "display_name": "Stream", "dynamic": false, - "info": "Stream the response from the model. Streaming works only in Chat.", + "info": "Whether to stream the response", "list": false, "list_add_label": "Add More", "name": "stream", @@ -1513,9 +1333,10 @@ "system_message": { "_input_type": "MultilineInput", "advanced": false, + "copy_field": false, "display_name": "System Message", "dynamic": false, - "info": "System message to pass to the model.", + "info": "A system message that helps set the behavior of the assistant", "input_types": [ "Message" ], @@ -1536,10 +1357,10 @@ }, "temperature": { "_input_type": "SliderInput", - "advanced": false, + "advanced": true, "display_name": "Temperature", "dynamic": false, - "info": "", + "info": "Controls randomness in responses", "max_label": "", "max_label_icon": "", "min_label": "", @@ -1561,70 +1382,56 @@ "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 }, "showNode": true, - "type": "OpenAIModel", - "selected_output": "text_output" + "type": "LanguageModelComponent" }, "dragging": false, - "id": "OpenAIModel-Xctjl", + "id": "LanguageModelComponent-IEakg", "measured": { - "height": 525, + "height": 532, "width": 320 }, "position": { - "x": 1257.1422612381514, - "y": -99.92567023810267 + "x": 1256.4027532477426, + "y": -116.62150897037591 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "parser-mO32W", + "id": "File-cLJI5", "node": { "base_classes": [ + "DataFrame", "Message" ], "beta": false, - "category": "processing", "conditional_paths": [], "custom_fields": {}, - "description": "Format a DataFrame or Data object into text using a template. Enable 'Stringify' to convert input into a readable string instead.", - "display_name": "Parser", + "description": "Loads content from one or more files as a DataFrame.", + "display_name": "File", "documentation": "", "edited": false, "field_order": [ - "mode", - "pattern", - "input_data", - "sep" + "path", + "file_path", + "separator", + "silent_errors", + "delete_server_file_after_processing", + "ignore_unsupported_extensions", + "ignore_unspecified_files", + "use_multithreading", + "concurrency_multithreading" ], "frozen": false, - "icon": "braces", - "key": "parser", + "icon": "file-text", "legacy": false, + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -1632,9 +1439,24 @@ { "allows_loop": false, "cache": true, - "display_name": "Parsed Text", - "method": "parse_combined_text", - "name": "parsed_text", + "display_name": "Loaded Files", + "group_outputs": false, + "method": "load_files", + "name": "dataframe", + "selected": "DataFrame", + "tool_mode": true, + "types": [ + "DataFrame" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Raw Content", + "group_outputs": false, + "method": "load_files_message", + "name": "message", "selected": "Message", "tool_mode": true, "types": [ @@ -1644,7 +1466,6 @@ } ], "pinned": false, - "score": 2.220446049250313e-16, "template": { "_type": "Component", "code": { @@ -1663,130 +1484,231 @@ "show": true, "title_case": false, "type": "code", - "value": "import json\nfrom typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import (\n BoolInput,\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TabInput,\n)\nfrom langflow.schema import Data, DataFrame\nfrom langflow.schema.message import Message\n\n\nclass ParserComponent(Component):\n name = \"parser\"\n display_name = \"Parser\"\n description = (\n \"Format a DataFrame or Data object into text using a template. \"\n \"Enable 'Stringify' to convert input into a readable string instead.\"\n )\n icon = \"braces\"\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Parser\", \"Stringify\"],\n value=\"Parser\",\n info=\"Convert into raw string instead of using a template.\",\n real_time_refresh=True,\n ),\n MultilineInput(\n name=\"pattern\",\n display_name=\"Template\",\n info=(\n \"Use variables within curly brackets to extract column values for DataFrames \"\n \"or key values for Data.\"\n \"For example: `Name: {Name}, Age: {Age}, Country: {Country}`\"\n ),\n value=\"Text: {text}\", # Example default\n dynamic=True,\n show=True,\n required=True,\n ),\n HandleInput(\n name=\"input_data\",\n display_name=\"Data or DataFrame\",\n input_types=[\"DataFrame\", \"Data\"],\n info=\"Accepts either a DataFrame or a Data object.\",\n required=True,\n ),\n MessageTextInput(\n name=\"sep\",\n display_name=\"Separator\",\n advanced=True,\n value=\"\\n\",\n info=\"String used to separate rows/items.\",\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Parsed Text\",\n name=\"parsed_text\",\n info=\"Formatted text output.\",\n method=\"parse_combined_text\",\n ),\n ]\n\n def update_build_config(self, build_config, field_value, field_name=None):\n \"\"\"Dynamically hide/show `template` and enforce requirement based on `stringify`.\"\"\"\n if field_name == \"mode\":\n build_config[\"pattern\"][\"show\"] = self.mode == \"Parser\"\n build_config[\"pattern\"][\"required\"] = self.mode == \"Parser\"\n if field_value:\n clean_data = BoolInput(\n name=\"clean_data\",\n display_name=\"Clean Data\",\n info=(\n \"Enable to clean the data by removing empty rows and lines \"\n \"in each cell of the DataFrame/ Data object.\"\n ),\n value=True,\n advanced=True,\n required=False,\n )\n build_config[\"clean_data\"] = clean_data.to_dict()\n else:\n build_config.pop(\"clean_data\", None)\n\n return build_config\n\n def _clean_args(self):\n \"\"\"Prepare arguments based on input type.\"\"\"\n input_data = self.input_data\n\n match input_data:\n case list() if all(isinstance(item, Data) for item in input_data):\n msg = \"List of Data objects is not supported.\"\n raise ValueError(msg)\n case DataFrame():\n return input_data, None\n case Data():\n return None, input_data\n case dict() if \"data\" in input_data:\n try:\n if \"columns\" in input_data: # Likely a DataFrame\n return DataFrame.from_dict(input_data), None\n # Likely a Data object\n return None, Data(**input_data)\n except (TypeError, ValueError, KeyError) as e:\n msg = f\"Invalid structured input provided: {e!s}\"\n raise ValueError(msg) from e\n case _:\n msg = f\"Unsupported input type: {type(input_data)}. Expected DataFrame or Data.\"\n raise ValueError(msg)\n\n def parse_combined_text(self) -> Message:\n \"\"\"Parse all rows/items into a single text or convert input to string if `stringify` is enabled.\"\"\"\n # Early return for stringify option\n if self.mode == \"Stringify\":\n return self.convert_to_string()\n\n df, data = self._clean_args()\n\n lines = []\n if df is not None:\n for _, row in df.iterrows():\n formatted_text = self.pattern.format(**row.to_dict())\n lines.append(formatted_text)\n elif data is not None:\n formatted_text = self.pattern.format(**data.data)\n lines.append(formatted_text)\n\n combined_text = self.sep.join(lines)\n self.status = combined_text\n return Message(text=combined_text)\n\n def _safe_convert(self, data: Any) -> str:\n \"\"\"Safely convert input data to string.\"\"\"\n try:\n if isinstance(data, str):\n return data\n if isinstance(data, Message):\n return data.get_text()\n if isinstance(data, Data):\n return json.dumps(data.data)\n if isinstance(data, DataFrame):\n if hasattr(self, \"clean_data\") and self.clean_data:\n # Remove empty rows\n data = data.dropna(how=\"all\")\n # Remove empty lines in each cell\n data = data.replace(r\"^\\s*$\", \"\", regex=True)\n # Replace multiple newlines with a single newline\n data = data.replace(r\"\\n+\", \"\\n\", regex=True)\n return data.to_markdown(index=False)\n return str(data)\n except (ValueError, TypeError, AttributeError) as e:\n msg = f\"Error converting data: {e!s}\"\n raise ValueError(msg) from e\n\n def convert_to_string(self) -> Message:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n result = \"\"\n if isinstance(self.input_data, list):\n result = \"\\n\".join([self._safe_convert(item) for item in self.input_data])\n else:\n result = self._safe_convert(self.input_data)\n self.log(f\"Converted to string with length: {len(result)}\")\n\n message = Message(text=result)\n self.status = message\n return message\n" + "value": "from langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" }, - "input_data": { + "concurrency_multithreading": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Processing Concurrency", + "dynamic": false, + "info": "When multiple files are being processed, the number of files to process concurrently.", + "list": false, + "list_add_label": "Add More", + "name": "concurrency_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 1 + }, + "delete_server_file_after_processing": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Delete Server File After Processing", + "dynamic": false, + "info": "If true, the Server File Path will be deleted after processing.", + "list": false, + "list_add_label": "Add More", + "name": "delete_server_file_after_processing", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "file_path": { "_input_type": "HandleInput", - "advanced": false, - "display_name": "Data or DataFrame", + "advanced": true, + "display_name": "Server File Path", "dynamic": false, - "info": "Accepts either a DataFrame or a Data object.", + "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", "input_types": [ - "DataFrame", - "Data" + "Data", + "Message" ], - "list": false, + "list": true, "list_add_label": "Add More", - "name": "input_data", + "name": "file_path", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "trace_as_metadata": true, "type": "other", "value": "" }, - "mode": { - "_input_type": "TabInput", - "advanced": false, - "display_name": "Mode", + "ignore_unspecified_files": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unspecified Files", "dynamic": false, - "info": "Convert into raw string instead of using a template.", - "name": "mode", - "options": [ - "Parser", - "Stringify" - ], + "info": "If true, Data with no 'file_path' property will be ignored.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unspecified_files", "placeholder": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "tab", - "value": "Parser" + "type": "bool", + "value": false }, - "pattern": { - "_input_type": "MultilineInput", + "ignore_unsupported_extensions": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unsupported Extensions", + "dynamic": false, + "info": "If true, files with unsupported extensions will not be processed.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unsupported_extensions", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "path": { + "_input_type": "FileInput", "advanced": false, - "copy_field": false, - "display_name": "Template", - "dynamic": true, - "info": "Use variables within curly brackets to extract column values for DataFrames or key values for Data.For example: `Name: {Name}, Age: {Age}, Country: {Country}`", - "input_types": [ - "Message" + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "zip", + "tar", + "tgz", + "bz2", + "gz" ], + "file_path": [], + "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", + "list": true, + "list_add_label": "Add More", + "name": "path", + "placeholder": "", + "required": false, + "show": true, + "temp_file": false, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "separator": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Separator", + "dynamic": false, + "info": "Specify the separator to use between multiple outputs in Message format.", "list": false, "list_add_label": "Add More", "load_from_db": false, - "multiline": true, - "name": "pattern", + "name": "separator", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "Text: {text}" + "value": "\n\n" }, - "sep": { - "_input_type": "MessageTextInput", + "silent_errors": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Separator", + "display_name": "Silent Errors", "dynamic": false, - "info": "String used to separate rows/items.", - "input_types": [ - "Message" - ], + "info": "If true, errors will not raise an exception.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "sep", + "name": "silent_errors", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "\n" + "type": "bool", + "value": false + }, + "use_multithreading": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "[Deprecated] Use Multithreading", + "dynamic": false, + "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", + "list": false, + "list_add_label": "Add More", + "name": "use_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true } }, "tool_mode": false }, "showNode": true, - "type": "parser", - "selected_output": "parsed_text" + "type": "File" }, "dragging": false, - "id": "parser-mO32W", + "id": "File-cLJI5", "measured": { - "height": 395, + "height": 233, "width": 320 }, "position": { - "x": 517.4341822104063, - "y": -211.09448665229442 + "x": 148.2876240515333, + "y": -84.59506014967624 }, "selected": true, "type": "genericNode" } ], "viewport": { - "x": 201.52975647724884, - "y": 410.819941953365, - "zoom": 0.6386710713834723 + "x": 262.39677390913835, + "y": 326.83109064058834, + "zoom": 0.6244838480835059 } }, "description": "Integrates PDF reading with a language model to answer document-specific questions. Ideal for small-scale texts, it facilitates direct queries with immediate insights.", "endpoint_name": null, - "id": "e088aa15-23da-4839-b3f0-eb733b973a8b", + "id": "8aa6b909-19cb-4e09-8395-a107d6aa278b", "is_component": false, - "last_tested_version": "1.2.0", + "last_tested_version": "1.4.3", "name": "Document Q&A", "tags": [ "rag", 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 25625409e8b9..038b673d4f94 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 @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-Mqzss", + "id": "ChatInput-xyuQP", "name": "message", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "OpenAIModel-gXjiP", + "id": "OpenAIModel-uwgKb", "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œ}", + "id": "reactflow__edge-ChatInput-xyuQP{œdataTypeœ:œChatInputœ,œidœ:œChatInput-xyuQPœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-uwgKb{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uwgKbœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "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œ}" + "source": "ChatInput-xyuQP", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-xyuQPœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-uwgKb", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-uwgKbœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-mtnlM", + "id": "Prompt-2aw7O", "name": "prompt", "output_types": [ "Message" @@ -43,19 +43,19 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "OpenAIModel-gXjiP", + "id": "OpenAIModel-uwgKb", "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œ}", + "id": "reactflow__edge-Prompt-2aw7O{œdataTypeœ:œPromptœ,œidœ:œPrompt-2aw7Oœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-uwgKb{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-uwgKbœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "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œ}" + "source": "Prompt-2aw7O", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-2aw7Oœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "OpenAIModel-uwgKb", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-uwgKbœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-gXjiP", + "id": "OpenAIModel-uwgKb", "name": "text_output", "output_types": [ "Message" @@ -71,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-Er0pP", + "id": "ChatOutput-cBToy", "inputTypes": [ "Data", "DataFrame", @@ -80,12 +80,12 @@ "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œ}", + "id": "reactflow__edge-OpenAIModel-uwgKb{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uwgKbœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-cBToy{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-cBToyœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "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œ}" + "source": "OpenAIModel-uwgKb", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-uwgKbœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-cBToy", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-cBToyœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -93,7 +93,7 @@ "data": { "sourceHandle": { "dataType": "TypeConverterComponent", - "id": "TypeConverterComponent-6PlBG", + "id": "TypeConverterComponent-6kqHz", "name": "message_output", "output_types": [ "Message" @@ -101,7 +101,7 @@ }, "targetHandle": { "fieldName": "memory", - "id": "Prompt-mtnlM", + "id": "Prompt-2aw7O", "inputTypes": [ "Message", "Text" @@ -109,18 +109,20 @@ "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œ}", + "id": "reactflow__edge-TypeConverterComponent-6kqHz{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-6kqHzœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-2aw7O{œfieldNameœ:œmemoryœ,œidœ:œPrompt-2aw7Oœ,œ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œ}" + "source": "TypeConverterComponent-6kqHz", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-6kqHzœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-2aw7O", + "targetHandle": "{œfieldNameœ: œmemoryœ, œidœ: œPrompt-2aw7Oœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { + "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "Memory", - "id": "Memory-OBr5W", + "id": "Memory-Esy2a", "name": "dataframe", "output_types": [ "DataFrame" @@ -128,7 +130,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "TypeConverterComponent-6PlBG", + "id": "TypeConverterComponent-6kqHz", "inputTypes": [ "Message", "Data", @@ -137,17 +139,18 @@ "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œ}" + "id": "reactflow__edge-Memory-Esy2a{œdataTypeœ:œMemoryœ,œidœ:œMemory-Esy2aœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-6kqHz{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-6kqHzœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "selected": false, + "source": "Memory-Esy2a", + "sourceHandle": "{œdataTypeœ: œMemoryœ, œidœ: œMemory-Esy2aœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "TypeConverterComponent-6kqHz", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-6kqHzœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" } ], "nodes": [ { "data": { - "id": "ChatInput-Mqzss", + "id": "ChatInput-xyuQP", "node": { "base_classes": [ "Message" @@ -173,7 +176,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.0.19.post2", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -432,7 +435,7 @@ }, "dragging": false, "height": 234, - "id": "ChatInput-Mqzss", + "id": "ChatInput-xyuQP", "measured": { "height": 234, "width": 320 @@ -453,7 +456,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-Er0pP", + "id": "ChatOutput-cBToy", "node": { "base_classes": [ "Message" @@ -479,7 +482,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.0.19.post2", + "lf_version": "1.4.3", "metadata": {}, "output_types": [], "outputs": [ @@ -736,7 +739,7 @@ }, "dragging": true, "height": 234, - "id": "ChatOutput-Er0pP", + "id": "ChatOutput-cBToy", "measured": { "height": 234, "width": 320 @@ -755,7 +758,7 @@ }, { "data": { - "id": "note-bo9zi", + "id": "note-WfHA2", "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": "", @@ -766,10 +769,10 @@ }, "dragging": false, "height": 736, - "id": "note-bo9zi", + "id": "note-WfHA2", "measured": { "height": 736, - "width": 325 + "width": 324 }, "position": { "x": 1512.8976594415833, @@ -790,7 +793,7 @@ }, { "data": { - "id": "note-yHt6k", + "id": "note-cHuhR", "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": "", @@ -803,10 +806,10 @@ }, "dragging": false, "height": 325, - "id": "note-yHt6k", + "id": "note-cHuhR", "measured": { "height": 325, - "width": 326 + "width": 325 }, "position": { "x": 2727.7060397092964, @@ -822,326 +825,7 @@ }, { "data": { - "id": "Memory-gWJrq", - "node": { - "base_classes": [ - "Data", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Stores or 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": "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": "retrieve_messages_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 Any, cast\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.inputs.inputs import DropdownInput, HandleInput, IntInput, MessageTextInput, MultilineInput, TabInput\nfrom langflow.memory import aget_messages, astore_message\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\nfrom langflow.template.field.base import Output\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_type\",\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\",\n display_name=\"Sender\",\n info=\"The sender of the message. Might be Machine or User. \"\n \"If empty, the current sender parameter will be used.\",\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 value=\"\",\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 ),\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 = [\n Output(display_name=\"Message\", name=\"messages_text\", method=\"retrieve_messages_as_text\", dynamic=True),\n Output(display_name=\"Dataframe\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True),\n ]\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=\"messages_text\", method=\"retrieve_messages_as_text\", dynamic=True\n ),\n Output(\n display_name=\"Dataframe\", name=\"dataframe\", method=\"retrieve_messages_dataframe\", dynamic=True\n ),\n ]\n return frontend_node\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 async def retrieve_messages(self) -> Data:\n sender_type = self.sender_type\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_type == \"Machine and User\":\n sender_type = 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_type:\n expected_type = MESSAGE_SENDER_AI if sender_type == 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_type,\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 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 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, - "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, - "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": true, - "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", - "dynamic": false, - "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "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": "" - }, - "sender_type": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender Type", - "dynamic": false, - "info": "Filter by sender type.", - "name": "sender_type", - "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" - }, - "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-2aw7O", "node": { "base_classes": [ "Message" @@ -1168,7 +852,7 @@ "is_input": null, "is_output": null, "legacy": false, - "lf_version": "1.0.19.post2", + "lf_version": "1.4.3", "metadata": {}, "name": "", "output_types": [], @@ -1279,14 +963,14 @@ }, "dragging": false, "height": 347, - "id": "Prompt-mtnlM", + "id": "Prompt-2aw7O", "measured": { "height": 347, "width": 320 }, "position": { - "x": 2327.422938009026, - "y": 675.992123914672 + "x": 2351.863651394379, + "y": 636.2759646634735 }, "positionAbsolute": { "x": 2327.422938009026, @@ -1298,7 +982,7 @@ }, { "data": { - "id": "OpenAIModel-gXjiP", + "id": "OpenAIModel-uwgKb", "node": { "base_classes": [ "LanguageModel", @@ -1329,6 +1013,7 @@ "icon": "OpenAI", "key": "OpenAIModel", "legacy": false, + "lf_version": "1.4.3", "metadata": { "keywords": [ "model", @@ -1388,7 +1073,7 @@ "show": true, "title_case": false, "type": "str", - "value": "" + "value": "OPENAI_API_KEY" }, "code": { "advanced": true, @@ -1674,9 +1359,9 @@ "type": "OpenAIModel" }, "dragging": false, - "id": "OpenAIModel-gXjiP", + "id": "OpenAIModel-uwgKb", "measured": { - "height": 537, + "height": 539, "width": 320 }, "position": { @@ -1688,7 +1373,7 @@ }, { "data": { - "id": "TypeConverterComponent-6PlBG", + "id": "TypeConverterComponent-6kqHz", "node": { "base_classes": [ "Message" @@ -1707,6 +1392,7 @@ "frozen": false, "icon": "repeat", "legacy": false, + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -1799,21 +1485,21 @@ "type": "TypeConverterComponent" }, "dragging": false, - "id": "TypeConverterComponent-6PlBG", + "id": "TypeConverterComponent-6kqHz", "measured": { - "height": 261, + "height": 262, "width": 320 }, "position": { - "x": 2015.8179098384944, - "y": 984.8081816210474 + "x": 1982.2119289336342, + "y": 949.6746561296028 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "Memory-OBr5W", + "id": "Memory-Esy2a", "node": { "base_classes": [ "DataFrame" @@ -1841,6 +1527,7 @@ "icon": "message-square-more", "key": "Memory", "legacy": false, + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -2131,30 +1818,30 @@ "type": "Memory" }, "dragging": false, - "id": "Memory-OBr5W", + "id": "Memory-Esy2a", "measured": { "height": 217, "width": 320 }, "position": { - "x": 1768.3132819856035, - "y": 639.5677452473304 + "x": 1872.1863138733531, + "y": 662.4809140460989 }, - "selected": true, + "selected": false, "type": "genericNode" } ], "viewport": { - "x": -777.2588770106167, - "y": 42.43614864750725, - "zoom": 0.5481262211197415 + "x": -844.2279175744859, + "y": -37.56256528430163, + "zoom": 0.6546453758419618 } }, "description": "Create a chatbot that saves and references previous messages, enabling the model to maintain context throughout the conversation.", "endpoint_name": null, - "id": "753b0efa-9a02-48ce-b9e8-b09244b1c110", + "id": "c4833c20-c17f-40e2-88d9-34dfed9f1fc2", "is_component": false, - "last_tested_version": "1.4.2", + "last_tested_version": "1.4.3", "name": "Memory Chatbot", "tags": [ "chatbots", From e6bd8f8b103fddcc7a94bd442407ba8729f45b52 Mon Sep 17 00:00:00 2001 From: Yuqi Tang Date: Tue, 24 Jun 2025 08:25:07 -0700 Subject: [PATCH 2/6] update simple agent and research translation loop --- .../Research Translation Loop.json | 280 +- .../starter_projects/Simple Agent.json | 2344 ++++++++--------- 2 files changed, 1322 insertions(+), 1302 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json index d0e90e18c769..65ebcd6aa60f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-7dhXG", + "id": "ChatInput-6hroM", "name": "message", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "search_query", - "id": "ArXivComponent-8YHhw", + "id": "ArXivComponent-XiOmX", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-7dhXG{œdataTypeœ:œChatInputœ,œidœ:œChatInput-7dhXGœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ArXivComponent-8YHhw{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-8YHhwœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-6hroM{œdataTypeœ:œChatInputœ,œidœ:œChatInput-6hroMœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ArXivComponent-XiOmX{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-XiOmXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-7dhXG", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-7dhXGœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "ArXivComponent-8YHhw", - "targetHandle": "{œfieldNameœ: œsearch_queryœ, œidœ: œArXivComponent-8YHhwœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-6hroM", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-6hroMœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "ArXivComponent-XiOmX", + "targetHandle": "{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-XiOmXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "ArXivComponent", - "id": "ArXivComponent-8YHhw", + "id": "ArXivComponent-XiOmX", "name": "dataframe", "output_types": [ "DataFrame" @@ -43,19 +43,19 @@ }, "targetHandle": { "fieldName": "data", - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "inputTypes": [ "DataFrame" ], "type": "other" } }, - "id": "reactflow__edge-ArXivComponent-8YHhw{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-8YHhwœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-LoopComponent-dkM1v{œfieldNameœ:œdataœ,œidœ:œLoopComponent-dkM1vœ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-ArXivComponent-XiOmX{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-XiOmXœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-LoopComponent-T1P62{œfieldNameœ:œdataœ,œidœ:œLoopComponent-T1P62œ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "ArXivComponent-8YHhw", - "sourceHandle": "{œdataTypeœ: œArXivComponentœ, œidœ: œArXivComponent-8YHhwœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "LoopComponent-dkM1v", - "targetHandle": "{œfieldNameœ: œdataœ, œidœ: œLoopComponent-dkM1vœ, œinputTypesœ: [œDataFrameœ], œtypeœ: œotherœ}" + "source": "ArXivComponent-XiOmX", + "sourceHandle": "{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-XiOmXœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}", + "target": "LoopComponent-T1P62", + "targetHandle": "{œfieldNameœ:œdataœ,œidœ:œLoopComponent-T1P62œ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "name": "item", "output_types": [ "Data" @@ -71,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "ParserComponent-uSCVe", + "id": "ParserComponent-9koHg", "inputTypes": [ "DataFrame", "Data" @@ -79,12 +79,12 @@ "type": "other" } }, - "id": "reactflow__edge-LoopComponent-dkM1v{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-dkM1vœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}-ParserComponent-uSCVe{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-uSCVeœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}-ParserComponent-9koHg{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-9koHgœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "LoopComponent-dkM1v", - "sourceHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-dkM1vœ, œnameœ: œitemœ, œoutput_typesœ: [œDataœ]}", - "target": "ParserComponent-uSCVe", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œParserComponent-uSCVeœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "LoopComponent-T1P62", + "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", + "target": "ParserComponent-9koHg", + "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-9koHgœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}" }, { "animated": false, @@ -92,7 +92,7 @@ "data": { "sourceHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "name": "done", "output_types": [ "DataFrame" @@ -100,7 +100,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "TypeConverterComponent-0TFCA", + "id": "TypeConverterComponent-FyeIR", "inputTypes": [ "Message", "Data", @@ -109,12 +109,12 @@ "type": "other" } }, - "id": "reactflow__edge-LoopComponent-dkM1v{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-dkM1vœ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-0TFCA{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-0TFCAœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-FyeIR{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-FyeIRœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "LoopComponent-dkM1v", - "sourceHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-dkM1vœ, œnameœ: œdoneœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "TypeConverterComponent-0TFCA", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-0TFCAœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + "source": "LoopComponent-T1P62", + "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}", + "target": "TypeConverterComponent-FyeIR", + "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-FyeIRœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" }, { "animated": false, @@ -122,7 +122,7 @@ "data": { "sourceHandle": { "dataType": "TypeConverterComponent", - "id": "TypeConverterComponent-0TFCA", + "id": "TypeConverterComponent-FyeIR", "name": "message_output", "output_types": [ "Message" @@ -130,7 +130,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-O3R2b", + "id": "ChatOutput-Owlsl", "inputTypes": [ "Data", "DataFrame", @@ -139,12 +139,12 @@ "type": "other" } }, - "id": "reactflow__edge-TypeConverterComponent-0TFCA{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-0TFCAœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-O3R2b{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-O3R2bœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-TypeConverterComponent-FyeIR{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-FyeIRœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Owlsl{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Owlslœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "TypeConverterComponent-0TFCA", - "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-0TFCAœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-O3R2b", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-O3R2bœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "TypeConverterComponent-FyeIR", + "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-FyeIRœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "ChatOutput-Owlsl", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Owlslœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" }, { "animated": false, @@ -152,7 +152,7 @@ "data": { "sourceHandle": { "dataType": "ParserComponent", - "id": "ParserComponent-uSCVe", + "id": "ParserComponent-9koHg", "name": "parsed_text", "output_types": [ "Message" @@ -160,19 +160,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "LanguageModelComponent-huxaM", + "id": "LanguageModelComponent-JpEyw", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ParserComponent-uSCVe{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-uSCVeœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-huxaM{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-huxaMœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ParserComponent-9koHg{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-9koHgœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-JpEyw{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-JpEywœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ParserComponent-uSCVe", - "sourceHandle": "{œdataTypeœ: œParserComponentœ, œidœ: œParserComponent-uSCVeœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-huxaM", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-huxaMœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ParserComponent-9koHg", + "sourceHandle": "{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-9koHgœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}", + "target": "LanguageModelComponent-JpEyw", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-JpEywœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" }, { "animated": false, @@ -180,61 +180,63 @@ "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-huxaM", + "id": "LanguageModelComponent-JpEyw", "name": "text_output", "output_types": [ "Message" ] }, "targetHandle": { - "fieldName": "message", - "id": "MessagetoData-uaDsB", + "fieldName": "input_data", + "id": "TypeConverterComponent-shkBL", "inputTypes": [ - "Message" + "Message", + "Data", + "DataFrame" ], - "type": "str" + "type": "other" } }, - "id": "reactflow__edge-LanguageModelComponent-huxaM{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-huxaMœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-MessagetoData-uaDsB{œfieldNameœ:œmessageœ,œidœ:œMessagetoData-uaDsBœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__LanguageModelComponent-JpEyw{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-JpEywœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-TypeConverterComponent-shkBL{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-shkBLœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "LanguageModelComponent-huxaM", - "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-huxaMœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "MessagetoData-uaDsB", - "targetHandle": "{œfieldNameœ: œmessageœ, œidœ: œMessagetoData-uaDsBœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "LanguageModelComponent-JpEyw", + "sourceHandle": "{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-JpEywœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "TypeConverterComponent-shkBL", + "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-shkBLœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { - "dataType": "MessagetoData", - "id": "MessagetoData-uaDsB", - "name": "data", + "dataType": "TypeConverterComponent", + "id": "TypeConverterComponent-shkBL", + "name": "data_output", "output_types": [ "Data" ] }, "targetHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "name": "item", "output_types": [ "Data" ] } }, - "id": "reactflow__edge-MessagetoData-uaDsB{œdataTypeœ:œMessagetoDataœ,œidœ:œMessagetoData-uaDsBœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-LoopComponent-dkM1v{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-dkM1vœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", + "id": "xy-edge__TypeConverterComponent-shkBL{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-shkBLœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", "selected": false, - "source": "MessagetoData-uaDsB", - "sourceHandle": "{œdataTypeœ: œMessagetoDataœ, œidœ: œMessagetoData-uaDsBœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}", - "target": "LoopComponent-dkM1v", - "targetHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-dkM1vœ, œnameœ: œitemœ, œoutput_typesœ: [œDataœ]}" + "source": "TypeConverterComponent-shkBL", + "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-shkBLœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}", + "target": "LoopComponent-T1P62", + "targetHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}" } ], "nodes": [ { "data": { - "id": "ArXivComponent-8YHhw", + "id": "ArXivComponent-XiOmX", "node": { "base_classes": [ "DataFrame" @@ -371,9 +373,9 @@ "type": "ArXivComponent" }, "dragging": false, - "id": "ArXivComponent-8YHhw", + "id": "ArXivComponent-XiOmX", "measured": { - "height": 368, + "height": 369, "width": 320 }, "position": { @@ -385,7 +387,7 @@ }, { "data": { - "id": "ChatOutput-O3R2b", + "id": "ChatOutput-Owlsl", "node": { "base_classes": [ "Message" @@ -680,7 +682,7 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-O3R2b", + "id": "ChatOutput-Owlsl", "measured": { "height": 48, "width": 192 @@ -694,7 +696,7 @@ }, { "data": { - "id": "ChatInput-7dhXG", + "id": "ChatInput-6hroM", "node": { "base_classes": [ "Message" @@ -992,9 +994,9 @@ "type": "ChatInput" }, "dragging": false, - "id": "ChatInput-7dhXG", + "id": "ChatInput-6hroM", "measured": { - "height": 203, + "height": 204, "width": 320 }, "position": { @@ -1006,7 +1008,7 @@ }, { "data": { - "id": "note-A2mJA", + "id": "note-Unlpq", "node": { "description": "# **Langflow Loop Component Template - ArXiv search result Translator** \nThis template translates research paper summaries on ArXiv into Portuguese and summarizes them. \n Using **Langflow’s looping mechanism**, the template iterates through multiple research papers, translates them with the **OpenAI** model component, and outputs an aggregated version of all translated papers. \n\n## Quickstart \n 1. Add your OpenAI API key to the **Language Model** component. \n2. In the **Playground**, enter a query related to a research topic (for example, “Quantum Computing Advancements”). \n\n The flow fetches a list of research papers from ArXiv matching the query. Each paper in the retrieved list is processed one-by-one using the Langflow **Loop component**. \n\n The abstract of each paper is translated into Portuguese by the **OpenAI** model component. \n\n Once all papers are translated, the system aggregates them into a **single structured output**.", "display_name": "", @@ -1017,7 +1019,7 @@ }, "dragging": false, "height": 647, - "id": "note-A2mJA", + "id": "note-Unlpq", "measured": { "height": 647, "width": 576 @@ -1033,7 +1035,7 @@ }, { "data": { - "id": "ParserComponent-uSCVe", + "id": "ParserComponent-9koHg", "node": { "base_classes": [ "Message" @@ -1192,9 +1194,9 @@ "type": "ParserComponent" }, "dragging": false, - "id": "ParserComponent-uSCVe", + "id": "ParserComponent-9koHg", "measured": { - "height": 328, + "height": 329, "width": 320 }, "position": { @@ -1206,7 +1208,7 @@ }, { "data": { - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "node": { "base_classes": [ "Data", @@ -1307,9 +1309,9 @@ "type": "LoopComponent" }, "dragging": false, - "id": "LoopComponent-dkM1v", + "id": "LoopComponent-T1P62", "measured": { - "height": 241, + "height": 242, "width": 320 }, "position": { @@ -1321,7 +1323,7 @@ }, { "data": { - "id": "TypeConverterComponent-0TFCA", + "id": "TypeConverterComponent-FyeIR", "node": { "base_classes": [ "Message" @@ -1435,7 +1437,7 @@ "type": "TypeConverterComponent" }, "dragging": false, - "id": "TypeConverterComponent-0TFCA", + "id": "TypeConverterComponent-FyeIR", "measured": { "height": 262, "width": 320 @@ -1449,7 +1451,7 @@ }, { "data": { - "id": "LanguageModelComponent-huxaM", + "id": "LanguageModelComponent-JpEyw", "node": { "base_classes": [ "LanguageModel", @@ -1492,6 +1494,8 @@ "group_outputs": false, "method": "text_response", "name": "text_output", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1506,6 +1510,8 @@ "group_outputs": false, "method": "build_model", "name": "model_output", + "options": null, + "required_inputs": null, "selected": "LanguageModel", "tool_mode": true, "types": [ @@ -1587,13 +1593,16 @@ "info": "Select the model to use", "name": "model_name", "options": [ - "claude-opus-4-20250514", - "claude-sonnet-4-20250514", - "claude-3-7-sonnet-latest", - "claude-3-5-sonnet-latest", - "claude-3-5-haiku-latest", - "claude-3-opus-latest", - "claude-3-sonnet-20240229" + "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" ], "options_metadata": [], "placeholder": "", @@ -1720,9 +1729,9 @@ "type": "LanguageModelComponent" }, "dragging": false, - "id": "LanguageModelComponent-huxaM", + "id": "LanguageModelComponent-JpEyw", "measured": { - "height": 532, + "height": 534, "width": 320 }, "position": { @@ -1734,26 +1743,27 @@ }, { "data": { - "id": "MessagetoData-uaDsB", + "id": "TypeConverterComponent-shkBL", "node": { "base_classes": [ - "Data" + "Message" ], - "beta": true, + "beta": false, "category": "processing", "conditional_paths": [], "custom_fields": {}, - "description": "Convert a Message object to a Data object", - "display_name": "Message to Data", + "description": "Convert between different types (Message, Data, DataFrame)", + "display_name": "Type Convert", "documentation": "", "edited": false, "field_order": [ - "message" + "input_data", + "output_type" ], "frozen": false, - "icon": "message-square-share", - "key": "MessagetoData", - "legacy": true, + "icon": "repeat", + "key": "TypeConverterComponent", + "legacy": false, "metadata": {}, "minimized": false, "output_types": [], @@ -1761,10 +1771,13 @@ { "allows_loop": false, "cache": true, - "display_name": "Data", + "display_name": "Data Output", "group_outputs": false, - "method": "convert_message_to_data", - "name": "data", + "hidden": null, + "method": "convert_to_data", + "name": "data_output", + "options": null, + "required_inputs": null, "selected": "Data", "tool_mode": true, "types": [ @@ -1774,7 +1787,7 @@ } ], "pinned": false, - "score": 0.008222426499470714, + "score": 0.007568328950209746, "template": { "_type": "Component", "code": { @@ -1793,60 +1806,81 @@ "show": true, "title_case": false, "type": "code", - "value": "from loguru import logger\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.io import MessageInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.message import Message\n\n\nclass MessageToDataComponent(Component):\n display_name = \"Message to Data\"\n description = \"Convert a Message object to a Data object\"\n icon = \"message-square-share\"\n beta = True\n name = \"MessagetoData\"\n legacy = True\n\n inputs = [\n MessageInput(\n name=\"message\",\n display_name=\"Message\",\n info=\"The Message object to convert to a Data object\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"convert_message_to_data\"),\n ]\n\n def convert_message_to_data(self) -> Data:\n if isinstance(self.message, Message):\n # Convert Message to Data\n return Data(data=self.message.data)\n\n msg = \"Error converting Message to Data: Input must be a Message object\"\n logger.opt(exception=True).debug(msg)\n self.status = msg\n return Data(data={\"error\": msg})\n" + "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 = [\n Output(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n )\n ]\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(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n ).to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"Data Output\",\n name=\"data_output\",\n method=\"convert_to_data\",\n ).to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\",\n name=\"dataframe_output\",\n 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 input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_message(input_value)\n self.status = result\n return result\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_data(input_value)\n self.status = result\n return result\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_dataframe(input_value)\n self.status = result\n return result\n" }, - "message": { - "_input_type": "MessageInput", + "input_data": { + "_input_type": "HandleInput", "advanced": false, - "display_name": "Message", + "display_name": "Input", "dynamic": false, - "info": "The Message object to convert to a Data object", + "info": "Accept Message, Data or DataFrame as input", "input_types": [ - "Message" + "Message", + "Data", + "DataFrame" ], "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "message", + "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_input": true, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "tab", + "value": "Data" } }, "tool_mode": false }, "showNode": true, - "type": "MessagetoData" + "type": "TypeConverterComponent" }, "dragging": false, - "id": "MessagetoData-uaDsB", + "id": "TypeConverterComponent-shkBL", "measured": { - "height": 203, + "height": 262, "width": 320 }, "position": { - "x": 1836.3049922790483, - "y": 411.76970008520357 + "x": 1842.690896003052, + "y": 374.08100578248695 }, - "selected": false, + "selected": true, "type": "genericNode" } ], "viewport": { - "x": 269.23666655845534, - "y": 307.2875075099402, - "zoom": 0.46271902027504264 + "x": 446.66109209865874, + "y": 293.1638977007619, + "zoom": 0.42727671233908193 } }, "description": "This template iterates over search results using LoopComponent and translates each result into Portuguese automatically. 🚀", "endpoint_name": null, - "id": "07f1c000-7ebe-4655-8421-06e012fb6da6", + "id": "df6cb896-ee70-4429-bf5b-dbb2ac7669b9", "is_component": false, "last_tested_version": "1.4.3", "name": "Research Translation Loop", 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 c0a984bbe758..4dce4bbfdd31 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 @@ -3,142 +3,195 @@ "edges": [ { "animated": false, + "className": "", "data": { "sourceHandle": { - "dataType": "Agent", - "id": "Agent-qYZ9W", - "name": "response", + "dataType": "CalculatorComponent", + "id": "CalculatorComponent-Sp3Ca", + "name": "component_as_tool", "output_types": [ - "Message" + "Tool" ] }, "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-aHj8n", + "fieldName": "tools", + "id": "Agent-xD3uW", "inputTypes": [ - "Data", - "DataFrame", - "Message" + "Tool" ], "type": "other" } }, - "id": "xy-edge__Agent-qYZ9W{œdataTypeœ:œAgentœ,œidœ:œAgent-qYZ9Wœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-aHj8n{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-aHj8nœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "xy-edge__CalculatorComponent-Sp3Ca{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-Sp3Caœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-xD3uW{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "Agent-qYZ9W", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-qYZ9Wœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-aHj8n", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-aHj8nœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "CalculatorComponent-Sp3Ca", + "sourceHandle": "{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-Sp3Caœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}", + "target": "Agent-xD3uW", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { - "dataType": "CalculatorComponent", - "id": "CalculatorComponent-15FSE", - "name": "component_as_tool", + "dataType": "ChatInput", + "id": "ChatInput-S6AhV", + "name": "message", "output_types": [ - "Tool" + "Message" ] }, "targetHandle": { - "fieldName": "tools", - "id": "Agent-qYZ9W", + "fieldName": "input_value", + "id": "Agent-xD3uW", "inputTypes": [ - "Tool" + "Message" ], - "type": "other" + "type": "str" } }, - "id": "xy-edge__CalculatorComponent-15FSE{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-15FSEœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-qYZ9W{œfieldNameœ:œtoolsœ,œidœ:œAgent-qYZ9Wœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "xy-edge__ChatInput-S6AhV{œdataTypeœ:œChatInputœ,œidœ:œChatInput-S6AhVœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-xD3uW{œfieldNameœ:œinput_valueœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "CalculatorComponent-15FSE", - "sourceHandle": "{œdataTypeœ: œCalculatorComponentœ, œidœ: œCalculatorComponent-15FSEœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-qYZ9W", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-qYZ9Wœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "ChatInput-S6AhV", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-S6AhVœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "Agent-xD3uW", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { - "dataType": "URL", - "id": "URL-o7GEq", - "name": "component_as_tool", + "dataType": "Agent", + "id": "Agent-xD3uW", + "name": "response", "output_types": [ - "Tool" + "Message" ] }, "targetHandle": { - "fieldName": "tools", - "id": "Agent-qYZ9W", + "fieldName": "input_value", + "id": "ChatOutput-WciiI", "inputTypes": [ - "Tool" + "Data", + "DataFrame", + "Message" ], "type": "other" } }, - "id": "xy-edge__URL-o7GEq{œdataTypeœ:œURLœ,œidœ:œURL-o7GEqœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-qYZ9W{œfieldNameœ:œtoolsœ,œidœ:œAgent-qYZ9Wœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "xy-edge__Agent-xD3uW{œdataTypeœ:œAgentœ,œidœ:œAgent-xD3uWœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-WciiI{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-WciiIœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "URL-o7GEq", - "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-o7GEqœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-qYZ9W", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-qYZ9Wœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "Agent-xD3uW", + "sourceHandle": "{œdataTypeœ:œAgentœ,œidœ:œAgent-xD3uWœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}", + "target": "ChatOutput-WciiI", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-WciiIœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" }, { + "animated": false, + "className": "", "data": { "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-2YUmA", - "name": "message", + "dataType": "URLComponent", + "id": "URLComponent-6fRXe", + "name": "component_as_tool", "output_types": [ - "Message" + "Tool" ] }, "targetHandle": { - "fieldName": "input_value", - "id": "Agent-qYZ9W", + "fieldName": "tools", + "id": "Agent-xD3uW", "inputTypes": [ - "Message" + "Tool" ], - "type": "str" + "type": "other" } }, - "id": "xy-edge__ChatInput-2YUmA{œdataTypeœ:œChatInputœ,œidœ:œChatInput-2YUmAœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-qYZ9W{œfieldNameœ:œinput_valueœ,œidœ:œAgent-qYZ9Wœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "source": "ChatInput-2YUmA", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-2YUmAœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-qYZ9W", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-qYZ9Wœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "id": "xy-edge__URLComponent-6fRXe{œdataTypeœ:œURLComponentœ,œidœ:œURLComponent-6fRXeœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-xD3uW{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "URLComponent-6fRXe", + "sourceHandle": "{œdataTypeœ:œURLComponentœ,œidœ:œURLComponent-6fRXeœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}", + "target": "Agent-xD3uW", + "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" } ], "nodes": [ { "data": { - "description": "Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "URL", - "id": "URL-o7GEq", + "id": "note-ukwn1", + "node": { + "description": "# 📖 README\nRun an Agent with URL and Calculator tools available for its use. \nThe Agent decides which tool to use to solve a problem.\n## Quick start\n\n1. Add your OpenAI API key to the Agent.\n2. Open the Playground and chat with the Agent. Request some information about a recipe, and then ask to add two numbers together. In the responses, the Agent will use different tools to solve different problems.\n\n## Next steps\nConnect more tools to the Agent to create your perfect assistant.\n\nFor more, see the [Langflow docs](https://docs.langflow.org/agents-tool-calling-agent-component).", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "neutral" + } + }, + "type": "note" + }, + "dragging": false, + "id": "note-ukwn1", + "measured": { + "height": 630, + "width": 575 + }, + "position": { + "x": 775.5268622081468, + "y": 27.927425537464444 + }, + "selected": false, + "type": "noteNode" + }, + { + "data": { + "id": "note-sQfYZ", + "node": { + "description": "### 💡 Add your OpenAI API key here👇", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "transparent" + } + }, + "type": "note" + }, + "dragging": false, + "id": "note-sQfYZ", + "measured": { + "height": 324, + "width": 324 + }, + "position": { + "x": 1758.8741678279712, + "y": 184.362412184038 + }, + "selected": false, + "type": "noteNode" + }, + { + "data": { + "id": "CalculatorComponent-Sp3Ca", "node": { "base_classes": [ - "Data", - "DataFrame", - "Message" + "Data" ], "beta": false, + "category": "tools", "conditional_paths": [], "custom_fields": {}, - "description": "Fetch content from one or more web pages, following links recursively.", - "display_name": "URL", + "description": "Perform basic arithmetic operations on a given expression.", + "display_name": "Calculator", "documentation": "", "edited": false, "field_order": [ - "urls", - "format", - "separator", - "clean_extra_whitespace" + "expression" ], "frozen": false, - "icon": "layout-template", + "icon": "calculator", + "key": "CalculatorComponent", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, "minimized": false, "output_types": [], @@ -147,6 +200,7 @@ "allows_loop": false, "cache": true, "display_name": "Toolset", + "group_outputs": false, "hidden": false, "method": "to_toolkit", "name": "component_as_tool", @@ -161,44 +215,9 @@ } ], "pinned": false, + "score": 0.001, "template": { "_type": "Component", - "autoset_encoding": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Autoset Encoding", - "dynamic": false, - "info": "If enabled, automatically sets the encoding of the request.", - "list": false, - "list_add_label": "Add More", - "name": "autoset_encoding", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "check_response_status": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Check Response Status", - "dynamic": false, - "info": "If enabled, checks the response status of the request.", - "list": false, - "list_add_label": "Add More", - "name": "check_response_status", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, "code": { "advanced": true, "dynamic": true, @@ -215,58 +234,40 @@ "show": true, "title_case": false, "type": "code", - "value": "import re\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom langchain_community.document_loaders import RecursiveUrlLoader\nfrom loguru import logger\n\nfrom langflow.custom import Component\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.helpers.data import safe_convert\nfrom langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SliderInput, TableInput\nfrom langflow.schema import DataFrame, Message\nfrom langflow.services.deps import get_settings_service\n\n# Constants\nDEFAULT_TIMEOUT = 30\nDEFAULT_MAX_DEPTH = 1\nDEFAULT_FORMAT = \"Text\"\nURL_REGEX = re.compile(\n r\"^(https?:\\/\\/)?\" r\"(www\\.)?\" r\"([a-zA-Z0-9.-]+)\" r\"(\\.[a-zA-Z]{2,})?\" r\"(:\\d+)?\" r\"(\\/[^\\s]*)?$\",\n re.IGNORECASE,\n)\n\n\nclass URLComponent(Component):\n \"\"\"A component that loads and parses content from web pages recursively.\n\n This component allows fetching content from one or more URLs, with options to:\n - Control crawl depth\n - Prevent crawling outside the root domain\n - Use async loading for better performance\n - Extract either raw HTML or clean text\n - Configure request headers and timeouts\n \"\"\"\n\n display_name = \"URL\"\n description = \"Fetch content from one or more web pages, following links recursively.\"\n icon = \"layout-template\"\n name = \"URLComponent\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs to crawl recursively, by clicking the '+' button.\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n input_types=[],\n ),\n SliderInput(\n name=\"max_depth\",\n display_name=\"Depth\",\n info=(\n \"Controls how many 'clicks' away from the initial page the crawler will go:\\n\"\n \"- depth 1: only the initial page\\n\"\n \"- depth 2: initial page + all pages linked directly from it\\n\"\n \"- depth 3: initial page + direct links + links found on those direct link pages\\n\"\n \"Note: This is about link traversal, not URL path depth.\"\n ),\n value=DEFAULT_MAX_DEPTH,\n range_spec=RangeSpec(min=1, max=5, step=1),\n required=False,\n min_label=\" \",\n max_label=\" \",\n min_label_icon=\"None\",\n max_label_icon=\"None\",\n # slider_input=True\n ),\n BoolInput(\n name=\"prevent_outside\",\n display_name=\"Prevent Outside\",\n info=(\n \"If enabled, only crawls URLs within the same domain as the root URL. \"\n \"This helps prevent the crawler from going to external websites.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"use_async\",\n display_name=\"Use Async\",\n info=(\n \"If enabled, uses asynchronous loading which can be significantly faster \"\n \"but might use more system resources.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.\",\n options=[\"Text\", \"HTML\"],\n value=DEFAULT_FORMAT,\n advanced=True,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"Timeout for the request in seconds.\",\n value=DEFAULT_TIMEOUT,\n required=False,\n advanced=True,\n ),\n TableInput(\n name=\"headers\",\n display_name=\"Headers\",\n info=\"The headers to send with the request\",\n table_schema=[\n {\n \"name\": \"key\",\n \"display_name\": \"Header\",\n \"type\": \"str\",\n \"description\": \"Header name\",\n },\n {\n \"name\": \"value\",\n \"display_name\": \"Value\",\n \"type\": \"str\",\n \"description\": \"Header value\",\n },\n ],\n value=[{\"key\": \"User-Agent\", \"value\": get_settings_service().settings.user_agent}],\n advanced=True,\n input_types=[\"DataFrame\"],\n ),\n BoolInput(\n name=\"filter_text_html\",\n display_name=\"Filter Text/HTML\",\n info=\"If enabled, filters out text/css content type from the results.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"continue_on_failure\",\n display_name=\"Continue on Failure\",\n info=\"If enabled, continues crawling even if some requests fail.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"check_response_status\",\n display_name=\"Check Response Status\",\n info=\"If enabled, checks the response status of the request.\",\n value=False,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"autoset_encoding\",\n display_name=\"Autoset Encoding\",\n info=\"If enabled, automatically sets the encoding of the request.\",\n value=True,\n required=False,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Result\", name=\"page_results\", method=\"fetch_content\"),\n Output(display_name=\"Raw Result\", name=\"raw_results\", method=\"as_message\"),\n ]\n\n @staticmethod\n def validate_url(url: str) -> bool:\n \"\"\"Validates if the given string matches URL pattern.\n\n Args:\n url: The URL string to validate\n\n Returns:\n bool: True if the URL is valid, False otherwise\n \"\"\"\n return bool(URL_REGEX.match(url))\n\n def ensure_url(self, url: str) -> str:\n \"\"\"Ensures the given string is a valid URL.\n\n Args:\n url: The URL string to validate and normalize\n\n Returns:\n str: The normalized URL\n\n Raises:\n ValueError: If the URL is invalid\n \"\"\"\n url = url.strip()\n if not url.startswith((\"http://\", \"https://\")):\n url = \"https://\" + url\n\n if not self.validate_url(url):\n msg = f\"Invalid URL: {url}\"\n raise ValueError(msg)\n\n return url\n\n def _create_loader(self, url: str) -> RecursiveUrlLoader:\n \"\"\"Creates a RecursiveUrlLoader instance with the configured settings.\n\n Args:\n url: The URL to load\n\n Returns:\n RecursiveUrlLoader: Configured loader instance\n \"\"\"\n headers_dict = {header[\"key\"]: header[\"value\"] for header in self.headers}\n extractor = (lambda x: x) if self.format == \"HTML\" else (lambda x: BeautifulSoup(x, \"lxml\").get_text())\n\n return RecursiveUrlLoader(\n url=url,\n max_depth=self.max_depth,\n prevent_outside=self.prevent_outside,\n use_async=self.use_async,\n extractor=extractor,\n timeout=self.timeout,\n headers=headers_dict,\n check_response_status=self.check_response_status,\n continue_on_failure=self.continue_on_failure,\n base_url=url, # Add base_url to ensure consistent domain crawling\n autoset_encoding=self.autoset_encoding, # Enable automatic encoding detection\n exclude_dirs=[], # Allow customization of excluded directories\n link_regex=None, # Allow customization of link filtering\n )\n\n def fetch_url_contents(self) -> list[dict]:\n \"\"\"Load documents from the configured URLs.\n\n Returns:\n List[Data]: List of Data objects containing the fetched content\n\n Raises:\n ValueError: If no valid URLs are provided or if there's an error loading documents\n \"\"\"\n try:\n urls = list({self.ensure_url(url) for url in self.urls if url.strip()})\n logger.info(f\"URLs: {urls}\")\n if not urls:\n msg = \"No valid URLs provided.\"\n raise ValueError(msg)\n\n all_docs = []\n for url in urls:\n logger.info(f\"Loading documents from {url}\")\n\n try:\n loader = self._create_loader(url)\n docs = loader.load()\n\n if not docs:\n logger.warning(f\"No documents found for {url}\")\n continue\n\n logger.info(f\"Found {len(docs)} documents from {url}\")\n all_docs.extend(docs)\n\n except requests.exceptions.RequestException as e:\n logger.exception(f\"Error loading documents from {url}: {e}\")\n continue\n\n if not all_docs:\n msg = \"No documents were successfully loaded from any URL\"\n raise ValueError(msg)\n\n # data = [Data(text=doc.page_content, **doc.metadata) for doc in all_docs]\n data = [\n {\n \"text\": safe_convert(doc.page_content, clean_data=True),\n \"url\": doc.metadata.get(\"source\", \"\"),\n \"title\": doc.metadata.get(\"title\", \"\"),\n \"description\": doc.metadata.get(\"description\", \"\"),\n \"content_type\": doc.metadata.get(\"content_type\", \"\"),\n \"language\": doc.metadata.get(\"language\", \"\"),\n }\n for doc in all_docs\n ]\n except Exception as e:\n error_msg = e.message if hasattr(e, \"message\") else e\n msg = f\"Error loading documents: {error_msg!s}\"\n logger.exception(msg)\n raise ValueError(msg) from e\n return data\n\n def fetch_content(self) -> DataFrame:\n \"\"\"Convert the documents to a DataFrame.\"\"\"\n return DataFrame(data=self.fetch_url_contents())\n\n def as_message(self) -> Message:\n \"\"\"Convert the documents to a Message.\"\"\"\n url_contents = self.fetch_url_contents()\n return Message(text=\"\\n\\n\".join([x[\"text\"] for x in url_contents]), data={\"data\": url_contents})\n" + "value": "import ast\nimport operator\nfrom collections.abc import Callable\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import MessageTextInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\n\n\nclass CalculatorComponent(Component):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n\n # Cache operators dictionary as a class variable\n OPERATORS: dict[type[ast.operator], Callable] = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"result\", type_=Data, method=\"evaluate_expression\"),\n ]\n\n def _eval_expr(self, node: ast.AST) -> float:\n \"\"\"Evaluate an AST node recursively.\"\"\"\n if isinstance(node, ast.Constant):\n if isinstance(node.value, int | float):\n return float(node.value)\n error_msg = f\"Unsupported constant type: {type(node.value).__name__}\"\n raise TypeError(error_msg)\n if isinstance(node, ast.Num): # For backwards compatibility\n if isinstance(node.n, int | float):\n return float(node.n)\n error_msg = f\"Unsupported number type: {type(node.n).__name__}\"\n raise TypeError(error_msg)\n\n if isinstance(node, ast.BinOp):\n op_type = type(node.op)\n if op_type not in self.OPERATORS:\n error_msg = f\"Unsupported binary operator: {op_type.__name__}\"\n raise TypeError(error_msg)\n\n left = self._eval_expr(node.left)\n right = self._eval_expr(node.right)\n return self.OPERATORS[op_type](left, right)\n\n error_msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(error_msg)\n\n def evaluate_expression(self) -> Data:\n \"\"\"Evaluate the mathematical expression and return the result.\"\"\"\n try:\n tree = ast.parse(self.expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n formatted_result = f\"{float(result):.6f}\".rstrip(\"0\").rstrip(\".\")\n self.log(f\"Calculation result: {formatted_result}\")\n\n self.status = formatted_result\n return Data(data={\"result\": formatted_result})\n\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n except (SyntaxError, TypeError, KeyError, ValueError, AttributeError, OverflowError) as e:\n error_message = f\"Invalid expression: {e!s}\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n def build(self):\n \"\"\"Return the main evaluation function.\"\"\"\n return self.evaluate_expression\n" }, - "continue_on_failure": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Continue on Failure", + "expression": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Expression", "dynamic": false, - "info": "If enabled, continues crawling even if some requests fail.", + "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", + "input_types": [ + "Message" + ], "list": false, "list_add_label": "Add More", - "name": "continue_on_failure", + "load_from_db": false, + "name": "expression", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": false, + "tool_mode": true, + "trace_as_input": true, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "str", + "value": "" }, - "filter_text_html": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Filter Text/HTML", + "tools_metadata": { + "_input_type": "ToolsInput", + "advanced": false, + "display_name": "Actions", "dynamic": false, - "info": "If enabled, filters out text/css content type from the results.", - "list": false, + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", + "is_list": true, "list_add_label": "Add More", - "name": "filter_text_html", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "format": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Output Format", - "dynamic": false, - "info": "Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.", - "name": "format", - "options": [ - "Text", - "HTML" - ], - "options_metadata": [], + "name": "tools_metadata", "placeholder": "", "real_time_refresh": true, "required": false, @@ -274,484 +275,376 @@ "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "Text" - }, - "headers": { - "_input_type": "TableInput", - "advanced": true, - "display_name": "Headers", - "dynamic": false, - "info": "The headers to send with the request", - "input_types": [ - "DataFrame" - ], - "is_list": true, - "list_add_label": "Add More", - "name": "headers", - "placeholder": "", - "required": false, - "show": true, - "table_icon": "Table", - "table_schema": { - "columns": [ - { - "default": "None", - "description": "Header name", - "disable_edit": false, - "display_name": "Header", - "edit_mode": "popover", - "filterable": true, - "formatter": "text", - "hidden": false, - "name": "key", - "sortable": true, - "type": "str" + "type": "tools", + "value": [ + { + "args": { + "expression": { + "default": "", + "description": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", + "title": "Expression", + "type": "string" + } }, - { - "default": "None", - "description": "Header value", - "disable_edit": false, - "display_name": "Value", - "edit_mode": "popover", - "filterable": true, - "formatter": "text", - "hidden": false, - "name": "value", - "sortable": true, - "type": "str" - } - ] - }, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "trigger_icon": "Table", - "trigger_text": "Open table", - "type": "table", - "value": [ - { - "key": "User-Agent", - "value": "langflow" - } - ] - }, - "max_depth": { - "_input_type": "SliderInput", - "advanced": false, - "display_name": "Depth", - "dynamic": false, - "info": "Controls how many 'clicks' away from the initial page the crawler will go:\n- depth 1: only the initial page\n- depth 2: initial page + all pages linked directly from it\n- depth 3: initial page + direct links + links found on those direct link pages\nNote: This is about link traversal, not URL path depth.", - "max_label": " ", - "max_label_icon": "None", - "min_label": " ", - "min_label_icon": "None", - "name": "max_depth", - "placeholder": "", - "range_spec": { - "max": 5.0, - "min": 1.0, - "step": 1.0, - "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": 1 - }, - "prevent_outside": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Prevent Outside", - "dynamic": false, - "info": "If enabled, only crawls URLs within the same domain as the root URL. This helps prevent the crawler from going to external websites.", - "list": false, - "list_add_label": "Add More", - "name": "prevent_outside", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "timeout": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Timeout", - "dynamic": false, - "info": "Timeout for the request in seconds.", - "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": 30 - }, - "tools_metadata": { - "_input_type": "ToolsInput", - "advanced": false, - "display_name": "Actions", - "dynamic": false, - "info": "Modify tool names and descriptions to help agents understand when to use each tool.", - "is_list": true, - "list_add_label": "Add More", - "name": "tools_metadata", - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "tools", - "value": [ - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. fetch_content() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. fetch_content() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "fetch_content", - "name": "fetch_content", - "status": true, - "tags": [ - "fetch_content" - ] - }, - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. fetch_content_text() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. fetch_content_text() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "fetch_content_text", - "name": "fetch_content_text", - "status": true, - "tags": [ - "fetch_content_text" - ] - }, - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. as_dataframe() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. as_dataframe() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "as_dataframe", - "name": "as_dataframe", + "description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", + "display_description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", + "display_name": "evaluate_expression", + "name": "evaluate_expression", "status": true, "tags": [ - "as_dataframe" + "evaluate_expression" ] } ] - }, - "urls": { - "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "URLs", - "dynamic": false, - "info": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", - "input_types": [], - "list": true, - "list_add_label": "Add URL", - "load_from_db": false, - "name": "urls", - "placeholder": "Enter a URL...", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "use_async": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Use Async", - "dynamic": false, - "info": "If enabled, uses asynchronous loading which can be significantly faster but might use more system resources.", - "list": false, - "list_add_label": "Add More", - "name": "use_async", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true } }, "tool_mode": true }, "selected_output": "component_as_tool", - "type": "URL" + "showNode": true, + "type": "CalculatorComponent" }, "dragging": false, - "id": "URL-o7GEq", + "id": "CalculatorComponent-Sp3Ca", "measured": { - "height": 523, + "height": 217, "width": 320 }, "position": { - "x": 1236.8269016193576, - "y": -173.8644169438124 + "x": 1233.166256931297, + "y": 514.7544001650839 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "note-wtgw9", - "node": { - "description": "# 📖 README\nRun an Agent with URL and Calculator tools available for its use. \nThe Agent decides which tool to use to solve a problem.\n## Quick start\n\n1. Add your OpenAI API key to the Agent.\n2. Open the Playground and chat with the Agent. Request some information about a recipe, and then ask to add two numbers together. In the responses, the Agent will use different tools to solve different problems.\n\n## Next steps\nConnect more tools to the Agent to create your perfect assistant.\n\nFor more, see the [Langflow docs](https://docs.langflow.org/agents-tool-calling-agent-component).", - "display_name": "", - "documentation": "", - "template": { - "backgroundColor": "neutral" - } - }, - "type": "note" - }, - "dragging": false, - "id": "note-wtgw9", - "measured": { - "height": 630, - "width": 325 - }, - "position": { - "x": 775.5268622081468, - "y": 27.927425537464444 - }, - "selected": false, - "type": "noteNode" - }, - { - "data": { - "id": "note-d6QWe", - "node": { - "description": "### 💡 Add your OpenAI API key here👇", - "display_name": "", - "documentation": "", - "template": { - "backgroundColor": "transparent" - } - }, - "type": "note" - }, - "id": "note-d6QWe", - "measured": { - "height": 324, - "width": 324 - }, - "position": { - "x": 1648.6876745095624, - "y": 253.8646618156497 - }, - "selected": false, - "type": "noteNode" - }, - { - "data": { - "id": "CalculatorComponent-15FSE", + "id": "ChatInput-S6AhV", "node": { "base_classes": [ - "Data" + "Message" ], "beta": false, - "category": "tools", + "category": "inputs", "conditional_paths": [], "custom_fields": {}, - "description": "Perform basic arithmetic operations on a given expression.", - "display_name": "Calculator", + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", "documentation": "", "edited": false, "field_order": [ - "expression" + "input_value", + "should_store_message", + "sender", + "sender_name", + "session_id", + "files", + "background_color", + "chat_icon", + "text_color" ], "frozen": false, - "icon": "calculator", - "key": "CalculatorComponent", + "icon": "MessagesSquare", + "key": "ChatInput", "legacy": false, - "lf_version": "1.2.0", + "lf_version": "1.4.3", "metadata": {}, - "minimized": false, + "minimized": true, "output_types": [], "outputs": [ { "allows_loop": false, "cache": true, - "display_name": "Toolset", - "hidden": false, - "method": "to_toolkit", - "name": "component_as_tool", - "options": null, - "required_inputs": null, - "selected": "Tool", + "display_name": "Chat Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "selected": "Message", "tool_mode": true, "types": [ - "Tool" + "Message" ], "value": "__UNDEFINED__" } ], "pinned": false, - "score": 0.001, + "score": 0.0020353564437605998, "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": "", + "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.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": "code", - "password": false, + "name": "input_value", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "code", - "value": "import ast\nimport operator\nfrom collections.abc import Callable\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import MessageTextInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\n\n\nclass CalculatorComponent(Component):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n\n # Cache operators dictionary as a class variable\n OPERATORS: dict[type[ast.operator], Callable] = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"result\", type_=Data, method=\"evaluate_expression\"),\n ]\n\n def _eval_expr(self, node: ast.AST) -> float:\n \"\"\"Evaluate an AST node recursively.\"\"\"\n if isinstance(node, ast.Constant):\n if isinstance(node.value, int | float):\n return float(node.value)\n error_msg = f\"Unsupported constant type: {type(node.value).__name__}\"\n raise TypeError(error_msg)\n if isinstance(node, ast.Num): # For backwards compatibility\n if isinstance(node.n, int | float):\n return float(node.n)\n error_msg = f\"Unsupported number type: {type(node.n).__name__}\"\n raise TypeError(error_msg)\n\n if isinstance(node, ast.BinOp):\n op_type = type(node.op)\n if op_type not in self.OPERATORS:\n error_msg = f\"Unsupported binary operator: {op_type.__name__}\"\n raise TypeError(error_msg)\n\n left = self._eval_expr(node.left)\n right = self._eval_expr(node.right)\n return self.OPERATORS[op_type](left, right)\n\n error_msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(error_msg)\n\n def evaluate_expression(self) -> Data:\n \"\"\"Evaluate the mathematical expression and return the result.\"\"\"\n try:\n tree = ast.parse(self.expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n formatted_result = f\"{float(result):.6f}\".rstrip(\"0\").rstrip(\".\")\n self.log(f\"Calculation result: {formatted_result}\")\n\n self.status = formatted_result\n return Data(data={\"result\": formatted_result})\n\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n except (SyntaxError, TypeError, KeyError, ValueError, AttributeError, OverflowError) as e:\n error_message = f\"Invalid expression: {e!s}\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n def build(self):\n \"\"\"Return the main evaluation function.\"\"\"\n return self.evaluate_expression\n" + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" }, - "expression": { + "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, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "sender_name": { "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Expression", + "advanced": true, + "display_name": "Sender Name", "dynamic": false, - "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", + "info": "Name of the sender.", "input_types": [ "Message" ], "list": false, "list_add_label": "Add More", "load_from_db": false, - "name": "expression", + "name": "sender_name", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": true, + "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": "" }, - "tools_metadata": { - "_input_type": "ToolsInput", - "advanced": false, - "display_name": "Actions", + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", "dynamic": false, - "info": "Modify tool names and descriptions to help agents understand when to use each tool.", - "is_list": true, + "info": "Store the message in the history.", + "list": false, "list_add_label": "Add More", - "name": "tools_metadata", + "name": "should_store_message", "placeholder": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "tools", - "value": [ - { - "args": { - "expression": { - "default": "", - "description": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", - "title": "Expression", - "type": "string" - } - }, - "description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", - "display_description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", - "display_name": "evaluate_expression", - "name": "evaluate_expression", - "status": true, - "tags": [ - "evaluate_expression" - ] - } - ] + "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": true + "tool_mode": false }, - "selected_output": "component_as_tool", - "showNode": true, - "type": "CalculatorComponent" + "selected_output": "message", + "showNode": false, + "type": "ChatInput" }, "dragging": false, - "id": "CalculatorComponent-15FSE", + "id": "ChatInput-S6AhV", "measured": { - "height": 248, - "width": 320 + "height": 48, + "width": 192 }, "position": { - "x": 1233.166256931297, - "y": 514.7544001650839 + "x": 1235.4222740043401, + "y": 897.5992294662233 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "ChatInput-2YUmA", + "id": "ChatOutput-WciiI", "node": { "base_classes": [ "Message" ], "beta": false, - "category": "inputs", + "category": "outputs", "conditional_paths": [], "custom_fields": {}, - "description": "Get chat inputs from the Playground.", - "display_name": "Chat Input", + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", "documentation": "", "edited": false, "field_order": [ @@ -760,15 +653,17 @@ "sender", "sender_name", "session_id", - "files", + "data_template", "background_color", "chat_icon", - "text_color" + "text_color", + "clean_data" ], "frozen": false, "icon": "MessagesSquare", - "key": "ChatInput", + "key": "ChatOutput", "legacy": false, + "lf_version": "1.4.3", "metadata": {}, "minimized": true, "output_types": [], @@ -776,7 +671,7 @@ { "allows_loop": false, "cache": true, - "display_name": "Chat Message", + "display_name": "Output Message", "group_outputs": false, "method": "message_response", "name": "message", @@ -789,7 +684,7 @@ } ], "pinned": false, - "score": 0.0020353564437605998, + "score": 0.003169567463043492, "template": { "_type": "Component", "background_color": { @@ -838,6 +733,24 @@ "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, @@ -854,73 +767,51 @@ "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.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" + "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.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\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.template.field.base import Output\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" }, - "files": { - "_input_type": "FileInput", + "data_template": { + "_input_type": "MessageTextInput", "advanced": true, - "display_name": "Files", + "display_name": "Data Template", "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" + "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", + "input_types": [ + "Message" ], - "file_path": "", - "info": "Files to be sent with the message.", - "list": true, + "list": false, "list_add_label": "Add More", - "name": "files", + "load_from_db": false, + "name": "data_template", "placeholder": "", "required": false, "show": true, - "temp_file": true, "title_case": false, + "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, - "type": "file", - "value": "" + "type": "str", + "value": "{text}" }, "input_value": { - "_input_type": "MultilineInput", + "_input_type": "HandleInput", "advanced": false, - "copy_field": false, - "display_name": "Input Text", + "display_name": "Inputs", "dynamic": false, - "info": "Message to be passed as input.", - "input_types": [], + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], "list": false, "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, "name": "input_value", "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, - "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", + "type": "other", "value": "" }, "sender": { @@ -944,7 +835,7 @@ "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "User" + "value": "Machine" }, "sender_name": { "_input_type": "MessageTextInput", @@ -967,7 +858,7 @@ "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "User" + "value": "AI" }, "session_id": { "_input_type": "MessageTextInput", @@ -1036,65 +927,85 @@ }, "tool_mode": false }, - "selected_output": "message", "showNode": false, - "type": "ChatInput" + "type": "ChatOutput" }, - "dragging": false, - "id": "ChatInput-2YUmA", + "id": "ChatOutput-WciiI", "measured": { - "height": 66, + "height": 48, "width": 192 }, "position": { - "x": 1235.4222740043401, - "y": 897.5992294662233 + "x": 2145, + "y": 660 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "ChatOutput-aHj8n", + "id": "Agent-xD3uW", "node": { "base_classes": [ "Message" ], "beta": false, - "category": "outputs", + "category": "agents", "conditional_paths": [], "custom_fields": {}, - "description": "Display a chat message in the Playground.", - "display_name": "Chat Output", + "description": "Define the agent's instructions, then enter a task to complete using tools.", + "display_name": "Agent", "documentation": "", "edited": false, "field_order": [ + "agent_llm", + "max_tokens", + "model_kwargs", + "json_mode", + "model_name", + "openai_api_base", + "api_key", + "temperature", + "seed", + "max_retries", + "timeout", + "system_prompt", + "tools", "input_value", - "should_store_message", + "handle_parsing_errors", + "verbose", + "max_iterations", + "agent_description", + "mode", + "message", + "memory", + "sender_type", "sender", "sender_name", + "n_messages", "session_id", - "data_template", - "background_color", - "chat_icon", - "text_color", - "clean_data" + "order", + "template", + "add_current_date_tool" ], "frozen": false, - "icon": "MessagesSquare", - "key": "ChatOutput", + "icon": "bot", + "key": "Agent", "legacy": false, + "lf_version": "1.4.3", "metadata": {}, - "minimized": true, + "minimized": false, "output_types": [], "outputs": [ { "allows_loop": false, "cache": true, - "display_name": "Output Message", + "display_name": "Response", "group_outputs": false, "method": "message_response", - "name": "message", + "name": "response", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1104,45 +1015,42 @@ } ], "pinned": false, - "score": 0.003169567463043492, + "score": 1.1732828199964098e-19, "template": { "_type": "Component", - "background_color": { - "_input_type": "MessageTextInput", + "add_current_date_tool": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Background Color", + "display_name": "Current Date", "dynamic": false, - "info": "The background color of the icon.", - "input_types": [ - "Message" - ], + "info": "If true, will add a tool to the agent that returns the current date.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "background_color", + "name": "add_current_date_tool", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "bool", + "value": true }, - "chat_icon": { - "_input_type": "MessageTextInput", + "agent_description": { + "_input_type": "MultilineInput", "advanced": true, - "display_name": "Icon", + "copy_field": false, + "display_name": "Agent Description [Deprecated]", "dynamic": false, - "info": "The icon of the message.", + "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, - "name": "chat_icon", + "multiline": true, + "name": "agent_description", "placeholder": "", "required": false, "show": true, @@ -1151,25 +1059,86 @@ "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "" + "value": "A helpful assistant with access to the following tools:" }, - "clean_data": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Basic Clean Data", + "agent_llm": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", "dynamic": false, - "info": "Whether to clean the data", - "list": false, - "list_add_label": "Add More", - "name": "clean_data", + "info": "The provider of the language model that the agent will use to generate responses.", + "input_types": [], + "name": "agent_llm", + "options": [ + "Amazon Bedrock", + "Anthropic", + "Azure OpenAI", + "Google Generative AI", + "Groq", + "NVIDIA", + "OpenAI", + "SambaNova", + "Custom" + ], + "options_metadata": [ + { + "icon": "Amazon" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Azure" + }, + { + "icon": "GoogleGenerativeAI" + }, + { + "icon": "Groq" + }, + { + "icon": "NVIDIA" + }, + { + "icon": "OpenAI" + }, + { + "icon": "SambaNova" + }, + { + "icon": "brain" + } + ], "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "str", + "value": "OpenAI" + }, + "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": "", + "real_time_refresh": true, + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" }, "code": { "advanced": true, @@ -1187,760 +1156,731 @@ "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.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\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.template.field.base import Output\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" + "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" }, - "data_template": { + "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": "" + }, + "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_iterations": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Data Template", + "display_name": "Max Iterations", "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" - ], + "info": "The maximum number of attempts the agent can make to complete its task before it stops.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "data_template", + "name": "max_iterations", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "{text}" + "type": "int", + "value": 15 }, - "input_value": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Inputs", + "max_retries": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Retries", "dynamic": false, - "info": "Message to be passed as output.", - "input_types": [ - "Data", - "DataFrame", - "Message" - ], + "info": "The maximum number of retries to make when generating.", "list": false, "list_add_label": "Add More", - "name": "input_value", + "name": "max_retries", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, + "tool_mode": false, "trace_as_metadata": true, - "type": "other", - "value": "" + "type": "int", + "value": 5 }, - "sender": { - "_input_type": "DropdownInput", + "max_tokens": { + "_input_type": "IntInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender Type", + "display_name": "Max Tokens", "dynamic": false, - "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User" - ], - "options_metadata": [], + "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": "str", - "value": "Machine" + "type": "int", + "value": "" }, - "sender_name": { - "_input_type": "MessageTextInput", + "memory": { + "_input_type": "HandleInput", "advanced": true, - "display_name": "Sender Name", + "display_name": "External Memory", "dynamic": false, - "info": "Name of the sender.", + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", "input_types": [ - "Message" + "Memory" ], "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "sender_name", + "name": "memory", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "AI" + "type": "other", + "value": "" }, - "session_id": { + "message": { "_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.", + "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": "session_id", + "name": "message", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, - "tool_mode": false, + "tool_mode": true, "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "" }, - "should_store_message": { - "_input_type": "BoolInput", + "mode": { + "_input_type": "TabInput", "advanced": true, - "display_name": "Store Messages", + "display_name": "Mode", "dynamic": false, - "info": "Store the message in the history.", - "list": false, - "list_add_label": "Add More", - "name": "should_store_message", + "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": "bool", - "value": true + "type": "tab", + "value": "Retrieve" }, - "text_color": { - "_input_type": "MessageTextInput", + "model_kwargs": { + "_input_type": "DictInput", "advanced": true, - "display_name": "Text Color", + "display_name": "Model Kwargs", "dynamic": false, - "info": "The text color of the name", - "input_types": [ - "Message" - ], + "info": "Additional keyword arguments to pass to the model.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "text_color", + "name": "model_kwargs", "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 - }, - "showNode": false, - "type": "ChatOutput" - }, - "id": "ChatOutput-aHj8n", - "measured": { - "height": 66, - "width": 192 - }, - "position": { - "x": 2145, - "y": 660 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "Agent-qYZ9W", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "category": "agents", - "conditional_paths": [], - "custom_fields": {}, - "description": "Define the agent's instructions, then enter a task to complete using tools.", - "display_name": "Agent", - "documentation": "", - "edited": false, - "field_order": [ - "agent_llm", - "max_tokens", - "model_kwargs", - "json_mode", - "model_name", - "openai_api_base", - "api_key", - "temperature", - "seed", - "max_retries", - "timeout", - "system_prompt", - "tools", - "input_value", - "handle_parsing_errors", - "verbose", - "max_iterations", - "agent_description", - "memory", - "sender", - "sender_name", - "n_messages", - "session_id", - "order", - "template", - "add_current_date_tool" - ], - "frozen": false, - "icon": "bot", - "key": "Agent", - "legacy": false, - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Response", - "method": "message_response", - "name": "response", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" + "type": "dict", + "value": {} + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": true, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", + "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" ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 1.1732828199964098e-19, - "template": { - "_type": "Component", - "add_current_date_tool": { - "_input_type": "BoolInput", + "options_metadata": [], + "placeholder": "", + "real_time_refresh": false, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o" + }, + "n_messages": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Current Date", + "display_name": "Number of Messages", "dynamic": false, - "info": "If true, will add a tool to the agent that returns the current date.", + "info": "Number of messages to retrieve.", "list": false, "list_add_label": "Add More", - "name": "add_current_date_tool", + "name": "n_messages", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "int", + "value": 100 }, - "agent_description": { - "_input_type": "MultilineInput", + "openai_api_base": { + "_input_type": "StrInput", "advanced": true, - "copy_field": false, - "display_name": "Agent Description [Deprecated]", + "display_name": "OpenAI API Base", "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" - ], + "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, - "multiline": true, - "name": "agent_description", + "name": "openai_api_base", "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:" + "value": "" }, - "agent_llm": { + "order": { "_input_type": "DropdownInput", - "advanced": false, + "advanced": true, "combobox": false, "dialog_inputs": {}, - "display_name": "Model Provider", + "display_name": "Order", "dynamic": false, - "info": "The provider of the language model that the agent will use to generate responses.", - "input_types": [], - "name": "agent_llm", + "info": "Order of the messages.", + "name": "order", "options": [ - "Anthropic", - "Google Generative AI", - "Groq", - "OpenAI", - "Custom" - ], - "options_metadata": [ - { - "icon": "Amazon" - }, - { - "icon": "Anthropic" - }, - { - "icon": "Azure" - }, - { - "icon": "GoogleGenerativeAI" - }, - { - "icon": "Groq" - }, - { - "icon": "NVIDIA" - }, - { - "icon": "OpenAI" - }, - { - "icon": "SambaNova" - }, - { - "icon": "brain" - } + "Ascending", + "Descending" ], + "options_metadata": [], "placeholder": "", - "real_time_refresh": true, - "required": false, + "required": true, "show": true, "title_case": false, "toggle": false, - "tool_mode": false, + "tool_mode": true, "trace_as_metadata": true, "type": "str", - "value": "OpenAI" - }, - "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": "OPENAI_API_KEY" - }, - "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_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" + "value": "Ascending" }, - "handle_parsing_errors": { - "_input_type": "BoolInput", + "seed": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Handle Parse Errors", + "display_name": "Seed", "dynamic": false, - "info": "Should the Agent fix errors when reading user input for better processing?", + "info": "The seed controls the reproducibility of the job.", "list": false, "list_add_label": "Add More", - "name": "handle_parsing_errors", + "name": "seed", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "int", + "value": 1 }, - "input_value": { + "sender": { "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "Input", + "advanced": true, + "display_name": "Sender", "dynamic": false, - "info": "The input provided by the user for the agent to process.", + "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", "input_types": [ "Message" ], "list": false, "list_add_label": "Add More", "load_from_db": false, - "name": "input_value", + "name": "sender", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": true, + "tool_mode": false, "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "" }, - "json_mode": { - "_input_type": "BoolInput", + "sender_name": { + "_input_type": "MessageTextInput", "advanced": true, - "display_name": "JSON Mode", + "display_name": "Sender Name", "dynamic": false, - "info": "If True, it will output JSON regardless of passing a schema.", + "info": "Filter by sender name.", + "input_types": [ + "Message" + ], "list": false, "list_add_label": "Add More", - "name": "json_mode", + "load_from_db": false, + "name": "sender_name", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, - "type": "bool", - "value": false + "type": "str", + "value": "" }, - "max_iterations": { - "_input_type": "IntInput", + "sender_type": { + "_input_type": "DropdownInput", "advanced": true, - "display_name": "Max Iterations", + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", "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", + "info": "Filter by sender type.", + "name": "sender_type", + "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": "int", - "value": 15 + "type": "str", + "value": "Machine and User" }, - "max_retries": { - "_input_type": "IntInput", + "session_id": { + "_input_type": "MessageTextInput", "advanced": true, - "display_name": "Max Retries", + "display_name": "Session ID", "dynamic": false, - "info": "The maximum number of retries to make when generating.", + "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", - "name": "max_retries", + "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": "int", - "value": 5 + "type": "str", + "value": "" }, - "max_tokens": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Tokens", + "system_prompt": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "Agent Instructions", "dynamic": false, - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", + "input_types": [ + "Message" + ], "list": false, "list_add_label": "Add More", - "name": "max_tokens", + "load_from_db": false, + "multiline": true, + "name": "system_prompt", "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_input": true, "trace_as_metadata": true, - "type": "int", - "value": "" + "type": "str", + "value": "You are a helpful assistant that can use tools to answer questions and perform tasks." }, - "memory": { - "_input_type": "HandleInput", + "temperature": { + "_input_type": "SliderInput", "advanced": true, - "display_name": "External Memory", + "display_name": "Temperature", "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", + "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, - "trace_as_metadata": true, - "type": "other", - "value": "" + "tool_mode": false, + "type": "slider", + "value": 0.1 }, - "message": { - "_input_type": "MessageTextInput", + "template": { + "_input_type": "MultilineInput", "advanced": true, - "display_name": "Message", + "copy_field": false, + "display_name": "Template", "dynamic": false, - "info": "The chat message to be stored.", + "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, - "name": "message", + "multiline": true, + "name": "template", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, - "tool_mode": true, + "tool_mode": false, "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" + "value": "{sender_name}: {text}" }, - "model_kwargs": { - "_input_type": "DictInput", + "timeout": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Model Kwargs", + "display_name": "Timeout", "dynamic": false, - "info": "Additional keyword arguments to pass to the model.", + "info": "The timeout for requests to OpenAI completion API.", "list": false, "list_add_label": "Add More", - "name": "model_kwargs", + "name": "timeout", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, - "type": "dict", - "value": {} + "trace_as_metadata": true, + "type": "int", + "value": 700 }, - "model_name": { - "_input_type": "DropdownInput", + "tools": { + "_input_type": "HandleInput", "advanced": false, - "combobox": true, - "dialog_inputs": {}, - "display_name": "Model Name", + "display_name": "Tools", "dynamic": false, - "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", - "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" + "info": "These are the tools that the agent can use to help with tasks.", + "input_types": [ + "Tool" ], - "options_metadata": [], + "list": true, + "list_add_label": "Add More", + "name": "tools", "placeholder": "", - "real_time_refresh": false, "required": false, "show": true, "title_case": false, - "toggle": false, - "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "gpt-4.1" + "type": "other", + "value": "" }, - "n_messages": { - "_input_type": "IntInput", + "verbose": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Number of Messages", + "display_name": "Verbose", "dynamic": false, - "info": "Number of messages to retrieve.", + "info": "", "list": false, "list_add_label": "Add More", - "name": "n_messages", + "name": "verbose", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "int", - "value": 100 - }, - "openai_api_base": { - "_input_type": "StrInput", + "type": "bool", + "value": true + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "Agent" + }, + "dragging": false, + "id": "Agent-xD3uW", + "measured": { + "height": 591, + "width": 320 + }, + "position": { + "x": 1763.0750031786254, + "y": 232.97801992415862 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "URLComponent-6fRXe", + "node": { + "base_classes": [ + "DataFrame", + "Message" + ], + "beta": false, + "category": "data", + "conditional_paths": [], + "custom_fields": {}, + "description": "Fetch content from one or more web pages, following links recursively.", + "display_name": "URL", + "documentation": "", + "edited": false, + "field_order": [ + "urls", + "max_depth", + "prevent_outside", + "use_async", + "format", + "timeout", + "headers", + "filter_text_html", + "continue_on_failure", + "check_response_status", + "autoset_encoding" + ], + "frozen": false, + "icon": "layout-template", + "key": "URLComponent", + "legacy": false, + "lf_version": "1.4.3", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "group_outputs": false, + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "options": null, + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 2.220446049250313e-16, + "template": { + "_type": "Component", + "autoset_encoding": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "OpenAI API Base", + "display_name": "Autoset Encoding", "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.", + "info": "If enabled, automatically sets the encoding of the request.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "openai_api_base", + "name": "autoset_encoding", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "bool", + "value": true }, - "order": { - "_input_type": "DropdownInput", + "check_response_status": { + "_input_type": "BoolInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Order", + "display_name": "Check Response Status", "dynamic": false, - "info": "Order of the messages.", - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "options_metadata": [], + "info": "If enabled, checks the response status of the request.", + "list": false, + "list_add_label": "Add More", + "name": "check_response_status", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "toggle": false, - "tool_mode": true, + "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "Ascending" + "type": "bool", + "value": false }, - "seed": { - "_input_type": "IntInput", + "code": { "advanced": true, - "display_name": "Seed", - "dynamic": false, - "info": "The seed controls the reproducibility of the job.", + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", "list": false, - "list_add_label": "Add More", - "name": "seed", + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, "placeholder": "", - "required": false, + "required": true, "show": true, "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 1 + "type": "code", + "value": "import re\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom langchain_community.document_loaders import RecursiveUrlLoader\nfrom loguru import logger\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.helpers.data import safe_convert\nfrom langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SliderInput, TableInput\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.services.deps import get_settings_service\n\n# Constants\nDEFAULT_TIMEOUT = 30\nDEFAULT_MAX_DEPTH = 1\nDEFAULT_FORMAT = \"Text\"\nURL_REGEX = re.compile(\n r\"^(https?:\\/\\/)?\"\n r\"(www\\.)?\"\n r\"([a-zA-Z0-9.-]+)\"\n r\"(\\.[a-zA-Z]{2,})?\"\n r\"(:\\d+)?\"\n r\"(\\/[^\\s]*)?$\",\n re.IGNORECASE,\n)\n\n\nclass URLComponent(Component):\n \"\"\"A component that loads and parses content from web pages recursively.\n\n This component allows fetching content from one or more URLs, with options to:\n - Control crawl depth\n - Prevent crawling outside the root domain\n - Use async loading for better performance\n - Extract either raw HTML or clean text\n - Configure request headers and timeouts\n \"\"\"\n\n display_name = \"URL\"\n description = \"Fetch content from one or more web pages, following links recursively.\"\n icon = \"layout-template\"\n name = \"URLComponent\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs to crawl recursively, by clicking the '+' button.\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n input_types=[],\n ),\n SliderInput(\n name=\"max_depth\",\n display_name=\"Depth\",\n info=(\n \"Controls how many 'clicks' away from the initial page the crawler will go:\\n\"\n \"- depth 1: only the initial page\\n\"\n \"- depth 2: initial page + all pages linked directly from it\\n\"\n \"- depth 3: initial page + direct links + links found on those direct link pages\\n\"\n \"Note: This is about link traversal, not URL path depth.\"\n ),\n value=DEFAULT_MAX_DEPTH,\n range_spec=RangeSpec(min=1, max=5, step=1),\n required=False,\n min_label=\" \",\n max_label=\" \",\n min_label_icon=\"None\",\n max_label_icon=\"None\",\n # slider_input=True\n ),\n BoolInput(\n name=\"prevent_outside\",\n display_name=\"Prevent Outside\",\n info=(\n \"If enabled, only crawls URLs within the same domain as the root URL. \"\n \"This helps prevent the crawler from going to external websites.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"use_async\",\n display_name=\"Use Async\",\n info=(\n \"If enabled, uses asynchronous loading which can be significantly faster \"\n \"but might use more system resources.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.\",\n options=[\"Text\", \"HTML\"],\n value=DEFAULT_FORMAT,\n advanced=True,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"Timeout for the request in seconds.\",\n value=DEFAULT_TIMEOUT,\n required=False,\n advanced=True,\n ),\n TableInput(\n name=\"headers\",\n display_name=\"Headers\",\n info=\"The headers to send with the request\",\n table_schema=[\n {\n \"name\": \"key\",\n \"display_name\": \"Header\",\n \"type\": \"str\",\n \"description\": \"Header name\",\n },\n {\n \"name\": \"value\",\n \"display_name\": \"Value\",\n \"type\": \"str\",\n \"description\": \"Header value\",\n },\n ],\n value=[{\"key\": \"User-Agent\", \"value\": get_settings_service().settings.user_agent}],\n advanced=True,\n input_types=[\"DataFrame\"],\n ),\n BoolInput(\n name=\"filter_text_html\",\n display_name=\"Filter Text/HTML\",\n info=\"If enabled, filters out text/css content type from the results.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"continue_on_failure\",\n display_name=\"Continue on Failure\",\n info=\"If enabled, continues crawling even if some requests fail.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"check_response_status\",\n display_name=\"Check Response Status\",\n info=\"If enabled, checks the response status of the request.\",\n value=False,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"autoset_encoding\",\n display_name=\"Autoset Encoding\",\n info=\"If enabled, automatically sets the encoding of the request.\",\n value=True,\n required=False,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Extracted Pages\", name=\"page_results\", method=\"fetch_content\"),\n Output(display_name=\"Raw Content\", name=\"raw_results\", method=\"fetch_content_as_message\", tool_mode=False),\n ]\n\n @staticmethod\n def validate_url(url: str) -> bool:\n \"\"\"Validates if the given string matches URL pattern.\n\n Args:\n url: The URL string to validate\n\n Returns:\n bool: True if the URL is valid, False otherwise\n \"\"\"\n return bool(URL_REGEX.match(url))\n\n def ensure_url(self, url: str) -> str:\n \"\"\"Ensures the given string is a valid URL.\n\n Args:\n url: The URL string to validate and normalize\n\n Returns:\n str: The normalized URL\n\n Raises:\n ValueError: If the URL is invalid\n \"\"\"\n url = url.strip()\n if not url.startswith((\"http://\", \"https://\")):\n url = \"https://\" + url\n\n if not self.validate_url(url):\n msg = f\"Invalid URL: {url}\"\n raise ValueError(msg)\n\n return url\n\n def _create_loader(self, url: str) -> RecursiveUrlLoader:\n \"\"\"Creates a RecursiveUrlLoader instance with the configured settings.\n\n Args:\n url: The URL to load\n\n Returns:\n RecursiveUrlLoader: Configured loader instance\n \"\"\"\n headers_dict = {header[\"key\"]: header[\"value\"] for header in self.headers}\n extractor = (lambda x: x) if self.format == \"HTML\" else (lambda x: BeautifulSoup(x, \"lxml\").get_text())\n\n return RecursiveUrlLoader(\n url=url,\n max_depth=self.max_depth,\n prevent_outside=self.prevent_outside,\n use_async=self.use_async,\n extractor=extractor,\n timeout=self.timeout,\n headers=headers_dict,\n check_response_status=self.check_response_status,\n continue_on_failure=self.continue_on_failure,\n base_url=url, # Add base_url to ensure consistent domain crawling\n autoset_encoding=self.autoset_encoding, # Enable automatic encoding detection\n exclude_dirs=[], # Allow customization of excluded directories\n link_regex=None, # Allow customization of link filtering\n )\n\n def fetch_url_contents(self) -> list[dict]:\n \"\"\"Load documents from the configured URLs.\n\n Returns:\n List[Data]: List of Data objects containing the fetched content\n\n Raises:\n ValueError: If no valid URLs are provided or if there's an error loading documents\n \"\"\"\n try:\n urls = list({self.ensure_url(url) for url in self.urls if url.strip()})\n logger.info(f\"URLs: {urls}\")\n if not urls:\n msg = \"No valid URLs provided.\"\n raise ValueError(msg)\n\n all_docs = []\n for url in urls:\n logger.info(f\"Loading documents from {url}\")\n\n try:\n loader = self._create_loader(url)\n docs = loader.load()\n\n if not docs:\n logger.warning(f\"No documents found for {url}\")\n continue\n\n logger.info(f\"Found {len(docs)} documents from {url}\")\n all_docs.extend(docs)\n\n except requests.exceptions.RequestException as e:\n logger.exception(f\"Error loading documents from {url}: {e}\")\n continue\n\n if not all_docs:\n msg = \"No documents were successfully loaded from any URL\"\n raise ValueError(msg)\n\n # data = [Data(text=doc.page_content, **doc.metadata) for doc in all_docs]\n data = [\n {\n \"text\": safe_convert(doc.page_content, clean_data=True),\n \"url\": doc.metadata.get(\"source\", \"\"),\n \"title\": doc.metadata.get(\"title\", \"\"),\n \"description\": doc.metadata.get(\"description\", \"\"),\n \"content_type\": doc.metadata.get(\"content_type\", \"\"),\n \"language\": doc.metadata.get(\"language\", \"\"),\n }\n for doc in all_docs\n ]\n except Exception as e:\n error_msg = e.message if hasattr(e, \"message\") else e\n msg = f\"Error loading documents: {error_msg!s}\"\n logger.exception(msg)\n raise ValueError(msg) from e\n return data\n\n def fetch_content(self) -> DataFrame:\n \"\"\"Convert the documents to a DataFrame.\"\"\"\n return DataFrame(data=self.fetch_url_contents())\n\n def fetch_content_as_message(self) -> Message:\n \"\"\"Convert the documents to a Message.\"\"\"\n url_contents = self.fetch_url_contents()\n return Message(text=\"\\n\\n\".join([x[\"text\"] for x in url_contents]), data={\"data\": url_contents})\n" }, - "sender": { - "_input_type": "DropdownInput", + "continue_on_failure": { + "_input_type": "BoolInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender", + "display_name": "Continue on Failure", "dynamic": false, - "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "options_metadata": [], + "info": "If enabled, continues crawling even if some requests fail.", + "list": false, + "list_add_label": "Add More", + "name": "continue_on_failure", "placeholder": "", "required": false, "show": true, "title_case": false, - "toggle": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "Machine and User" + "type": "bool", + "value": true }, - "sender_name": { - "_input_type": "MessageTextInput", + "filter_text_html": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Sender Name", + "display_name": "Filter Text/HTML", "dynamic": false, - "info": "Filter by sender name.", - "input_types": [ - "Message" - ], + "info": "If enabled, filters out text/css content type from the results.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "sender_name", + "name": "filter_text_html", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "bool", + "value": true }, - "sender_type": { + "format": { "_input_type": "DropdownInput", "advanced": true, "combobox": false, "dialog_inputs": {}, - "display_name": "Sender Type", + "display_name": "Output Format", "dynamic": false, - "info": "Filter by sender type.", - "name": "sender_type", + "info": "Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.", + "name": "format", "options": [ - "Machine", - "User", - "Machine and User" + "Text", + "HTML" ], "options_metadata": [], "placeholder": "", @@ -1951,72 +1891,83 @@ "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "Machine and User" + "value": "Text" }, - "session_id": { - "_input_type": "MessageTextInput", + "headers": { + "_input_type": "TableInput", "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": "" - }, - "system_prompt": { - "_input_type": "MultilineInput", - "advanced": false, - "copy_field": false, - "display_name": "Agent Instructions", + "display_name": "Headers", "dynamic": false, - "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", + "info": "The headers to send with the request", "input_types": [ - "Message" + "DataFrame" ], - "list": false, + "is_list": true, "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_prompt", + "name": "headers", "placeholder": "", "required": false, "show": true, + "table_icon": "Table", + "table_schema": { + "columns": [ + { + "default": "None", + "description": "Header name", + "disable_edit": false, + "display_name": "Header", + "edit_mode": "popover", + "filterable": true, + "formatter": "text", + "hidden": false, + "name": "key", + "sortable": true, + "type": "str" + }, + { + "default": "None", + "description": "Header value", + "disable_edit": false, + "display_name": "Value", + "edit_mode": "popover", + "filterable": true, + "formatter": "text", + "hidden": false, + "name": "value", + "sortable": true, + "type": "str" + } + ] + }, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "You are a helpful assistant that can use tools to answer questions and perform tasks." + "trigger_icon": "Table", + "trigger_text": "Open table", + "type": "table", + "value": [ + { + "key": "User-Agent", + "value": "langflow" + } + ] }, - "temperature": { + "max_depth": { "_input_type": "SliderInput", - "advanced": true, - "display_name": "Temperature", + "advanced": false, + "display_name": "Depth", "dynamic": false, - "info": "", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", + "info": "Controls how many 'clicks' away from the initial page the crawler will go:\n- depth 1: only the initial page\n- depth 2: initial page + all pages linked directly from it\n- depth 3: initial page + direct links + links found on those direct link pages\nNote: This is about link traversal, not URL path depth.", + "max_label": " ", + "max_label_icon": "None", + "min_label": " ", + "min_label_icon": "None", + "name": "max_depth", "placeholder": "", "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, + "max": 5, + "min": 1, + "step": 1, "step_type": "float" }, "required": false, @@ -2027,39 +1978,32 @@ "title_case": false, "tool_mode": false, "type": "slider", - "value": 0.1 + "value": 1 }, - "template": { - "_input_type": "MultilineInput", + "prevent_outside": { + "_input_type": "BoolInput", "advanced": true, - "copy_field": false, - "display_name": "Template", + "display_name": "Prevent Outside", "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" - ], + "info": "If enabled, only crawls URLs within the same domain as the root URL. This helps prevent the crawler from going to external websites.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "template", + "name": "prevent_outside", "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}" + "type": "bool", + "value": true }, "timeout": { "_input_type": "IntInput", "advanced": true, "display_name": "Timeout", "dynamic": false, - "info": "The timeout for requests to OpenAI completion API.", + "info": "Timeout for the request in seconds.", "list": false, "list_add_label": "Add More", "name": "timeout", @@ -2070,37 +2014,80 @@ "tool_mode": false, "trace_as_metadata": true, "type": "int", - "value": 700 + "value": 30 }, - "tools": { - "_input_type": "HandleInput", + "tools_metadata": { + "_input_type": "ToolsInput", "advanced": false, - "display_name": "Tools", + "display_name": "Actions", "dynamic": false, - "info": "These are the tools that the agent can use to help with tasks.", - "input_types": [ - "Tool" - ], - "list": true, + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", + "is_list": true, "list_add_label": "Add More", - "name": "tools", + "name": "tools_metadata", "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "title_case": false, + "tool_mode": false, "trace_as_metadata": true, - "type": "other", + "type": "tools", + "value": [ + { + "args": { + "urls": { + "default": "", + "description": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", + "items": { + "type": "string" + }, + "title": "Urls", + "type": "array" + } + }, + "description": "Fetch content from one or more web pages, following links recursively.", + "display_description": "Fetch content from one or more web pages, following links recursively.", + "display_name": "fetch_content", + "name": "fetch_content", + "readonly": false, + "status": true, + "tags": [ + "fetch_content" + ] + } + ] + }, + "urls": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "URLs", + "dynamic": false, + "info": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", + "input_types": [], + "list": true, + "list_add_label": "Add URL", + "load_from_db": false, + "name": "urls", + "placeholder": "Enter a URL...", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", "value": "" }, - "verbose": { + "use_async": { "_input_type": "BoolInput", "advanced": true, - "display_name": "Verbose", + "display_name": "Use Async", "dynamic": false, - "info": "", + "info": "If enabled, uses asynchronous loading which can be significantly faster but might use more system resources.", "list": false, "list_add_label": "Add More", - "name": "verbose", + "name": "use_async", "placeholder": "", "required": false, "show": true, @@ -2111,37 +2098,36 @@ "value": true } }, - "tool_mode": false + "tool_mode": true }, - "selected_output": "response", "showNode": true, - "type": "Agent" + "type": "URLComponent" }, "dragging": false, - "id": "Agent-qYZ9W", + "id": "URLComponent-6fRXe", "measured": { - "height": 624, + "height": 289, "width": 320 }, "position": { - "x": 1641.6239626366948, - "y": 301.10345101561927 + "x": 1388.440925896035, + "y": 2.4339723656417362 }, "selected": true, "type": "genericNode" } ], "viewport": { - "x": -343.42019144353594, - "y": 66.6702275988537, - "zoom": 0.5868650751024124 + "x": -398.05374665521265, + "y": 135.56417788603605, + "zoom": 0.5899089629086188 } }, "description": "A simple but powerful starter agent.", "endpoint_name": null, - "id": "85e419cd-33e0-43b7-b5ab-cac101c578f6", + "id": "907853a5-ae9a-4636-b629-4c44cfaf6b85", "is_component": false, - "last_tested_version": "1.3.4", + "last_tested_version": "1.4.3", "name": "Simple Agent", "tags": [ "assistants", From 57d35ba102f2d294f85183b9cb76c7ef353782d5 Mon Sep 17 00:00:00 2001 From: Yuqi Tang Date: Tue, 24 Jun 2025 08:38:26 -0700 Subject: [PATCH 3/6] update travel planning agent --- .../Travel Planning Agents.json | 2342 ++++++++--------- 1 file changed, 1143 insertions(+), 1199 deletions(-) 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 da963a6ee61f..b1c57f885ddb 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 @@ -6,56 +6,56 @@ "className": "", "data": { "sourceHandle": { - "dataType": "SearchComponent", - "id": "SearchComponent-kgsWf", - "name": "component_as_tool", + "dataType": "ChatInput", + "id": "ChatInput-GQzN9", + "name": "message", "output_types": [ - "Tool" + "Message" ] }, "targetHandle": { - "fieldName": "tools", - "id": "Agent-H916j", + "fieldName": "input_value", + "id": "Agent-SVMdS", "inputTypes": [ - "Tool" + "Message" ], - "type": "other" + "type": "str" } }, - "id": "reactflow__edge-SearchComponent-kgsWf{œdataTypeœ:œSearchComponentœ,œidœ:œSearchComponent-kgsWfœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-H916j{œfieldNameœ:œtoolsœ,œidœ:œAgent-H916jœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-ChatInput-GQzN9{œdataTypeœ:œChatInputœ,œidœ:œChatInput-GQzN9œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-SVMdS{œfieldNameœ:œinput_valueœ,œidœ:œAgent-SVMdSœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "SearchComponent-kgsWf", - "sourceHandle": "{œdataTypeœ: œSearchComponentœ, œidœ: œSearchComponent-kgsWfœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-H916j", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-H916jœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "ChatInput-GQzN9", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-GQzN9œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-SVMdS", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-SVMdSœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-6dlgJ", - "name": "message", + "dataType": "Agent", + "id": "Agent-SVMdS", + "name": "response", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_value", - "id": "Agent-H916j", + "id": "Agent-Huf0O", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-6dlgJ{œdataTypeœ:œChatInputœ,œidœ:œChatInput-6dlgJœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-H916j{œfieldNameœ:œinput_valueœ,œidœ:œAgent-H916jœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Agent-SVMdS{œdataTypeœ:œAgentœ,œidœ:œAgent-SVMdSœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-Agent-Huf0O{œfieldNameœ:œinput_valueœ,œidœ:œAgent-Huf0Oœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-6dlgJ", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-6dlgJœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-H916j", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-H916jœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Agent-SVMdS", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-SVMdSœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-Huf0O", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-Huf0Oœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "Agent", - "id": "Agent-H916j", + "id": "Agent-Huf0O", "name": "response", "output_types": [ "Message" @@ -71,19 +71,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "Agent-zFKST", + "id": "Agent-jiGgJ", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Agent-H916j{œdataTypeœ:œAgentœ,œidœ:œAgent-H916jœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-Agent-zFKST{œfieldNameœ:œinput_valueœ,œidœ:œAgent-zFKSTœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Agent-Huf0O{œdataTypeœ:œAgentœ,œidœ:œAgent-Huf0Oœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-Agent-jiGgJ{œfieldNameœ:œinput_valueœ,œidœ:œAgent-jiGgJœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Agent-H916j", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-H916jœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-zFKST", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-zFKSTœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Agent-Huf0O", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-Huf0Oœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-jiGgJ", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-jiGgJœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -91,7 +91,7 @@ "data": { "sourceHandle": { "dataType": "Agent", - "id": "Agent-zFKST", + "id": "Agent-jiGgJ", "name": "response", "output_types": [ "Message" @@ -99,56 +99,55 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "Agent-3tuWs", + "id": "ChatOutput-CvvO2", "inputTypes": [ + "Data", + "DataFrame", "Message" ], "type": "str" } }, - "id": "reactflow__edge-Agent-zFKST{œdataTypeœ:œAgentœ,œidœ:œAgent-zFKSTœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-Agent-3tuWs{œfieldNameœ:œinput_valueœ,œidœ:œAgent-3tuWsœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Agent-jiGgJ{œdataTypeœ:œAgentœ,œidœ:œAgent-jiGgJœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-CvvO2{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-CvvO2œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Agent-zFKST", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-zFKSTœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-3tuWs", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-3tuWsœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Agent-jiGgJ", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-jiGgJœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-CvvO2", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-CvvO2œ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, - "className": "", "data": { "sourceHandle": { - "dataType": "Agent", - "id": "Agent-3tuWs", - "name": "response", + "dataType": "SearchComponent", + "id": "SearchComponent-3nVDj", + "name": "component_as_tool", "output_types": [ - "Message" + "Tool" ] }, "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-n7Oik", + "fieldName": "tools", + "id": "Agent-SVMdS", "inputTypes": [ - "Data", - "DataFrame", - "Message" + "Tool" ], - "type": "str" + "type": "other" } }, - "id": "reactflow__edge-Agent-3tuWs{œdataTypeœ:œAgentœ,œidœ:œAgent-3tuWsœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-n7Oik{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-n7Oikœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__SearchComponent-3nVDj{œdataTypeœ:œSearchComponentœ,œidœ:œSearchComponent-3nVDjœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-SVMdS{œfieldNameœ:œtoolsœ,œidœ:œAgent-SVMdSœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "Agent-3tuWs", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-3tuWsœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-n7Oik", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-n7Oikœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "SearchComponent-3nVDj", + "sourceHandle": "{œdataTypeœ: œSearchComponentœ, œidœ: œSearchComponent-3nVDjœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-SVMdS", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-SVMdSœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { "animated": false, "data": { "sourceHandle": { - "dataType": "URL", - "id": "URL-4LSRv", + "dataType": "URLComponent", + "id": "URLComponent-8bgTS", "name": "component_as_tool", "output_types": [ "Tool" @@ -156,26 +155,25 @@ }, "targetHandle": { "fieldName": "tools", - "id": "Agent-zFKST", + "id": "Agent-Huf0O", "inputTypes": [ "Tool" ], "type": "other" } }, - "id": "xy-edge__URL-4LSRv{œdataTypeœ:œURLœ,œidœ:œURL-4LSRvœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-zFKST{œfieldNameœ:œtoolsœ,œidœ:œAgent-zFKSTœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "xy-edge__URLComponent-8bgTS{œdataTypeœ:œURLComponentœ,œidœ:œURLComponent-8bgTSœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-Huf0O{œfieldNameœ:œtoolsœ,œidœ:œAgent-Huf0Oœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "URL-4LSRv", - "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-4LSRvœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-zFKST", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-zFKSTœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "URLComponent-8bgTS", + "sourceHandle": "{œdataTypeœ: œURLComponentœ, œidœ: œURLComponent-8bgTSœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-Huf0O", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-Huf0Oœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { - "animated": false, "data": { "sourceHandle": { "dataType": "CalculatorComponent", - "id": "CalculatorComponent-hlWP4", + "id": "CalculatorComponent-JKeHR", "name": "component_as_tool", "output_types": [ "Tool" @@ -183,25 +181,24 @@ }, "targetHandle": { "fieldName": "tools", - "id": "Agent-3tuWs", + "id": "Agent-jiGgJ", "inputTypes": [ "Tool" ], "type": "other" } }, - "id": "xy-edge__CalculatorComponent-hlWP4{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-hlWP4œ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-3tuWs{œfieldNameœ:œtoolsœ,œidœ:œAgent-3tuWsœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "selected": false, - "source": "CalculatorComponent-hlWP4", - "sourceHandle": "{œdataTypeœ: œCalculatorComponentœ, œidœ: œCalculatorComponent-hlWP4œ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-3tuWs", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-3tuWsœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "id": "xy-edge__CalculatorComponent-JKeHR{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-JKeHRœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-jiGgJ{œfieldNameœ:œtoolsœ,œidœ:œAgent-jiGgJœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "source": "CalculatorComponent-JKeHR", + "sourceHandle": "{œdataTypeœ: œCalculatorComponentœ, œidœ: œCalculatorComponent-JKeHRœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-jiGgJ", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-jiGgJœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" } ], "nodes": [ { "data": { - "id": "ChatInput-6dlgJ", + "id": "ChatInput-GQzN9", "node": { "base_classes": [ "Message" @@ -475,10 +472,10 @@ }, "dragging": false, "height": 262, - "id": "ChatInput-6dlgJ", + "id": "ChatInput-GQzN9", "measured": { "height": 262, - "width": 320 + "width": 360 }, "position": { "x": 1731.3224944651397, @@ -496,7 +493,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-n7Oik", + "id": "ChatOutput-CvvO2", "node": { "base_classes": [ "Message" @@ -779,10 +776,10 @@ }, "dragging": false, "height": 262, - "id": "ChatOutput-n7Oik", + "id": "ChatOutput-CvvO2", "measured": { "height": 262, - "width": 320 + "width": 360 }, "position": { "x": 4349.229697347143, @@ -798,7 +795,7 @@ }, { "data": { - "id": "note-NwG3w", + "id": "note-CQDFw", "node": { "description": "# Travel Planning Agents \n\nThe travel planning system is a smart setup that uses several specialized \"agents\" to help plan incredible trips. Imagine each agent as a travel expert focusing on a part of your journey. Here's how it works:\n\n- **User-Friendly Start:** You start by telling the system about your travel needs—where you want to go and what you love to do.\n\n- **Data Collection:** The agents uses its tools to gather current info about various destinations, like the best travel times, weather, and costs.\n\n- **Three Key Agents:**\n - **City Selection Agent:** Picks the best places to visit based on your likes and current data.\n - **Local Expert Agent:** Gathers interesting details about what to do and see in the chosen city.\n - **Travel Concierge Agent:** Builds a day-by-day plan that includes where to stay, eat, and explore!\n\n- **Tools and Data:** Each agent uses tools to find and organize the latest information so you get recommendations that are both accurate and exciting.\n\n- **Final Plan:** Once everything is put together, you receive a complete, easy-to-follow travel itinerary, perfect for your adventure!\n", "display_name": "", @@ -809,10 +806,10 @@ }, "dragging": false, "height": 603, - "id": "note-NwG3w", + "id": "note-CQDFw", "measured": { "height": 603, - "width": 325 + "width": 324 }, "position": { "x": 1319.2860379588103, @@ -833,7 +830,7 @@ }, { "data": { - "id": "note-zaSgy", + "id": "note-tuc3p", "node": { "description": "# **City Selection Agent**\n - **Purpose:** This agent evaluates potential travel destinations based on user input and external data sources.\n - **Core Functions:** Analyzes factors such as weather, local events, and travel costs to recommend optimal cities.\n - **Tools Utilized:** Employs APIs and data-fetching tools to gather real-time information for decision-making.\n", "display_name": "", @@ -846,10 +843,10 @@ }, "dragging": false, "height": 334, - "id": "note-zaSgy", + "id": "note-tuc3p", "measured": { "height": 334, - "width": 325 + "width": 324 }, "position": { "x": 2112.2352264274264, @@ -870,7 +867,7 @@ }, { "data": { - "id": "note-imuMP", + "id": "note-68RyJ", "node": { "description": "# **Local Expert Agent**\n - **Purpose:** Focused on gathering and providing an in-depth guide to the selected city.\n - **Core Functions:** Compiles insights into cultural attractions, local customs, and unique experiences.\n - **Tools Utilized:** Uses web content fetchers and data APIs to collect detailed local insights and enhance the user understanding with hidden gems.\n", "display_name": "", @@ -883,10 +880,10 @@ }, "dragging": false, "height": 342, - "id": "note-imuMP", + "id": "note-68RyJ", "measured": { "height": 342, - "width": 325 + "width": 324 }, "position": { "x": 2827.660803823376, @@ -907,7 +904,7 @@ }, { "data": { - "id": "note-KFInF", + "id": "note-2s03A", "node": { "description": "# **Travel Concierge Agent**\n - **Purpose:** Crafts detailed travel itineraries that are customized to the traveler's interests and needs.\n - **Core Functions:** Offers a comprehensive daily schedule, including accommodations, dining spots, and activities.\n - **Tools Utilized:** Integrates calculators and data tools for accurate budget planning and itinerary logistics.", "display_name": "", @@ -920,10 +917,10 @@ }, "dragging": false, "height": 336, - "id": "note-KFInF", + "id": "note-2s03A", "measured": { "height": 336, - "width": 325 + "width": 324 }, "position": { "x": 3536.084279543714, @@ -944,7 +941,7 @@ }, { "data": { - "id": "note-b1zMq", + "id": "note-Qrd85", "node": { "description": "## Configure the agent by obtaining your OpenAI API key from [platform.openai.com](https://platform.openai.com). Under \"Model Provider\", choose:\n- OpenAI: Default, requires only API key\n- Anthropic/Azure/Groq/NVIDIA: Each requires their own API keys\n- Custom: Use your own model endpoint + authentication\n\nSelect model and input API key before running the flow.", "display_name": "", @@ -957,10 +954,10 @@ }, "dragging": false, "height": 324, - "id": "note-b1zMq", + "id": "note-Qrd85", "measured": { "height": 324, - "width": 325 + "width": 324 }, "position": { "x": 2463.3881993480218, @@ -977,32 +974,52 @@ }, { "data": { - "description": "Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "URL", - "id": "URL-4LSRv", + "id": "Agent-SVMdS", "node": { "base_classes": [ - "Data", - "DataFrame", "Message" ], "beta": false, "conditional_paths": [], "custom_fields": {}, - "description": "Fetch content from one or more web pages, following links recursively.", - "display_name": "URL", + "description": "Define the agent's instructions, then enter a task to complete using tools.", + "display_name": "Agent", "documentation": "", "edited": false, "field_order": [ - "urls", - "format", - "separator", - "clean_extra_whitespace" + "agent_llm", + "max_tokens", + "model_kwargs", + "json_mode", + "model_name", + "openai_api_base", + "api_key", + "temperature", + "seed", + "max_retries", + "timeout", + "system_prompt", + "tools", + "input_value", + "handle_parsing_errors", + "verbose", + "max_iterations", + "agent_description", + "mode", + "message", + "memory", + "sender_type", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template", + "add_current_date_tool" ], "frozen": false, - "icon": "layout-template", + "icon": "bot", "legacy": false, - "lf_version": "1.2.0", "metadata": {}, "minimized": false, "output_types": [], @@ -1010,16 +1027,16 @@ { "allows_loop": false, "cache": true, - "display_name": "Toolset", - "hidden": false, - "method": "to_toolkit", - "name": "component_as_tool", + "display_name": "Response", + "group_outputs": false, + "method": "message_response", + "name": "response", "options": null, "required_inputs": null, - "selected": "Tool", + "selected": "Message", "tool_mode": true, "types": [ - "Tool" + "Message" ], "value": "__UNDEFINED__" } @@ -1027,15 +1044,15 @@ "pinned": false, "template": { "_type": "Component", - "autoset_encoding": { + "add_current_date_tool": { "_input_type": "BoolInput", "advanced": true, - "display_name": "Autoset Encoding", + "display_name": "Current Date", "dynamic": false, - "info": "If enabled, automatically sets the encoding of the request.", + "info": "If true, will add a tool to the agent that returns the current date.", "list": false, "list_add_label": "Add More", - "name": "autoset_encoding", + "name": "add_current_date_tool", "placeholder": "", "required": false, "show": true, @@ -1045,23 +1062,97 @@ "type": "bool", "value": true }, - "check_response_status": { - "_input_type": "BoolInput", + "agent_description": { + "_input_type": "MultilineInput", "advanced": true, - "display_name": "Check Response Status", + "copy_field": false, + "display_name": "Agent Description [Deprecated]", "dynamic": false, - "info": "If enabled, checks the response status of the request.", + "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", - "name": "check_response_status", + "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": "bool", - "value": false + "type": "str", + "value": "A helpful assistant with access to the following tools:" + }, + "agent_llm": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "The provider of the language model that the agent will use to generate responses.", + "input_types": [], + "name": "agent_llm", + "options": [ + "Amazon Bedrock", + "Anthropic", + "Azure OpenAI", + "Google Generative AI", + "Groq", + "NVIDIA", + "OpenAI", + "SambaNova", + "Custom" + ], + "options_metadata": [ + { + "icon": "Anthropic" + }, + { + "icon": "GoogleGenerativeAI" + }, + { + "icon": "Groq" + }, + { + "icon": "OpenAI" + }, + { + "icon": "brain" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "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": "", + "real_time_refresh": true, + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" }, "code": { "advanced": true, @@ -1079,17 +1170,17 @@ "show": true, "title_case": false, "type": "code", - "value": "import re\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom langchain_community.document_loaders import RecursiveUrlLoader\nfrom loguru import logger\n\nfrom langflow.custom import Component\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.helpers.data import safe_convert\nfrom langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SliderInput, TableInput\nfrom langflow.schema import DataFrame, Message\nfrom langflow.services.deps import get_settings_service\n\n# Constants\nDEFAULT_TIMEOUT = 30\nDEFAULT_MAX_DEPTH = 1\nDEFAULT_FORMAT = \"Text\"\nURL_REGEX = re.compile(\n r\"^(https?:\\/\\/)?\" r\"(www\\.)?\" r\"([a-zA-Z0-9.-]+)\" r\"(\\.[a-zA-Z]{2,})?\" r\"(:\\d+)?\" r\"(\\/[^\\s]*)?$\",\n re.IGNORECASE,\n)\n\n\nclass URLComponent(Component):\n \"\"\"A component that loads and parses content from web pages recursively.\n\n This component allows fetching content from one or more URLs, with options to:\n - Control crawl depth\n - Prevent crawling outside the root domain\n - Use async loading for better performance\n - Extract either raw HTML or clean text\n - Configure request headers and timeouts\n \"\"\"\n\n display_name = \"URL\"\n description = \"Fetch content from one or more web pages, following links recursively.\"\n icon = \"layout-template\"\n name = \"URLComponent\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs to crawl recursively, by clicking the '+' button.\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n input_types=[],\n ),\n SliderInput(\n name=\"max_depth\",\n display_name=\"Depth\",\n info=(\n \"Controls how many 'clicks' away from the initial page the crawler will go:\\n\"\n \"- depth 1: only the initial page\\n\"\n \"- depth 2: initial page + all pages linked directly from it\\n\"\n \"- depth 3: initial page + direct links + links found on those direct link pages\\n\"\n \"Note: This is about link traversal, not URL path depth.\"\n ),\n value=DEFAULT_MAX_DEPTH,\n range_spec=RangeSpec(min=1, max=5, step=1),\n required=False,\n min_label=\" \",\n max_label=\" \",\n min_label_icon=\"None\",\n max_label_icon=\"None\",\n # slider_input=True\n ),\n BoolInput(\n name=\"prevent_outside\",\n display_name=\"Prevent Outside\",\n info=(\n \"If enabled, only crawls URLs within the same domain as the root URL. \"\n \"This helps prevent the crawler from going to external websites.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"use_async\",\n display_name=\"Use Async\",\n info=(\n \"If enabled, uses asynchronous loading which can be significantly faster \"\n \"but might use more system resources.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.\",\n options=[\"Text\", \"HTML\"],\n value=DEFAULT_FORMAT,\n advanced=True,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"Timeout for the request in seconds.\",\n value=DEFAULT_TIMEOUT,\n required=False,\n advanced=True,\n ),\n TableInput(\n name=\"headers\",\n display_name=\"Headers\",\n info=\"The headers to send with the request\",\n table_schema=[\n {\n \"name\": \"key\",\n \"display_name\": \"Header\",\n \"type\": \"str\",\n \"description\": \"Header name\",\n },\n {\n \"name\": \"value\",\n \"display_name\": \"Value\",\n \"type\": \"str\",\n \"description\": \"Header value\",\n },\n ],\n value=[{\"key\": \"User-Agent\", \"value\": get_settings_service().settings.user_agent}],\n advanced=True,\n input_types=[\"DataFrame\"],\n ),\n BoolInput(\n name=\"filter_text_html\",\n display_name=\"Filter Text/HTML\",\n info=\"If enabled, filters out text/css content type from the results.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"continue_on_failure\",\n display_name=\"Continue on Failure\",\n info=\"If enabled, continues crawling even if some requests fail.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"check_response_status\",\n display_name=\"Check Response Status\",\n info=\"If enabled, checks the response status of the request.\",\n value=False,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"autoset_encoding\",\n display_name=\"Autoset Encoding\",\n info=\"If enabled, automatically sets the encoding of the request.\",\n value=True,\n required=False,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Result\", name=\"page_results\", method=\"fetch_content\"),\n Output(display_name=\"Raw Result\", name=\"raw_results\", method=\"as_message\"),\n ]\n\n @staticmethod\n def validate_url(url: str) -> bool:\n \"\"\"Validates if the given string matches URL pattern.\n\n Args:\n url: The URL string to validate\n\n Returns:\n bool: True if the URL is valid, False otherwise\n \"\"\"\n return bool(URL_REGEX.match(url))\n\n def ensure_url(self, url: str) -> str:\n \"\"\"Ensures the given string is a valid URL.\n\n Args:\n url: The URL string to validate and normalize\n\n Returns:\n str: The normalized URL\n\n Raises:\n ValueError: If the URL is invalid\n \"\"\"\n url = url.strip()\n if not url.startswith((\"http://\", \"https://\")):\n url = \"https://\" + url\n\n if not self.validate_url(url):\n msg = f\"Invalid URL: {url}\"\n raise ValueError(msg)\n\n return url\n\n def _create_loader(self, url: str) -> RecursiveUrlLoader:\n \"\"\"Creates a RecursiveUrlLoader instance with the configured settings.\n\n Args:\n url: The URL to load\n\n Returns:\n RecursiveUrlLoader: Configured loader instance\n \"\"\"\n headers_dict = {header[\"key\"]: header[\"value\"] for header in self.headers}\n extractor = (lambda x: x) if self.format == \"HTML\" else (lambda x: BeautifulSoup(x, \"lxml\").get_text())\n\n return RecursiveUrlLoader(\n url=url,\n max_depth=self.max_depth,\n prevent_outside=self.prevent_outside,\n use_async=self.use_async,\n extractor=extractor,\n timeout=self.timeout,\n headers=headers_dict,\n check_response_status=self.check_response_status,\n continue_on_failure=self.continue_on_failure,\n base_url=url, # Add base_url to ensure consistent domain crawling\n autoset_encoding=self.autoset_encoding, # Enable automatic encoding detection\n exclude_dirs=[], # Allow customization of excluded directories\n link_regex=None, # Allow customization of link filtering\n )\n\n def fetch_url_contents(self) -> list[dict]:\n \"\"\"Load documents from the configured URLs.\n\n Returns:\n List[Data]: List of Data objects containing the fetched content\n\n Raises:\n ValueError: If no valid URLs are provided or if there's an error loading documents\n \"\"\"\n try:\n urls = list({self.ensure_url(url) for url in self.urls if url.strip()})\n logger.info(f\"URLs: {urls}\")\n if not urls:\n msg = \"No valid URLs provided.\"\n raise ValueError(msg)\n\n all_docs = []\n for url in urls:\n logger.info(f\"Loading documents from {url}\")\n\n try:\n loader = self._create_loader(url)\n docs = loader.load()\n\n if not docs:\n logger.warning(f\"No documents found for {url}\")\n continue\n\n logger.info(f\"Found {len(docs)} documents from {url}\")\n all_docs.extend(docs)\n\n except requests.exceptions.RequestException as e:\n logger.exception(f\"Error loading documents from {url}: {e}\")\n continue\n\n if not all_docs:\n msg = \"No documents were successfully loaded from any URL\"\n raise ValueError(msg)\n\n # data = [Data(text=doc.page_content, **doc.metadata) for doc in all_docs]\n data = [\n {\n \"text\": safe_convert(doc.page_content, clean_data=True),\n \"url\": doc.metadata.get(\"source\", \"\"),\n \"title\": doc.metadata.get(\"title\", \"\"),\n \"description\": doc.metadata.get(\"description\", \"\"),\n \"content_type\": doc.metadata.get(\"content_type\", \"\"),\n \"language\": doc.metadata.get(\"language\", \"\"),\n }\n for doc in all_docs\n ]\n except Exception as e:\n error_msg = e.message if hasattr(e, \"message\") else e\n msg = f\"Error loading documents: {error_msg!s}\"\n logger.exception(msg)\n raise ValueError(msg) from e\n return data\n\n def fetch_content(self) -> DataFrame:\n \"\"\"Convert the documents to a DataFrame.\"\"\"\n return DataFrame(data=self.fetch_url_contents())\n\n def as_message(self) -> Message:\n \"\"\"Convert the documents to a Message.\"\"\"\n url_contents = self.fetch_url_contents()\n return Message(text=\"\\n\\n\".join([x[\"text\"] for x in url_contents]), data={\"data\": url_contents})\n" + "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" }, - "continue_on_failure": { + "handle_parsing_errors": { "_input_type": "BoolInput", "advanced": true, - "display_name": "Continue on Failure", + "display_name": "Handle Parse Errors", "dynamic": false, - "info": "If enabled, continues crawling even if some requests fail.", + "info": "Should the Agent fix errors when reading user input for better processing?", "list": false, "list_add_label": "Add More", - "name": "continue_on_failure", + "name": "handle_parsing_errors", "placeholder": "", "required": false, "show": true, @@ -1099,268 +1190,143 @@ "type": "bool", "value": true }, - "filter_text_html": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Filter Text/HTML", + "input_value": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Input", "dynamic": false, - "info": "If enabled, filters out text/css content type from the results.", + "info": "The input provided by the user for the agent to process.", + "input_types": [ + "Message" + ], "list": false, "list_add_label": "Add More", - "name": "filter_text_html", + "load_from_db": false, + "name": "input_value", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": false, + "tool_mode": true, + "trace_as_input": true, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "str", + "value": "" }, - "format": { - "_input_type": "DropdownInput", + "json_mode": { + "_input_type": "BoolInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Output Format", + "display_name": "JSON Mode", "dynamic": false, - "info": "Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.", - "name": "format", - "options": [ - "Text", - "HTML" - ], - "options_metadata": [], + "info": "If True, it will output JSON regardless of passing a schema.", + "list": false, + "list_add_label": "Add More", + "name": "json_mode", "placeholder": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "Text" + "type": "bool", + "value": false }, - "headers": { - "_input_type": "TableInput", + "max_iterations": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Headers", + "display_name": "Max Iterations", "dynamic": false, - "info": "The headers to send with the request", - "input_types": [ - "DataFrame" - ], - "is_list": true, + "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": "headers", + "name": "max_iterations", "placeholder": "", "required": false, "show": true, - "table_icon": "Table", - "table_schema": { - "columns": [ - { - "default": "None", - "description": "Header name", - "disable_edit": false, - "display_name": "Header", - "edit_mode": "popover", - "filterable": true, - "formatter": "text", - "hidden": false, - "name": "key", - "sortable": true, - "type": "str" - }, - { - "default": "None", - "description": "Header value", - "disable_edit": false, - "display_name": "Value", - "edit_mode": "popover", - "filterable": true, - "formatter": "text", - "hidden": false, - "name": "value", - "sortable": true, - "type": "str" - } - ] - }, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "trigger_icon": "Table", - "trigger_text": "Open table", - "type": "table", - "value": [ - { - "key": "User-Agent", - "value": "langflow" - } - ] - }, - "max_depth": { - "_input_type": "SliderInput", - "advanced": false, - "display_name": "Depth", - "dynamic": false, - "info": "Controls how many 'clicks' away from the initial page the crawler will go:\n- depth 1: only the initial page\n- depth 2: initial page + all pages linked directly from it\n- depth 3: initial page + direct links + links found on those direct link pages\nNote: This is about link traversal, not URL path depth.", - "max_label": " ", - "max_label_icon": "None", - "min_label": " ", - "min_label_icon": "None", - "name": "max_depth", - "placeholder": "", - "range_spec": { - "max": 5.0, - "min": 1.0, - "step": 1.0, - "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": 1 + "type": "int", + "value": 15 }, - "prevent_outside": { - "_input_type": "BoolInput", + "max_retries": { + "_input_type": "IntInput", "advanced": true, - "display_name": "Prevent Outside", + "display_name": "Max Retries", "dynamic": false, - "info": "If enabled, only crawls URLs within the same domain as the root URL. This helps prevent the crawler from going to external websites.", + "info": "The maximum number of retries to make when generating.", "list": false, "list_add_label": "Add More", - "name": "prevent_outside", + "name": "max_retries", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "int", + "value": 5 }, - "timeout": { + "max_tokens": { "_input_type": "IntInput", "advanced": true, - "display_name": "Timeout", + "display_name": "Max Tokens", "dynamic": false, - "info": "Timeout for the request in seconds.", + "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", "list": false, "list_add_label": "Add More", - "name": "timeout", + "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": 30 + "value": "" }, - "tools_metadata": { - "_input_type": "ToolsInput", - "advanced": false, - "display_name": "Actions", + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", "dynamic": false, - "info": "Modify tool names and descriptions to help agents understand when to use each tool.", - "is_list": true, + "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": "tools_metadata", + "name": "memory", "placeholder": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, - "tool_mode": false, "trace_as_metadata": true, - "type": "tools", - "value": [ - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. fetch_content() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. fetch_content() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "fetch_content", - "name": "fetch_content", - "status": true, - "tags": [ - "fetch_content" - ] - }, - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. fetch_content_text() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. fetch_content_text() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "fetch_content_text", - "name": "fetch_content_text", - "status": true, - "tags": [ - "fetch_content_text" - ] - }, - { - "args": { - "urls": { - "default": "", - "description": "", - "items": { - "type": "string" - }, - "title": "Urls", - "type": "array" - } - }, - "description": "URL. as_dataframe() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_description": "URL. as_dataframe() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", - "display_name": "as_dataframe", - "name": "as_dataframe", - "status": true, - "tags": [ - "as_dataframe" - ] - } - ] + "type": "other", + "value": "" }, - "urls": { + "message": { "_input_type": "MessageTextInput", - "advanced": false, - "display_name": "URLs", - "dynamic": false, - "info": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", - "input_types": [], - "list": true, - "list_add_label": "Add URL", + "advanced": true, + "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": "urls", - "placeholder": "Enter a URL...", + "name": "message", + "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": true, "trace_as_input": true, @@ -1368,311 +1334,261 @@ "type": "str", "value": "" }, - "use_async": { - "_input_type": "BoolInput", + "mode": { + "_input_type": "TabInput", "advanced": true, - "display_name": "Use Async", + "display_name": "Mode", "dynamic": false, - "info": "If enabled, uses asynchronous loading which can be significantly faster but might use more system resources.", - "list": false, - "list_add_label": "Add More", - "name": "use_async", + "info": "Operation mode: Store messages or Retrieve messages.", + "load_from_db": false, + "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": "bool", - "value": true - } - }, - "tool_mode": true - }, - "selected_output": "component_as_tool", - "showNode": true, - "type": "URL" - }, - "dragging": false, - "id": "URL-4LSRv", - "measured": { - "height": 523, - "width": 320 - }, - "position": { - "x": 2815.5187953791974, - "y": 949.802524756467 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "CalculatorComponent-hlWP4", - "node": { - "base_classes": [ - "Data" - ], - "beta": false, - "category": "tools", - "conditional_paths": [], - "custom_fields": {}, - "description": "Perform basic arithmetic operations on a given expression.", - "display_name": "Calculator", - "documentation": "", - "edited": false, - "field_order": [ - "expression" - ], - "frozen": false, - "icon": "calculator", - "key": "CalculatorComponent", - "legacy": false, - "lf_version": "1.2.0", - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Toolset", - "hidden": false, - "method": "to_toolkit", - "name": "component_as_tool", - "options": null, - "required_inputs": null, - "selected": "Tool", - "tool_mode": true, - "types": [ - "Tool" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 0.001, - "template": { - "_type": "Component", - "code": { + "type": "tab", + "value": "Store" + }, + "model_kwargs": { + "_input_type": "DictInput", "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", + "display_name": "Model Kwargs", + "dynamic": false, + "info": "Additional keyword arguments to pass to the model.", "list": false, - "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, + "list_add_label": "Add More", + "name": "model_kwargs", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "code", - "value": "import ast\nimport operator\nfrom collections.abc import Callable\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import MessageTextInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\n\n\nclass CalculatorComponent(Component):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n\n # Cache operators dictionary as a class variable\n OPERATORS: dict[type[ast.operator], Callable] = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"result\", type_=Data, method=\"evaluate_expression\"),\n ]\n\n def _eval_expr(self, node: ast.AST) -> float:\n \"\"\"Evaluate an AST node recursively.\"\"\"\n if isinstance(node, ast.Constant):\n if isinstance(node.value, int | float):\n return float(node.value)\n error_msg = f\"Unsupported constant type: {type(node.value).__name__}\"\n raise TypeError(error_msg)\n if isinstance(node, ast.Num): # For backwards compatibility\n if isinstance(node.n, int | float):\n return float(node.n)\n error_msg = f\"Unsupported number type: {type(node.n).__name__}\"\n raise TypeError(error_msg)\n\n if isinstance(node, ast.BinOp):\n op_type = type(node.op)\n if op_type not in self.OPERATORS:\n error_msg = f\"Unsupported binary operator: {op_type.__name__}\"\n raise TypeError(error_msg)\n\n left = self._eval_expr(node.left)\n right = self._eval_expr(node.right)\n return self.OPERATORS[op_type](left, right)\n\n error_msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(error_msg)\n\n def evaluate_expression(self) -> Data:\n \"\"\"Evaluate the mathematical expression and return the result.\"\"\"\n try:\n tree = ast.parse(self.expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n formatted_result = f\"{float(result):.6f}\".rstrip(\"0\").rstrip(\".\")\n self.log(f\"Calculation result: {formatted_result}\")\n\n self.status = formatted_result\n return Data(data={\"result\": formatted_result})\n\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n except (SyntaxError, TypeError, KeyError, ValueError, AttributeError, OverflowError) as e:\n error_message = f\"Invalid expression: {e!s}\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n def build(self):\n \"\"\"Return the main evaluation function.\"\"\"\n return self.evaluate_expression\n" + "tool_mode": false, + "trace_as_input": true, + "type": "dict", + "value": {} }, - "expression": { - "_input_type": "MessageTextInput", + "model_name": { + "_input_type": "DropdownInput", "advanced": false, - "display_name": "Expression", + "combobox": true, + "dialog_inputs": {}, + "display_name": "Model Name", "dynamic": false, - "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", + "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", "load_from_db": false, - "name": "expression", + "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": false, "required": false, "show": true, "title_case": false, - "tool_mode": true, - "trace_as_input": true, + "toggle": false, + "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "" + "value": "gpt-4.1" }, - "tools_metadata": { - "_input_type": "ToolsInput", - "advanced": false, - "display_name": "Actions", + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", "dynamic": false, - "info": "Modify tool names and descriptions to help agents understand when to use each tool.", - "is_list": true, + "info": "Number of messages to retrieve.", + "list": false, "list_add_label": "Add More", - "name": "tools_metadata", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": false, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "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": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "tools", - "value": [ - { - "args": { - "expression": { - "default": "", - "description": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", - "title": "Expression", - "type": "string" - } - }, - "description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", - "display_description": "CalculatorComponent. evaluate_expression() - Perform basic arithmetic operations on a given expression.", - "display_name": "evaluate_expression", - "name": "evaluate_expression", - "status": true, - "tags": [ - "evaluate_expression" - ] - } - ] - } - }, - "tool_mode": true - }, - "selected_output": "component_as_tool", - "showNode": true, - "type": "CalculatorComponent" - }, - "dragging": false, - "id": "CalculatorComponent-hlWP4", - "measured": { - "height": 248, - "width": 320 - }, - "position": { - "x": 3540.356346381247, - "y": 989.3563951826354 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "description": "Call the searchapi.io API with result limiting", - "display_name": "Search API", - "id": "SearchComponent-kgsWf", - "node": { - "base_classes": [ - "Data", - "DataFrame", - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Call the searchapi.io API with result limiting", - "display_name": "Search API", - "documentation": "https://www.searchapi.io/docs/google", - "edited": false, - "field_order": [ - "engine", - "api_key", - "input_value", - "search_params", - "max_results", - "max_snippet_length" - ], - "frozen": false, - "icon": "SearchAPI", - "legacy": false, - "lf_version": "1.2.0", - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Toolset", - "hidden": null, - "method": "to_toolkit", - "name": "component_as_tool", - "options": null, - "required_inputs": null, - "selected": "Tool", - "tool_mode": true, - "types": [ - "Tool" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "SearchAPI API Key", + "type": "str", + "value": "" + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", "dynamic": false, - "info": "", - "input_types": [], - "load_from_db": false, - "name": "api_key", - "password": true, + "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": "SEARCHAPI_API_KEY" + "value": "Ascending" }, - "code": { + "seed": { + "_input_type": "IntInput", "advanced": true, - "dynamic": true, - "fileTypes": [], - "file_path": "", - "info": "", + "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 + }, + "sender": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender", + "dynamic": false, + "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", + "input_types": [ + "Message" + ], "list": false, + "list_add_label": "Add More", "load_from_db": false, - "multiline": true, - "name": "code", - "password": false, + "name": "sender", "placeholder": "", - "required": true, + "required": false, "show": true, "title_case": false, - "type": "code", - "value": "from typing import Any\n\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import DictInput, DropdownInput, IntInput, MultilineInput, SecretStrInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\n\n\nclass SearchComponent(Component):\n display_name: str = \"Search API\"\n description: str = \"Call the searchapi.io API with result limiting\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n icon = \"SearchAPI\"\n\n inputs = [\n DropdownInput(name=\"engine\", display_name=\"Engine\", value=\"google\", options=[\"google\", \"bing\", \"duckduckgo\"]),\n SecretStrInput(name=\"api_key\", display_name=\"SearchAPI API Key\", required=True),\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input\",\n tool_mode=True,\n ),\n DictInput(name=\"search_params\", display_name=\"Search parameters\", advanced=True, is_list=True),\n IntInput(name=\"max_results\", display_name=\"Max Results\", value=5, advanced=True),\n IntInput(name=\"max_snippet_length\", display_name=\"Max Snippet Length\", value=100, advanced=True),\n ]\n\n outputs = [\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"fetch_content_dataframe\"),\n ]\n\n def _build_wrapper(self):\n return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)\n\n def run_model(self) -> DataFrame:\n return self.fetch_content_dataframe()\n\n def fetch_content(self) -> list[Data]:\n wrapper = self._build_wrapper()\n\n def search_func(\n query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100\n ) -> list[Data]:\n params = params or {}\n full_results = wrapper.results(query=query, **params)\n organic_results = full_results.get(\"organic_results\", [])[:max_results]\n\n return [\n Data(\n text=result.get(\"snippet\", \"\"),\n data={\n \"title\": result.get(\"title\", \"\")[:max_snippet_length],\n \"link\": result.get(\"link\", \"\"),\n \"snippet\": result.get(\"snippet\", \"\")[:max_snippet_length],\n },\n )\n for result in organic_results\n ]\n\n results = search_func(\n self.input_value,\n self.search_params or {},\n self.max_results,\n self.max_snippet_length,\n )\n self.status = results\n return results\n\n def fetch_content_dataframe(self) -> DataFrame:\n \"\"\"Convert the search results to a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the search results.\n \"\"\"\n data = self.fetch_content()\n return DataFrame(data)\n" + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" }, - "engine": { + "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": "" + }, + "sender_type": { "_input_type": "DropdownInput", - "advanced": false, + "advanced": true, "combobox": false, "dialog_inputs": {}, - "display_name": "Engine", + "display_name": "Sender Type", "dynamic": false, - "info": "", - "name": "engine", + "info": "Filter by sender type.", + "name": "sender_type", "options": [ - "google", - "bing", - "duckduckgo" + "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": "google" + "value": "Machine and User" }, - "input_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": "" + }, + "system_prompt": { "_input_type": "MultilineInput", "advanced": false, "copy_field": false, - "display_name": "Input", + "display_name": "Agent Instructions", "dynamic": false, - "info": "", + "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", "input_types": [ "Message" ], @@ -1680,44 +1596,79 @@ "list_add_label": "Add More", "load_from_db": false, "multiline": true, - "name": "input_value", + "name": "system_prompt", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": true, + "tool_mode": false, "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "" + "value": "You are a helpful assistant that can use tools to answer questions and perform tasks." }, - "max_results": { - "_input_type": "IntInput", + "temperature": { + "_input_type": "SliderInput", "advanced": true, - "display_name": "Max Results", + "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 + }, + "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", - "name": "max_results", + "load_from_db": false, + "multiline": true, + "name": "template", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, - "type": "int", - "value": 5 + "type": "str", + "value": "{sender_name}: {text}" }, - "max_snippet_length": { + "timeout": { "_input_type": "IntInput", "advanced": true, - "display_name": "Max Snippet Length", + "display_name": "Timeout", "dynamic": false, - "info": "", + "info": "The timeout for requests to OpenAI completion API.", "list": false, "list_add_label": "Add More", - "name": "max_snippet_length", + "name": "timeout", "placeholder": "", "required": false, "show": true, @@ -1725,112 +1676,78 @@ "tool_mode": false, "trace_as_metadata": true, "type": "int", - "value": 100 + "value": 700 }, - "search_params": { - "_input_type": "DictInput", - "advanced": true, - "display_name": "Search parameters", + "tools": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Tools", "dynamic": false, - "info": "", + "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": "search_params", + "name": "tools", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "dict", - "value": {} + "trace_as_metadata": true, + "type": "other", + "value": "" }, - "tools_metadata": { - "_input_type": "ToolsInput", - "advanced": false, - "display_name": "Actions", + "verbose": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Verbose", "dynamic": false, - "info": "Modify tool names and descriptions to help agents understand when to use each tool.", - "is_list": true, + "info": "", + "list": false, "list_add_label": "Add More", - "name": "tools_metadata", + "name": "verbose", "placeholder": "", - "real_time_refresh": true, "required": false, "show": true, "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "tools", - "value": [ - { - "args": {}, - "description": "SearchComponent. fetch_content(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_description": "SearchComponent. fetch_content(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_name": "fetch_content", - "name": "fetch_content", - "status": true, - "tags": [ - "fetch_content" - ] - }, - { - "args": {}, - "description": "SearchComponent. fetch_content_text(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_description": "SearchComponent. fetch_content_text(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_name": "fetch_content_text", - "name": "fetch_content_text", - "status": true, - "tags": [ - "fetch_content_text" - ] - }, - { - "args": {}, - "description": "SearchComponent. as_dataframe(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_description": "SearchComponent. as_dataframe(SearchComponent. api_key: FieldTypes.TEXT) - Call the searchapi.io API with result limiting", - "display_name": "as_dataframe", - "name": "as_dataframe", - "status": true, - "tags": [ - "as_dataframe" - ] - } - ] + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true } }, - "tool_mode": true + "tool_mode": false }, - "selected_output": "component_as_tool", + "selected_output": "response", "showNode": true, - "type": "SearchComponent" + "type": "Agent" }, "dragging": false, - "id": "SearchComponent-kgsWf", + "id": "Agent-SVMdS", "measured": { - "height": 421, + "height": 591, "width": 320 }, "position": { - "x": 2089.0393126914205, - "y": 894.9349094163676 + "x": 2462.5131863965653, + "y": 338.2948031203553 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "Agent-H916j", + "id": "Agent-Huf0O", "node": { "base_classes": [ "Message" ], "beta": false, - "category": "agents", "conditional_paths": [], "custom_fields": {}, "description": "Define the agent's instructions, then enter a task to complete using tools.", - "display_name": "City Selection Agent", + "display_name": "Agent", "documentation": "", "edited": false, "field_order": [ @@ -1852,7 +1769,10 @@ "verbose", "max_iterations", "agent_description", + "mode", + "message", "memory", + "sender_type", "sender", "sender_name", "n_messages", @@ -1863,9 +1783,7 @@ ], "frozen": false, "icon": "bot", - "key": "Agent", "legacy": false, - "lf_version": "1.2.0", "metadata": {}, "minimized": false, "output_types": [], @@ -1874,8 +1792,11 @@ "allows_loop": false, "cache": true, "display_name": "Response", + "group_outputs": false, "method": "message_response", "name": "response", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1885,7 +1806,6 @@ } ], "pinned": false, - "score": 1.1732828199964098e-19, "template": { "_type": "Component", "add_current_date_tool": { @@ -1942,37 +1862,29 @@ "input_types": [], "name": "agent_llm", "options": [ + "Amazon Bedrock", "Anthropic", + "Azure OpenAI", "Google Generative AI", "Groq", + "NVIDIA", "OpenAI", + "SambaNova", "Custom" ], "options_metadata": [ - { - "icon": "Amazon" - }, { "icon": "Anthropic" }, - { - "icon": "Azure" - }, { "icon": "GoogleGenerativeAI" }, { "icon": "Groq" }, - { - "icon": "NVIDIA" - }, { "icon": "OpenAI" }, - { - "icon": "SambaNova" - }, { "icon": "brain" } @@ -1982,6 +1894,7 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", @@ -1998,11 +1911,12 @@ "name": "api_key", "password": true, "placeholder": "", + "real_time_refresh": true, "required": true, "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -2165,7 +2079,7 @@ "_input_type": "MessageTextInput", "advanced": true, "display_name": "Message", - "dynamic": false, + "dynamic": true, "info": "The chat message to be stored.", "input_types": [ "Message" @@ -2176,7 +2090,7 @@ "name": "message", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": true, "trace_as_input": true, @@ -2190,6 +2104,7 @@ "display_name": "Mode", "dynamic": false, "info": "Operation mode: Store messages or Retrieve messages.", + "load_from_db": false, "name": "mode", "options": [ "Retrieve", @@ -2231,6 +2146,7 @@ "display_name": "Model Name", "dynamic": false, "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", + "load_from_db": false, "name": "model_name", "options": [ "gpt-4o-mini", @@ -2251,6 +2167,7 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", @@ -2267,7 +2184,7 @@ "name": "n_messages", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_metadata": true, @@ -2311,6 +2228,7 @@ "required": true, "show": true, "title_case": false, + "toggle": false, "tool_mode": true, "trace_as_metadata": true, "type": "str", @@ -2335,25 +2253,24 @@ "value": 1 }, "sender": { - "_input_type": "DropdownInput", + "_input_type": "MessageTextInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, "display_name": "Sender", "dynamic": false, "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" + "input_types": [ + "Message" ], - "options_metadata": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "Machine and User" @@ -2373,7 +2290,7 @@ "name": "sender_name", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -2452,7 +2369,7 @@ "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "You are a helpful assistant that can use tools to answer questions and perform tasks." + "value": "You are a knowledgeable Local Expert with extensive information about the selected city, its attractions, and customs. Your goal is to provide the BEST insights about the city. Compile an in-depth guide for travelers, including key attractions, local customs, special events, and daily activity recommendations. Focus on hidden gems and local hotspots. Your final output should be a comprehensive city guide, rich in cultural insights and practical tips." }, "temperature": { "_input_type": "SliderInput", @@ -2499,7 +2416,7 @@ "name": "template", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -2571,31 +2488,30 @@ "type": "Agent" }, "dragging": false, - "id": "Agent-H916j", + "id": "Agent-Huf0O", "measured": { - "height": 624, + "height": 591, "width": 320 }, "position": { - "x": 2462.5131863965653, - "y": 338.2948031203553 + "x": 3168.5639981397626, + "y": 342.7862450828752 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "Agent-zFKST", + "id": "Agent-jiGgJ", "node": { "base_classes": [ "Message" ], "beta": false, - "category": "agents", "conditional_paths": [], "custom_fields": {}, "description": "Define the agent's instructions, then enter a task to complete using tools.", - "display_name": "Local Expert Agent", + "display_name": "Agent", "documentation": "", "edited": false, "field_order": [ @@ -2617,7 +2533,10 @@ "verbose", "max_iterations", "agent_description", + "mode", + "message", "memory", + "sender_type", "sender", "sender_name", "n_messages", @@ -2628,9 +2547,7 @@ ], "frozen": false, "icon": "bot", - "key": "Agent", "legacy": false, - "lf_version": "1.2.0", "metadata": {}, "minimized": false, "output_types": [], @@ -2639,8 +2556,11 @@ "allows_loop": false, "cache": true, "display_name": "Response", + "group_outputs": false, "method": "message_response", "name": "response", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -2650,7 +2570,6 @@ } ], "pinned": false, - "score": 1.1732828199964098e-19, "template": { "_type": "Component", "add_current_date_tool": { @@ -2707,37 +2626,29 @@ "input_types": [], "name": "agent_llm", "options": [ + "Amazon Bedrock", "Anthropic", + "Azure OpenAI", "Google Generative AI", "Groq", + "NVIDIA", "OpenAI", + "SambaNova", "Custom" ], "options_metadata": [ - { - "icon": "Amazon" - }, { "icon": "Anthropic" }, - { - "icon": "Azure" - }, { "icon": "GoogleGenerativeAI" }, { "icon": "Groq" }, - { - "icon": "NVIDIA" - }, { "icon": "OpenAI" }, - { - "icon": "SambaNova" - }, { "icon": "brain" } @@ -2747,6 +2658,7 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", @@ -2763,11 +2675,12 @@ "name": "api_key", "password": true, "placeholder": "", + "real_time_refresh": true, "required": true, "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -2930,7 +2843,7 @@ "_input_type": "MessageTextInput", "advanced": true, "display_name": "Message", - "dynamic": false, + "dynamic": true, "info": "The chat message to be stored.", "input_types": [ "Message" @@ -2941,7 +2854,7 @@ "name": "message", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": true, "trace_as_input": true, @@ -2955,6 +2868,7 @@ "display_name": "Mode", "dynamic": false, "info": "Operation mode: Store messages or Retrieve messages.", + "load_from_db": false, "name": "mode", "options": [ "Retrieve", @@ -2996,6 +2910,7 @@ "display_name": "Model Name", "dynamic": false, "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", + "load_from_db": false, "name": "model_name", "options": [ "gpt-4o-mini", @@ -3016,6 +2931,7 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", @@ -3032,7 +2948,7 @@ "name": "n_messages", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_metadata": true, @@ -3076,6 +2992,7 @@ "required": true, "show": true, "title_case": false, + "toggle": false, "tool_mode": true, "trace_as_metadata": true, "type": "str", @@ -3100,25 +3017,24 @@ "value": 1 }, "sender": { - "_input_type": "DropdownInput", + "_input_type": "MessageTextInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, "display_name": "Sender", "dynamic": false, "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" + "input_types": [ + "Message" ], - "options_metadata": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "Machine and User" @@ -3138,7 +3054,7 @@ "name": "sender_name", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -3217,7 +3133,7 @@ "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "You are a knowledgeable Local Expert with extensive information about the selected city, its attractions, and customs. Your goal is to provide the BEST insights about the city. Compile an in-depth guide for travelers, including key attractions, local customs, special events, and daily activity recommendations. Focus on hidden gems and local hotspots. Your final output should be a comprehensive city guide, rich in cultural insights and practical tips." + "value": "You are an Amazing Travel Concierge, a specialist in travel planning and logistics with decades of experience. Your goal is to create the most amazing travel itineraries with budget and packing suggestions for the city. Expand the city guide into a full 7-day travel itinerary with detailed per-day plans. Include weather forecasts, places to eat, packing suggestions, and a budget breakdown. Suggest actual places to visit, hotels to stay, and restaurants to go to. Your final output should be a complete expanded travel plan, formatted as markdown, encompassing a daily schedule, anticipated weather conditions, recommended clothing and items to pack, and a detailed budget." }, "temperature": { "_input_type": "SliderInput", @@ -3264,7 +3180,7 @@ "name": "template", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -3336,66 +3252,45 @@ "type": "Agent" }, "dragging": false, - "id": "Agent-zFKST", + "id": "Agent-jiGgJ", "measured": { - "height": 624, + "height": 591, "width": 320 }, "position": { - "x": 3168.5639981397626, - "y": 342.7862450828752 + "x": 3936.123532016018, + "y": 356.3534774464019 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "Agent-3tuWs", + "id": "SearchComponent-3nVDj", "node": { "base_classes": [ - "Message" + "DataFrame" ], "beta": false, - "category": "agents", + "category": "search", "conditional_paths": [], "custom_fields": {}, - "description": "Define the agent's instructions, then enter a task to complete using tools.", - "display_name": "Travel Concierge Agent", - "documentation": "", + "description": "Call the searchapi.io API with result limiting", + "display_name": "Search API", + "documentation": "https://www.searchapi.io/docs/google", "edited": false, "field_order": [ - "agent_llm", - "max_tokens", - "model_kwargs", - "json_mode", - "model_name", - "openai_api_base", + "engine", "api_key", - "temperature", - "seed", - "max_retries", - "timeout", - "system_prompt", - "tools", "input_value", - "handle_parsing_errors", - "verbose", - "max_iterations", - "agent_description", - "memory", - "sender", - "sender_name", - "n_messages", - "session_id", - "order", - "template", - "add_current_date_tool" + "search_params", + "max_results", + "max_snippet_length" ], "frozen": false, - "icon": "bot", - "key": "Agent", + "icon": "SearchAPI", + "key": "SearchComponent", "legacy": false, - "lf_version": "1.2.0", "metadata": {}, "minimized": false, "output_types": [], @@ -3403,128 +3298,33 @@ { "allows_loop": false, "cache": true, - "display_name": "Response", - "method": "message_response", - "name": "response", - "selected": "Message", + "display_name": "Toolset", + "group_outputs": false, + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "options": null, + "required_inputs": null, + "selected": "Tool", "tool_mode": true, "types": [ - "Message" + "Tool" ], "value": "__UNDEFINED__" } ], - "pinned": false, - "score": 1.1732828199964098e-19, - "template": { - "_type": "Component", - "add_current_date_tool": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Current Date", - "dynamic": false, - "info": "If true, will add a tool to the agent that returns the current date.", - "list": false, - "list_add_label": "Add More", - "name": "add_current_date_tool", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "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:" - }, - "agent_llm": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Provider", - "dynamic": false, - "info": "The provider of the language model that the agent will use to generate responses.", - "input_types": [], - "name": "agent_llm", - "options": [ - "Anthropic", - "Google Generative AI", - "Groq", - "OpenAI", - "Custom" - ], - "options_metadata": [ - { - "icon": "Amazon" - }, - { - "icon": "Anthropic" - }, - { - "icon": "Azure" - }, - { - "icon": "GoogleGenerativeAI" - }, - { - "icon": "Groq" - }, - { - "icon": "NVIDIA" - }, - { - "icon": "OpenAI" - }, - { - "icon": "SambaNova" - }, - { - "icon": "brain" - } - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "OpenAI" - }, + "pinned": false, + "score": 4.574062563799057e-12, + "template": { + "_type": "Component", "api_key": { "_input_type": "SecretStrInput", "advanced": false, - "display_name": "OpenAI API Key", + "display_name": "SearchAPI API Key", "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", + "info": "", "input_types": [], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -3532,7 +3332,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -3550,38 +3350,47 @@ "show": true, "title_case": false, "type": "code", - "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" + "value": "from typing import Any\n\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import DictInput, DropdownInput, IntInput, MultilineInput, SecretStrInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\n\n\nclass SearchComponent(Component):\n display_name: str = \"Search API\"\n description: str = \"Call the searchapi.io API with result limiting\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n icon = \"SearchAPI\"\n\n inputs = [\n DropdownInput(name=\"engine\", display_name=\"Engine\", value=\"google\", options=[\"google\", \"bing\", \"duckduckgo\"]),\n SecretStrInput(name=\"api_key\", display_name=\"SearchAPI API Key\", required=True),\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input\",\n tool_mode=True,\n ),\n DictInput(name=\"search_params\", display_name=\"Search parameters\", advanced=True, is_list=True),\n IntInput(name=\"max_results\", display_name=\"Max Results\", value=5, advanced=True),\n IntInput(name=\"max_snippet_length\", display_name=\"Max Snippet Length\", value=100, advanced=True),\n ]\n\n outputs = [\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"fetch_content_dataframe\"),\n ]\n\n def _build_wrapper(self):\n return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)\n\n def run_model(self) -> DataFrame:\n return self.fetch_content_dataframe()\n\n def fetch_content(self) -> list[Data]:\n wrapper = self._build_wrapper()\n\n def search_func(\n query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100\n ) -> list[Data]:\n params = params or {}\n full_results = wrapper.results(query=query, **params)\n organic_results = full_results.get(\"organic_results\", [])[:max_results]\n\n return [\n Data(\n text=result.get(\"snippet\", \"\"),\n data={\n \"title\": result.get(\"title\", \"\")[:max_snippet_length],\n \"link\": result.get(\"link\", \"\"),\n \"snippet\": result.get(\"snippet\", \"\")[:max_snippet_length],\n },\n )\n for result in organic_results\n ]\n\n results = search_func(\n self.input_value,\n self.search_params or {},\n self.max_results,\n self.max_snippet_length,\n )\n self.status = results\n return results\n\n def fetch_content_dataframe(self) -> DataFrame:\n \"\"\"Convert the search results to a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the search results.\n \"\"\"\n data = self.fetch_content()\n return DataFrame(data)\n" }, - "handle_parsing_errors": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Handle Parse Errors", + "engine": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Engine", "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", + "info": "", + "name": "engine", + "options": [ + "google", + "bing", + "duckduckgo" + ], + "options_metadata": [], "placeholder": "", "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, - "type": "bool", - "value": true + "type": "str", + "value": "google" }, "input_value": { - "_input_type": "MessageTextInput", + "_input_type": "MultilineInput", "advanced": false, + "copy_field": false, "display_name": "Input", "dynamic": false, - "info": "The input provided by the user for the agent to process.", + "info": "", "input_types": [ "Message" ], "list": false, "list_add_label": "Add More", "load_from_db": false, + "multiline": true, "name": "input_value", "placeholder": "", "required": false, @@ -3593,51 +3402,15 @@ "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_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 - }, - "max_retries": { + "max_results": { "_input_type": "IntInput", "advanced": true, - "display_name": "Max Retries", + "display_name": "Max Results", "dynamic": false, - "info": "The maximum number of retries to make when generating.", + "info": "", "list": false, "list_add_label": "Add More", - "name": "max_retries", + "name": "max_results", "placeholder": "", "required": false, "show": true, @@ -3647,84 +3420,51 @@ "type": "int", "value": 5 }, - "max_tokens": { + "max_snippet_length": { "_input_type": "IntInput", "advanced": true, - "display_name": "Max Tokens", + "display_name": "Max Snippet Length", "dynamic": false, - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "info": "", "list": false, "list_add_label": "Add More", - "name": "max_tokens", + "name": "max_snippet_length", "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": "" - }, - "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": "" + "value": 100 }, - "message": { - "_input_type": "MessageTextInput", + "search_params": { + "_input_type": "DictInput", "advanced": true, - "display_name": "Message", + "display_name": "Search parameters", "dynamic": false, - "info": "The chat message to be stored.", - "input_types": [ - "Message" - ], - "list": false, + "info": "", + "list": true, "list_add_label": "Add More", - "load_from_db": false, - "name": "message", + "name": "search_params", "placeholder": "", "required": false, "show": true, "title_case": false, - "tool_mode": true, + "tool_mode": false, "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "dict", + "value": {} }, - "mode": { - "_input_type": "TabInput", - "advanced": true, - "display_name": "Mode", + "tools_metadata": { + "_input_type": "ToolsInput", + "advanced": false, + "display_name": "Actions", "dynamic": false, - "info": "Operation mode: Store messages or Retrieve messages.", - "name": "mode", - "options": [ - "Retrieve", - "Store" - ], + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", + "is_list": true, + "list_add_label": "Add More", + "name": "tools_metadata", "placeholder": "", "real_time_refresh": true, "required": false, @@ -3732,198 +3472,209 @@ "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "tab", - "value": "Store" - }, - "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": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", - "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" + "type": "tools", + "value": [ + { + "args": { + "input_value": { + "default": "", + "description": "", + "title": "Input Value", + "type": "string" + } + }, + "description": "Call the searchapi.io API with result limiting", + "display_description": "Call the searchapi.io API with result limiting", + "display_name": "fetch_content_dataframe", + "name": "fetch_content_dataframe", + "readonly": false, + "status": true, + "tags": [ + "fetch_content_dataframe" + ] + } + ] + } + }, + "tool_mode": true + }, + "showNode": true, + "type": "SearchComponent" + }, + "dragging": false, + "id": "SearchComponent-3nVDj", + "measured": { + "height": 365, + "width": 320 + }, + "position": { + "x": 2093.1263360559856, + "y": 887.965322905591 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "URLComponent-8bgTS", + "node": { + "base_classes": [ + "DataFrame", + "Message" + ], + "beta": false, + "category": "data", + "conditional_paths": [], + "custom_fields": {}, + "description": "Fetch content from one or more web pages, following links recursively.", + "display_name": "URL", + "documentation": "", + "edited": false, + "field_order": [ + "urls", + "max_depth", + "prevent_outside", + "use_async", + "format", + "timeout", + "headers", + "filter_text_html", + "continue_on_failure", + "check_response_status", + "autoset_encoding" + ], + "frozen": false, + "icon": "layout-template", + "key": "URLComponent", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "group_outputs": false, + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "options": null, + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" ], - "options_metadata": [], - "placeholder": "", - "real_time_refresh": false, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4.1" - }, - "n_messages": { - "_input_type": "IntInput", + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 2.220446049250313e-16, + "template": { + "_type": "Component", + "autoset_encoding": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Number of Messages", + "display_name": "Autoset Encoding", "dynamic": false, - "info": "Number of messages to retrieve.", + "info": "If enabled, automatically sets the encoding of the request.", "list": false, "list_add_label": "Add More", - "name": "n_messages", + "name": "autoset_encoding", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "int", - "value": 100 + "type": "bool", + "value": true }, - "openai_api_base": { - "_input_type": "StrInput", + "check_response_status": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "OpenAI API Base", + "display_name": "Check Response Status", "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.", + "info": "If enabled, checks the response status of the request.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "openai_api_base", + "name": "check_response_status", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "bool", + "value": false }, - "order": { - "_input_type": "DropdownInput", + "code": { "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Order", - "dynamic": false, - "info": "Order of the messages.", - "name": "order", - "options": [ - "Ascending", - "Descending" - ], - "options_metadata": [], + "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, - "tool_mode": true, - "trace_as_metadata": true, - "type": "str", - "value": "Ascending" + "type": "code", + "value": "import re\n\nimport requests\nfrom bs4 import BeautifulSoup\nfrom langchain_community.document_loaders import RecursiveUrlLoader\nfrom loguru import logger\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.helpers.data import safe_convert\nfrom langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SliderInput, TableInput\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.services.deps import get_settings_service\n\n# Constants\nDEFAULT_TIMEOUT = 30\nDEFAULT_MAX_DEPTH = 1\nDEFAULT_FORMAT = \"Text\"\nURL_REGEX = re.compile(\n r\"^(https?:\\/\\/)?\"\n r\"(www\\.)?\"\n r\"([a-zA-Z0-9.-]+)\"\n r\"(\\.[a-zA-Z]{2,})?\"\n r\"(:\\d+)?\"\n r\"(\\/[^\\s]*)?$\",\n re.IGNORECASE,\n)\n\n\nclass URLComponent(Component):\n \"\"\"A component that loads and parses content from web pages recursively.\n\n This component allows fetching content from one or more URLs, with options to:\n - Control crawl depth\n - Prevent crawling outside the root domain\n - Use async loading for better performance\n - Extract either raw HTML or clean text\n - Configure request headers and timeouts\n \"\"\"\n\n display_name = \"URL\"\n description = \"Fetch content from one or more web pages, following links recursively.\"\n icon = \"layout-template\"\n name = \"URLComponent\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs to crawl recursively, by clicking the '+' button.\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n input_types=[],\n ),\n SliderInput(\n name=\"max_depth\",\n display_name=\"Depth\",\n info=(\n \"Controls how many 'clicks' away from the initial page the crawler will go:\\n\"\n \"- depth 1: only the initial page\\n\"\n \"- depth 2: initial page + all pages linked directly from it\\n\"\n \"- depth 3: initial page + direct links + links found on those direct link pages\\n\"\n \"Note: This is about link traversal, not URL path depth.\"\n ),\n value=DEFAULT_MAX_DEPTH,\n range_spec=RangeSpec(min=1, max=5, step=1),\n required=False,\n min_label=\" \",\n max_label=\" \",\n min_label_icon=\"None\",\n max_label_icon=\"None\",\n # slider_input=True\n ),\n BoolInput(\n name=\"prevent_outside\",\n display_name=\"Prevent Outside\",\n info=(\n \"If enabled, only crawls URLs within the same domain as the root URL. \"\n \"This helps prevent the crawler from going to external websites.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"use_async\",\n display_name=\"Use Async\",\n info=(\n \"If enabled, uses asynchronous loading which can be significantly faster \"\n \"but might use more system resources.\"\n ),\n value=True,\n required=False,\n advanced=True,\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=\"Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.\",\n options=[\"Text\", \"HTML\"],\n value=DEFAULT_FORMAT,\n advanced=True,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"Timeout for the request in seconds.\",\n value=DEFAULT_TIMEOUT,\n required=False,\n advanced=True,\n ),\n TableInput(\n name=\"headers\",\n display_name=\"Headers\",\n info=\"The headers to send with the request\",\n table_schema=[\n {\n \"name\": \"key\",\n \"display_name\": \"Header\",\n \"type\": \"str\",\n \"description\": \"Header name\",\n },\n {\n \"name\": \"value\",\n \"display_name\": \"Value\",\n \"type\": \"str\",\n \"description\": \"Header value\",\n },\n ],\n value=[{\"key\": \"User-Agent\", \"value\": get_settings_service().settings.user_agent}],\n advanced=True,\n input_types=[\"DataFrame\"],\n ),\n BoolInput(\n name=\"filter_text_html\",\n display_name=\"Filter Text/HTML\",\n info=\"If enabled, filters out text/css content type from the results.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"continue_on_failure\",\n display_name=\"Continue on Failure\",\n info=\"If enabled, continues crawling even if some requests fail.\",\n value=True,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"check_response_status\",\n display_name=\"Check Response Status\",\n info=\"If enabled, checks the response status of the request.\",\n value=False,\n required=False,\n advanced=True,\n ),\n BoolInput(\n name=\"autoset_encoding\",\n display_name=\"Autoset Encoding\",\n info=\"If enabled, automatically sets the encoding of the request.\",\n value=True,\n required=False,\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Extracted Pages\", name=\"page_results\", method=\"fetch_content\"),\n Output(display_name=\"Raw Content\", name=\"raw_results\", method=\"fetch_content_as_message\", tool_mode=False),\n ]\n\n @staticmethod\n def validate_url(url: str) -> bool:\n \"\"\"Validates if the given string matches URL pattern.\n\n Args:\n url: The URL string to validate\n\n Returns:\n bool: True if the URL is valid, False otherwise\n \"\"\"\n return bool(URL_REGEX.match(url))\n\n def ensure_url(self, url: str) -> str:\n \"\"\"Ensures the given string is a valid URL.\n\n Args:\n url: The URL string to validate and normalize\n\n Returns:\n str: The normalized URL\n\n Raises:\n ValueError: If the URL is invalid\n \"\"\"\n url = url.strip()\n if not url.startswith((\"http://\", \"https://\")):\n url = \"https://\" + url\n\n if not self.validate_url(url):\n msg = f\"Invalid URL: {url}\"\n raise ValueError(msg)\n\n return url\n\n def _create_loader(self, url: str) -> RecursiveUrlLoader:\n \"\"\"Creates a RecursiveUrlLoader instance with the configured settings.\n\n Args:\n url: The URL to load\n\n Returns:\n RecursiveUrlLoader: Configured loader instance\n \"\"\"\n headers_dict = {header[\"key\"]: header[\"value\"] for header in self.headers}\n extractor = (lambda x: x) if self.format == \"HTML\" else (lambda x: BeautifulSoup(x, \"lxml\").get_text())\n\n return RecursiveUrlLoader(\n url=url,\n max_depth=self.max_depth,\n prevent_outside=self.prevent_outside,\n use_async=self.use_async,\n extractor=extractor,\n timeout=self.timeout,\n headers=headers_dict,\n check_response_status=self.check_response_status,\n continue_on_failure=self.continue_on_failure,\n base_url=url, # Add base_url to ensure consistent domain crawling\n autoset_encoding=self.autoset_encoding, # Enable automatic encoding detection\n exclude_dirs=[], # Allow customization of excluded directories\n link_regex=None, # Allow customization of link filtering\n )\n\n def fetch_url_contents(self) -> list[dict]:\n \"\"\"Load documents from the configured URLs.\n\n Returns:\n List[Data]: List of Data objects containing the fetched content\n\n Raises:\n ValueError: If no valid URLs are provided or if there's an error loading documents\n \"\"\"\n try:\n urls = list({self.ensure_url(url) for url in self.urls if url.strip()})\n logger.info(f\"URLs: {urls}\")\n if not urls:\n msg = \"No valid URLs provided.\"\n raise ValueError(msg)\n\n all_docs = []\n for url in urls:\n logger.info(f\"Loading documents from {url}\")\n\n try:\n loader = self._create_loader(url)\n docs = loader.load()\n\n if not docs:\n logger.warning(f\"No documents found for {url}\")\n continue\n\n logger.info(f\"Found {len(docs)} documents from {url}\")\n all_docs.extend(docs)\n\n except requests.exceptions.RequestException as e:\n logger.exception(f\"Error loading documents from {url}: {e}\")\n continue\n\n if not all_docs:\n msg = \"No documents were successfully loaded from any URL\"\n raise ValueError(msg)\n\n # data = [Data(text=doc.page_content, **doc.metadata) for doc in all_docs]\n data = [\n {\n \"text\": safe_convert(doc.page_content, clean_data=True),\n \"url\": doc.metadata.get(\"source\", \"\"),\n \"title\": doc.metadata.get(\"title\", \"\"),\n \"description\": doc.metadata.get(\"description\", \"\"),\n \"content_type\": doc.metadata.get(\"content_type\", \"\"),\n \"language\": doc.metadata.get(\"language\", \"\"),\n }\n for doc in all_docs\n ]\n except Exception as e:\n error_msg = e.message if hasattr(e, \"message\") else e\n msg = f\"Error loading documents: {error_msg!s}\"\n logger.exception(msg)\n raise ValueError(msg) from e\n return data\n\n def fetch_content(self) -> DataFrame:\n \"\"\"Convert the documents to a DataFrame.\"\"\"\n return DataFrame(data=self.fetch_url_contents())\n\n def fetch_content_as_message(self) -> Message:\n \"\"\"Convert the documents to a Message.\"\"\"\n url_contents = self.fetch_url_contents()\n return Message(text=\"\\n\\n\".join([x[\"text\"] for x in url_contents]), data={\"data\": url_contents})\n" }, - "seed": { - "_input_type": "IntInput", + "continue_on_failure": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Seed", + "display_name": "Continue on Failure", "dynamic": false, - "info": "The seed controls the reproducibility of the job.", + "info": "If enabled, continues crawling even if some requests fail.", "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 - }, - "sender": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender", - "dynamic": false, - "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" - ], - "options_metadata": [], + "name": "continue_on_failure", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, "trace_as_metadata": true, - "type": "str", - "value": "Machine and User" + "type": "bool", + "value": true }, - "sender_name": { - "_input_type": "MessageTextInput", + "filter_text_html": { + "_input_type": "BoolInput", "advanced": true, - "display_name": "Sender Name", + "display_name": "Filter Text/HTML", "dynamic": false, - "info": "Filter by sender name.", - "input_types": [ - "Message" - ], + "info": "If enabled, filters out text/css content type from the results.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "name": "sender_name", + "name": "filter_text_html", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "" + "type": "bool", + "value": true }, - "sender_type": { + "format": { "_input_type": "DropdownInput", "advanced": true, "combobox": false, "dialog_inputs": {}, - "display_name": "Sender Type", + "display_name": "Output Format", "dynamic": false, - "info": "Filter by sender type.", - "name": "sender_type", + "info": "Output Format. Use 'Text' to extract the text from the HTML or 'HTML' for the raw HTML content.", + "name": "format", "options": [ - "Machine", - "User", - "Machine and User" + "Text", + "HTML" ], "options_metadata": [], "placeholder": "", @@ -3934,72 +3685,83 @@ "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "Machine and User" + "value": "Text" }, - "session_id": { - "_input_type": "MessageTextInput", + "headers": { + "_input_type": "TableInput", "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": "" - }, - "system_prompt": { - "_input_type": "MultilineInput", - "advanced": false, - "copy_field": false, - "display_name": "Agent Instructions", + "display_name": "Headers", "dynamic": false, - "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", + "info": "The headers to send with the request", "input_types": [ - "Message" + "DataFrame" ], - "list": false, + "is_list": true, "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_prompt", + "name": "headers", "placeholder": "", "required": false, "show": true, + "table_icon": "Table", + "table_schema": { + "columns": [ + { + "default": "None", + "description": "Header name", + "disable_edit": false, + "display_name": "Header", + "edit_mode": "popover", + "filterable": true, + "formatter": "text", + "hidden": false, + "name": "key", + "sortable": true, + "type": "str" + }, + { + "default": "None", + "description": "Header value", + "disable_edit": false, + "display_name": "Value", + "edit_mode": "popover", + "filterable": true, + "formatter": "text", + "hidden": false, + "name": "value", + "sortable": true, + "type": "str" + } + ] + }, "title_case": false, "tool_mode": false, - "trace_as_input": true, "trace_as_metadata": true, - "type": "str", - "value": "You are an Amazing Travel Concierge, a specialist in travel planning and logistics with decades of experience. Your goal is to create the most amazing travel itineraries with budget and packing suggestions for the city. Expand the city guide into a full 7-day travel itinerary with detailed per-day plans. Include weather forecasts, places to eat, packing suggestions, and a budget breakdown. Suggest actual places to visit, hotels to stay, and restaurants to go to. Your final output should be a complete expanded travel plan, formatted as markdown, encompassing a daily schedule, anticipated weather conditions, recommended clothing and items to pack, and a detailed budget." + "trigger_icon": "Table", + "trigger_text": "Open table", + "type": "table", + "value": [ + { + "key": "User-Agent", + "value": "langflow" + } + ] }, - "temperature": { - "_input_type": "SliderInput", - "advanced": true, - "display_name": "Temperature", + "max_depth": { + "_input_type": "SliderInput", + "advanced": false, + "display_name": "Depth", "dynamic": false, - "info": "", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", + "info": "Controls how many 'clicks' away from the initial page the crawler will go:\n- depth 1: only the initial page\n- depth 2: initial page + all pages linked directly from it\n- depth 3: initial page + direct links + links found on those direct link pages\nNote: This is about link traversal, not URL path depth.", + "max_label": " ", + "max_label_icon": "None", + "min_label": " ", + "min_label_icon": "None", + "name": "max_depth", "placeholder": "", "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, + "max": 5, + "min": 1, + "step": 1, "step_type": "float" }, "required": false, @@ -4010,39 +3772,32 @@ "title_case": false, "tool_mode": false, "type": "slider", - "value": 0.1 + "value": 1 }, - "template": { - "_input_type": "MultilineInput", + "prevent_outside": { + "_input_type": "BoolInput", "advanced": true, - "copy_field": false, - "display_name": "Template", + "display_name": "Prevent Outside", "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" - ], + "info": "If enabled, only crawls URLs within the same domain as the root URL. This helps prevent the crawler from going to external websites.", "list": false, "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "template", + "name": "prevent_outside", "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}" + "type": "bool", + "value": true }, "timeout": { "_input_type": "IntInput", "advanced": true, "display_name": "Timeout", "dynamic": false, - "info": "The timeout for requests to OpenAI completion API.", + "info": "Timeout for the request in seconds.", "list": false, "list_add_label": "Add More", "name": "timeout", @@ -4053,37 +3808,80 @@ "tool_mode": false, "trace_as_metadata": true, "type": "int", - "value": 700 + "value": 30 }, - "tools": { - "_input_type": "HandleInput", + "tools_metadata": { + "_input_type": "ToolsInput", "advanced": false, - "display_name": "Tools", + "display_name": "Actions", "dynamic": false, - "info": "These are the tools that the agent can use to help with tasks.", - "input_types": [ - "Tool" - ], - "list": true, + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", + "is_list": true, "list_add_label": "Add More", - "name": "tools", + "name": "tools_metadata", "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "title_case": false, + "tool_mode": false, "trace_as_metadata": true, - "type": "other", + "type": "tools", + "value": [ + { + "args": { + "urls": { + "default": "", + "description": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", + "items": { + "type": "string" + }, + "title": "Urls", + "type": "array" + } + }, + "description": "Fetch content from one or more web pages, following links recursively.", + "display_description": "Fetch content from one or more web pages, following links recursively.", + "display_name": "fetch_content", + "name": "fetch_content", + "readonly": false, + "status": true, + "tags": [ + "fetch_content" + ] + } + ] + }, + "urls": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "URLs", + "dynamic": false, + "info": "Enter one or more URLs to crawl recursively, by clicking the '+' button.", + "input_types": [], + "list": true, + "list_add_label": "Add URL", + "load_from_db": false, + "name": "urls", + "placeholder": "Enter a URL...", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", "value": "" }, - "verbose": { + "use_async": { "_input_type": "BoolInput", "advanced": true, - "display_name": "Verbose", + "display_name": "Use Async", "dynamic": false, - "info": "", + "info": "If enabled, uses asynchronous loading which can be significantly faster but might use more system resources.", "list": false, "list_add_label": "Add More", - "name": "verbose", + "name": "use_async", "placeholder": "", "required": false, "show": true, @@ -4094,37 +3892,183 @@ "value": true } }, - "tool_mode": false + "tool_mode": true }, - "selected_output": "response", "showNode": true, - "type": "Agent" + "type": "URLComponent" }, "dragging": false, - "id": "Agent-3tuWs", + "id": "URLComponent-8bgTS", "measured": { - "height": 624, + "height": 289, "width": 320 }, "position": { - "x": 3936.123532016018, - "y": 356.3534774464019 + "x": 2713.896825977834, + "y": 998.9242993650518 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "CalculatorComponent-JKeHR", + "node": { + "base_classes": [ + "Data" + ], + "beta": false, + "category": "helpers", + "conditional_paths": [], + "custom_fields": {}, + "description": "Perform basic arithmetic operations on a given expression.", + "display_name": "Calculator", + "documentation": "", + "edited": false, + "field_order": [ + "expression" + ], + "frozen": false, + "icon": "calculator", + "key": "CalculatorComponent", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "group_outputs": false, + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "options": null, + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 0.001, + "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 ast\nimport operator\nfrom collections.abc import Callable\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.inputs.inputs import MessageTextInput\nfrom langflow.io import Output\nfrom langflow.schema.data import Data\n\n\nclass CalculatorComponent(Component):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n\n # Cache operators dictionary as a class variable\n OPERATORS: dict[type[ast.operator], Callable] = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"result\", type_=Data, method=\"evaluate_expression\"),\n ]\n\n def _eval_expr(self, node: ast.AST) -> float:\n \"\"\"Evaluate an AST node recursively.\"\"\"\n if isinstance(node, ast.Constant):\n if isinstance(node.value, int | float):\n return float(node.value)\n error_msg = f\"Unsupported constant type: {type(node.value).__name__}\"\n raise TypeError(error_msg)\n if isinstance(node, ast.Num): # For backwards compatibility\n if isinstance(node.n, int | float):\n return float(node.n)\n error_msg = f\"Unsupported number type: {type(node.n).__name__}\"\n raise TypeError(error_msg)\n\n if isinstance(node, ast.BinOp):\n op_type = type(node.op)\n if op_type not in self.OPERATORS:\n error_msg = f\"Unsupported binary operator: {op_type.__name__}\"\n raise TypeError(error_msg)\n\n left = self._eval_expr(node.left)\n right = self._eval_expr(node.right)\n return self.OPERATORS[op_type](left, right)\n\n error_msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(error_msg)\n\n def evaluate_expression(self) -> Data:\n \"\"\"Evaluate the mathematical expression and return the result.\"\"\"\n try:\n tree = ast.parse(self.expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n formatted_result = f\"{float(result):.6f}\".rstrip(\"0\").rstrip(\".\")\n self.log(f\"Calculation result: {formatted_result}\")\n\n self.status = formatted_result\n return Data(data={\"result\": formatted_result})\n\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n except (SyntaxError, TypeError, KeyError, ValueError, AttributeError, OverflowError) as e:\n error_message = f\"Invalid expression: {e!s}\"\n self.status = error_message\n return Data(data={\"error\": error_message, \"input\": self.expression})\n\n def build(self):\n \"\"\"Return the main evaluation function.\"\"\"\n return self.evaluate_expression\n" + }, + "expression": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Expression", + "dynamic": false, + "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "expression", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "tools_metadata": { + "_input_type": "ToolsInput", + "advanced": false, + "display_name": "Actions", + "dynamic": false, + "info": "Modify tool names and descriptions to help agents understand when to use each tool.", + "is_list": true, + "list_add_label": "Add More", + "name": "tools_metadata", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "tools", + "value": [ + { + "args": { + "expression": { + "default": "", + "description": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", + "title": "Expression", + "type": "string" + } + }, + "description": "Perform basic arithmetic operations on a given expression.", + "display_description": "Perform basic arithmetic operations on a given expression.", + "display_name": "evaluate_expression", + "name": "evaluate_expression", + "readonly": false, + "status": true, + "tags": [ + "evaluate_expression" + ] + } + ] + } + }, + "tool_mode": true + }, + "showNode": true, + "type": "CalculatorComponent" + }, + "dragging": false, + "id": "CalculatorComponent-JKeHR", + "measured": { + "height": 217, + "width": 320 + }, + "position": { + "x": 3427.6329448251768, + "y": 1066.3993526174263 }, "selected": true, "type": "genericNode" } ], "viewport": { - "x": -750.9105189752929, - "y": 96.67627127061348, - "zoom": 0.44896152055995414 + "x": -1956.9334684808755, + "y": -270.19574649754327, + "zoom": 0.6669131453914963 } }, "description": "Create a travel planning chatbot that uses specialized agents to craft personalized trip itineraries.", "endpoint_name": null, - "id": "0148d85f-5339-4ba1-ae38-54b504c0223d", + "id": "95d851e7-4eb4-445b-a506-224570a44f37", "is_component": false, - "last_tested_version": "1.3.4", + "last_tested_version": "1.4.3", "name": "Travel Planning Agents", "tags": [ "agents", From e44c90ee55b3fcb9e604126d4ef1db00c6bc8a05 Mon Sep 17 00:00:00 2001 From: Yuqi Tang Date: Tue, 24 Jun 2025 08:48:53 -0700 Subject: [PATCH 4/6] update --- .../starter_projects/Simple Agent.json | 18 +- .../starter_projects/Social Media Agent.json | 192 +++++++++--------- 2 files changed, 102 insertions(+), 108 deletions(-) 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 4dce4bbfdd31..9226d14283c1 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 @@ -25,9 +25,9 @@ "id": "xy-edge__CalculatorComponent-Sp3Ca{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-Sp3Caœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-xD3uW{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, "source": "CalculatorComponent-Sp3Ca", - "sourceHandle": "{œdataTypeœ:œCalculatorComponentœ,œidœ:œCalculatorComponent-Sp3Caœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}", + "sourceHandle": "{œdataTypeœ: œCalculatorComponentœ, œidœ: œCalculatorComponent-Sp3Caœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", "target": "Agent-xD3uW", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-xD3uWœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -53,9 +53,9 @@ "id": "xy-edge__ChatInput-S6AhV{œdataTypeœ:œChatInputœ,œidœ:œChatInput-S6AhVœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-xD3uW{œfieldNameœ:œinput_valueœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, "source": "ChatInput-S6AhV", - "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-S6AhVœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-S6AhVœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", "target": "Agent-xD3uW", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-xD3uWœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -83,9 +83,9 @@ "id": "xy-edge__Agent-xD3uW{œdataTypeœ:œAgentœ,œidœ:œAgent-xD3uWœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-WciiI{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-WciiIœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, "source": "Agent-xD3uW", - "sourceHandle": "{œdataTypeœ:œAgentœ,œidœ:œAgent-xD3uWœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-xD3uWœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", "target": "ChatOutput-WciiI", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-WciiIœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-WciiIœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -111,9 +111,9 @@ "id": "xy-edge__URLComponent-6fRXe{œdataTypeœ:œURLComponentœ,œidœ:œURLComponent-6fRXeœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-xD3uW{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, "source": "URLComponent-6fRXe", - "sourceHandle": "{œdataTypeœ:œURLComponentœ,œidœ:œURLComponent-6fRXeœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}", + "sourceHandle": "{œdataTypeœ: œURLComponentœ, œidœ: œURLComponent-6fRXeœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", "target": "Agent-xD3uW", - "targetHandle": "{œfieldNameœ:œtoolsœ,œidœ:œAgent-xD3uWœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}" + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-xD3uWœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" } ], "nodes": [ @@ -1156,7 +1156,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" + "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" }, "handle_parsing_errors": { "_input_type": "BoolInput", 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 21745321363e..7297b43cdfe5 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 @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ApifyActors", - "id": "ApifyActors-CVPGK", + "id": "ApifyActors-I9HDQ", "name": "tool", "output_types": [ "Tool" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "tools", - "id": "Agent-yJhxC", + "id": "Agent-wmQQE", "inputTypes": [ "Tool" ], "type": "other" } }, - "id": "reactflow__edge-ApifyActors-CVPGK{œdataTypeœ:œApifyActorsœ,œidœ:œApifyActors-CVPGKœ,œnameœ:œtoolœ,œoutput_typesœ:[œToolœ]}-Agent-yJhxC{œfieldNameœ:œtoolsœ,œidœ:œAgent-yJhxCœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "xy-edge__ApifyActors-I9HDQ{œdataTypeœ:œApifyActorsœ,œidœ:œApifyActors-I9HDQœ,œnameœ:œtoolœ,œoutput_typesœ:[œToolœ]}-Agent-wmQQE{œfieldNameœ:œtoolsœ,œidœ:œAgent-wmQQEœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "ApifyActors-CVPGK", - "sourceHandle": "{œdataTypeœ: œApifyActorsœ, œidœ: œApifyActors-CVPGKœ, œnameœ: œtoolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-yJhxC", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-yJhxCœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "ApifyActors-I9HDQ", + "sourceHandle": "{œdataTypeœ: œApifyActorsœ, œidœ: œApifyActors-I9HDQœ, œnameœ: œtoolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-wmQQE", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-wmQQEœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "ApifyActors", - "id": "ApifyActors-tL94Y", + "id": "ApifyActors-maVx2", "name": "tool", "output_types": [ "Tool" @@ -43,83 +43,82 @@ }, "targetHandle": { "fieldName": "tools", - "id": "Agent-yJhxC", + "id": "Agent-wmQQE", "inputTypes": [ "Tool" ], "type": "other" } }, - "id": "reactflow__edge-ApifyActors-tL94Y{œdataTypeœ:œApifyActorsœ,œidœ:œApifyActors-tL94Yœ,œnameœ:œtoolœ,œoutput_typesœ:[œToolœ]}-Agent-yJhxC{œfieldNameœ:œtoolsœ,œidœ:œAgent-yJhxCœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "id": "xy-edge__ApifyActors-maVx2{œdataTypeœ:œApifyActorsœ,œidœ:œApifyActors-maVx2œ,œnameœ:œtoolœ,œoutput_typesœ:[œToolœ]}-Agent-wmQQE{œfieldNameœ:œtoolsœ,œidœ:œAgent-wmQQEœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", "selected": false, - "source": "ApifyActors-tL94Y", - "sourceHandle": "{œdataTypeœ: œApifyActorsœ, œidœ: œApifyActors-tL94Yœ, œnameœ: œtoolœ, œoutput_typesœ: [œToolœ]}", - "target": "Agent-yJhxC", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-yJhxCœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "source": "ApifyActors-maVx2", + "sourceHandle": "{œdataTypeœ: œApifyActorsœ, œidœ: œApifyActors-maVx2œ, œnameœ: œtoolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-wmQQE", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-wmQQEœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { - "dataType": "ChatInput", - "id": "ChatInput-E01OU", - "name": "message", + "dataType": "Agent", + "id": "Agent-wmQQE", + "name": "response", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_value", - "id": "Agent-yJhxC", + "id": "ChatOutput-5yecv", "inputTypes": [ + "Data", + "DataFrame", "Message" ], - "type": "str" + "type": "other" } }, - "id": "reactflow__edge-ChatInput-E01OU{œdataTypeœ:œChatInputœ,œidœ:œChatInput-E01OUœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-yJhxC{œfieldNameœ:œinput_valueœ,œidœ:œAgent-yJhxCœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "xy-edge__Agent-wmQQE{œdataTypeœ:œAgentœ,œidœ:œAgent-wmQQEœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-5yecv{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-5yecvœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "ChatInput-E01OU", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-E01OUœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Agent-yJhxC", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-yJhxCœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Agent-wmQQE", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-wmQQEœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-5yecv", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-5yecvœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" }, { "animated": false, - "className": "", "data": { "sourceHandle": { - "dataType": "Agent", - "id": "Agent-yJhxC", - "name": "response", + "dataType": "ChatInput", + "id": "ChatInput-amkkr", + "name": "message", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-xSo6b", + "id": "Agent-wmQQE", "inputTypes": [ - "Data", - "DataFrame", "Message" ], - "type": "other" + "type": "str" } }, - "id": "reactflow__edge-Agent-yJhxC{œdataTypeœ:œAgentœ,œidœ:œAgent-yJhxCœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-xSo6b{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-xSo6bœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "xy-edge__ChatInput-amkkr{œdataTypeœ:œChatInputœ,œidœ:œChatInput-amkkrœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-wmQQE{œfieldNameœ:œinput_valueœ,œidœ:œAgent-wmQQEœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Agent-yJhxC", - "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-yJhxCœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-xSo6b", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-xSo6bœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "ChatInput-amkkr", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-amkkrœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-wmQQE", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-wmQQEœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "ApifyActors-tL94Y", + "id": "ApifyActors-I9HDQ", "node": { "base_classes": [ "Data", @@ -308,9 +307,9 @@ "type": "ApifyActors" }, "dragging": false, - "id": "ApifyActors-tL94Y", + "id": "ApifyActors-I9HDQ", "measured": { - "height": 526, + "height": 523, "width": 320 }, "position": { @@ -322,7 +321,7 @@ }, { "data": { - "id": "ApifyActors-CVPGK", + "id": "ApifyActors-maVx2", "node": { "base_classes": [ "Data", @@ -511,9 +510,9 @@ "type": "ApifyActors" }, "dragging": false, - "id": "ApifyActors-CVPGK", + "id": "ApifyActors-maVx2", "measured": { - "height": 526, + "height": 523, "width": 320 }, "position": { @@ -525,7 +524,7 @@ }, { "data": { - "id": "note-hmQk5", + "id": "note-xssJa", "node": { "description": "### 💡 Add your Apify API key here ", "display_name": "", @@ -537,7 +536,7 @@ "type": "note" }, "dragging": false, - "id": "note-hmQk5", + "id": "note-xssJa", "measured": { "height": 324, "width": 324 @@ -551,7 +550,7 @@ }, { "data": { - "id": "note-Utxei", + "id": "note-TeGrW", "node": { "description": "### 💡 Add your Apify API key here ", "display_name": "", @@ -564,7 +563,7 @@ }, "dragging": false, "height": 324, - "id": "note-Utxei", + "id": "note-TeGrW", "measured": { "height": 324, "width": 324 @@ -580,7 +579,7 @@ }, { "data": { - "id": "note-NoZ4e", + "id": "note-6PTdf", "node": { "description": "# Social Media Agent\n\nExtract data with **Apify Actors** and analyze the data with an **Agent**.\n\n## Prerequisites\n\n* An [Apify API token](https://docs.apify.com/platform/integrations/api#api-token)\n* An [OpenAI API key](https://platform.openai.com/)\n\n## Quickstart\n\n1. Enter your **Apify** API token in the **Apify Token** fields of the **Apify Actors** components. \n2. Enter your **OpenAI** API token in the **OpenAI API Key** field of the **Agent** component.\n3. Open the **Playground** and chat with the agent. For example, task it with retrieving a profile bio and the latest video by using this prompt: \n ```\n Find the TikTok profile of the company OpenAI using Google search, then show me the profile bio and their latest video.\n ```", "display_name": "", @@ -593,10 +592,10 @@ }, "dragging": false, "height": 657, - "id": "note-NoZ4e", + "id": "note-6PTdf", "measured": { "height": 657, - "width": 525 + "width": 524 }, "position": { "x": -313.0627945618906, @@ -609,7 +608,7 @@ }, { "data": { - "id": "ChatInput-E01OU", + "id": "ChatInput-amkkr", "node": { "base_classes": [ "Message" @@ -635,7 +634,7 @@ "frozen": false, "icon": "MessagesSquare", "legacy": false, - "lf_version": "1.4.2", + "lf_version": "1.4.3", "metadata": {}, "minimized": true, "output_types": [], @@ -906,7 +905,7 @@ "type": "ChatInput" }, "dragging": false, - "id": "ChatInput-E01OU", + "id": "ChatInput-amkkr", "measured": { "height": 48, "width": 192 @@ -920,7 +919,7 @@ }, { "data": { - "id": "ChatOutput-xSo6b", + "id": "ChatOutput-5yecv", "node": { "base_classes": [ "Message" @@ -1214,7 +1213,7 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-xSo6b", + "id": "ChatOutput-5yecv", "measured": { "height": 48, "width": 192 @@ -1228,7 +1227,7 @@ }, { "data": { - "id": "note-kMYOt", + "id": "note-psyK5", "node": { "description": "### 💡 Add your OpenAI API key here ", "display_name": "", @@ -1240,7 +1239,7 @@ "type": "note" }, "dragging": false, - "id": "note-kMYOt", + "id": "note-psyK5", "measured": { "height": 324, "width": 324 @@ -1254,7 +1253,7 @@ }, { "data": { - "id": "Agent-yJhxC", + "id": "Agent-wmQQE", "node": { "base_classes": [ "Message" @@ -1286,7 +1285,10 @@ "verbose", "max_iterations", "agent_description", + "mode", + "message", "memory", + "sender_type", "sender", "sender_name", "n_messages", @@ -1299,7 +1301,6 @@ "icon": "bot", "key": "Agent", "legacy": false, - "lf_version": "1.4.2", "metadata": {}, "minimized": false, "output_types": [], @@ -1308,8 +1309,11 @@ "allows_loop": false, "cache": true, "display_name": "Response", + "group_outputs": false, "method": "message_response", "name": "response", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1383,30 +1387,18 @@ "Custom" ], "options_metadata": [ - { - "icon": "Amazon" - }, { "icon": "Anthropic" }, - { - "icon": "Azure" - }, { "icon": "GoogleGenerativeAI" }, { "icon": "Groq" }, - { - "icon": "NVIDIA" - }, { "icon": "OpenAI" }, - { - "icon": "SambaNova" - }, { "icon": "brain" } @@ -1416,6 +1408,7 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", @@ -1432,6 +1425,7 @@ "name": "api_key", "password": true, "placeholder": "", + "real_time_refresh": true, "required": true, "show": true, "title_case": false, @@ -1599,7 +1593,7 @@ "_input_type": "MessageTextInput", "advanced": true, "display_name": "Message", - "dynamic": false, + "dynamic": true, "info": "The chat message to be stored.", "input_types": [ "Message" @@ -1610,7 +1604,7 @@ "name": "message", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": true, "trace_as_input": true, @@ -1637,7 +1631,7 @@ "tool_mode": false, "trace_as_metadata": true, "type": "tab", - "value": "Store" + "value": "Retrieve" }, "model_kwargs": { "_input_type": "DictInput", @@ -1685,10 +1679,11 @@ "required": false, "show": true, "title_case": false, + "toggle": false, "tool_mode": false, "trace_as_metadata": true, "type": "str", - "value": "gpt-4.1" + "value": "gpt-4o" }, "n_messages": { "_input_type": "IntInput", @@ -1701,7 +1696,7 @@ "name": "n_messages", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_metadata": true, @@ -1745,6 +1740,7 @@ "required": true, "show": true, "title_case": false, + "toggle": false, "tool_mode": true, "trace_as_metadata": true, "type": "str", @@ -1769,28 +1765,27 @@ "value": 1 }, "sender": { - "_input_type": "DropdownInput", + "_input_type": "MessageTextInput", "advanced": true, - "combobox": false, - "dialog_inputs": {}, "display_name": "Sender", "dynamic": false, "info": "The sender of the message. Might be Machine or User. If empty, the current sender parameter will be used.", - "name": "sender", - "options": [ - "Machine", - "User", - "Machine and User" + "input_types": [ + "Message" ], - "options_metadata": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender", "placeholder": "", "required": false, "show": true, "title_case": false, "tool_mode": false, + "trace_as_input": true, "trace_as_metadata": true, "type": "str", - "value": "Machine and User" + "value": "" }, "sender_name": { "_input_type": "MessageTextInput", @@ -1807,7 +1802,7 @@ "name": "sender_name", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -1933,7 +1928,7 @@ "name": "template", "placeholder": "", "required": false, - "show": true, + "show": false, "title_case": false, "tool_mode": false, "trace_as_input": true, @@ -2000,35 +1995,34 @@ }, "tool_mode": false }, - "selected_output": "response", "showNode": true, "type": "Agent" }, "dragging": false, - "id": "Agent-yJhxC", + "id": "Agent-wmQQE", "measured": { - "height": 594, + "height": 591, "width": 320 }, "position": { - "x": 1023.5315500182937, - "y": 280.6548808097231 + "x": 1040.7328325813182, + "y": 309.2404087611358 }, - "selected": false, + "selected": true, "type": "genericNode" } ], "viewport": { - "x": 251.14965955941773, - "y": 42.193990847111536, - "zoom": 0.5946080556136385 + "x": -11.680210843338386, + "y": 7.902598790141155, + "zoom": 0.5704013010733382 } }, "description": "Utilize Apify Actors as agent tools to search and analyze social media profiles.", "endpoint_name": null, - "id": "08525bf9-dd32-4bbe-920c-169cffc78890", + "id": "fc6be8ff-289b-43c5-bb7a-35274b5930af", "is_component": false, - "last_tested_version": "1.4.2", + "last_tested_version": "1.4.3", "name": "Social Media Agent", "tags": [ "agent", From b264fd510f2630405ec9764e6a21e4764acfe51d Mon Sep 17 00:00:00 2001 From: Yuqi Tang Date: Tue, 24 Jun 2025 09:09:44 -0700 Subject: [PATCH 5/6] change outpu --- .../Research Translation Loop.json | 186 +++++++++--------- 1 file changed, 91 insertions(+), 95 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json index 65ebcd6aa60f..85a54cdf5fbc 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-6hroM", + "id": "ChatInput-FQZVj", "name": "message", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "search_query", - "id": "ArXivComponent-XiOmX", + "id": "ArXivComponent-hsHDF", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ChatInput-6hroM{œdataTypeœ:œChatInputœ,œidœ:œChatInput-6hroMœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ArXivComponent-XiOmX{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-XiOmXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-FQZVj{œdataTypeœ:œChatInputœ,œidœ:œChatInput-FQZVjœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-ArXivComponent-hsHDF{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-hsHDFœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-6hroM", - "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-6hroMœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", - "target": "ArXivComponent-XiOmX", - "targetHandle": "{œfieldNameœ:œsearch_queryœ,œidœ:œArXivComponent-XiOmXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + "source": "ChatInput-FQZVj", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-FQZVjœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "ArXivComponent-hsHDF", + "targetHandle": "{œfieldNameœ: œsearch_queryœ, œidœ: œArXivComponent-hsHDFœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "ArXivComponent", - "id": "ArXivComponent-XiOmX", + "id": "ArXivComponent-hsHDF", "name": "dataframe", "output_types": [ "DataFrame" @@ -43,19 +43,19 @@ }, "targetHandle": { "fieldName": "data", - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "inputTypes": [ "DataFrame" ], "type": "other" } }, - "id": "reactflow__edge-ArXivComponent-XiOmX{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-XiOmXœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-LoopComponent-T1P62{œfieldNameœ:œdataœ,œidœ:œLoopComponent-T1P62œ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-ArXivComponent-hsHDF{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-hsHDFœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-LoopComponent-ukukr{œfieldNameœ:œdataœ,œidœ:œLoopComponent-ukukrœ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "ArXivComponent-XiOmX", - "sourceHandle": "{œdataTypeœ:œArXivComponentœ,œidœ:œArXivComponent-XiOmXœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}", - "target": "LoopComponent-T1P62", - "targetHandle": "{œfieldNameœ:œdataœ,œidœ:œLoopComponent-T1P62œ,œinputTypesœ:[œDataFrameœ],œtypeœ:œotherœ}" + "source": "ArXivComponent-hsHDF", + "sourceHandle": "{œdataTypeœ: œArXivComponentœ, œidœ: œArXivComponent-hsHDFœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "LoopComponent-ukukr", + "targetHandle": "{œfieldNameœ: œdataœ, œidœ: œLoopComponent-ukukrœ, œinputTypesœ: [œDataFrameœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -63,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "name": "item", "output_types": [ "Data" @@ -71,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "ParserComponent-9koHg", + "id": "ParserComponent-sJ2Cp", "inputTypes": [ "DataFrame", "Data" @@ -79,12 +79,12 @@ "type": "other" } }, - "id": "reactflow__edge-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}-ParserComponent-9koHg{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-9koHgœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LoopComponent-ukukr{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-ukukrœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}-ParserComponent-sJ2Cp{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-sJ2Cpœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "LoopComponent-T1P62", - "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", - "target": "ParserComponent-9koHg", - "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-9koHgœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}" + "source": "LoopComponent-ukukr", + "sourceHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-ukukrœ, œnameœ: œitemœ, œoutput_typesœ: [œDataœ]}", + "target": "ParserComponent-sJ2Cp", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œParserComponent-sJ2Cpœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -92,7 +92,7 @@ "data": { "sourceHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "name": "done", "output_types": [ "DataFrame" @@ -100,7 +100,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "TypeConverterComponent-FyeIR", + "id": "TypeConverterComponent-5rZSf", "inputTypes": [ "Message", "Data", @@ -109,12 +109,12 @@ "type": "other" } }, - "id": "reactflow__edge-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-FyeIR{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-FyeIRœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LoopComponent-ukukr{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-ukukrœ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}-TypeConverterComponent-5rZSf{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-5rZSfœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "LoopComponent-T1P62", - "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œdoneœ,œoutput_typesœ:[œDataFrameœ]}", - "target": "TypeConverterComponent-FyeIR", - "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-FyeIRœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" + "source": "LoopComponent-ukukr", + "sourceHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-ukukrœ, œnameœ: œdoneœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "TypeConverterComponent-5rZSf", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-5rZSfœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -122,7 +122,7 @@ "data": { "sourceHandle": { "dataType": "TypeConverterComponent", - "id": "TypeConverterComponent-FyeIR", + "id": "TypeConverterComponent-5rZSf", "name": "message_output", "output_types": [ "Message" @@ -130,7 +130,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-Owlsl", + "id": "ChatOutput-ou9sf", "inputTypes": [ "Data", "DataFrame", @@ -139,12 +139,12 @@ "type": "other" } }, - "id": "reactflow__edge-TypeConverterComponent-FyeIR{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-FyeIRœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Owlsl{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Owlslœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-TypeConverterComponent-5rZSf{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-5rZSfœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-ou9sf{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-ou9sfœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "TypeConverterComponent-FyeIR", - "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-FyeIRœ,œnameœ:œmessage_outputœ,œoutput_typesœ:[œMessageœ]}", - "target": "ChatOutput-Owlsl", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Owlslœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}" + "source": "TypeConverterComponent-5rZSf", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-5rZSfœ, œnameœ: œmessage_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-ou9sf", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-ou9sfœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -152,7 +152,7 @@ "data": { "sourceHandle": { "dataType": "ParserComponent", - "id": "ParserComponent-9koHg", + "id": "ParserComponent-sJ2Cp", "name": "parsed_text", "output_types": [ "Message" @@ -160,19 +160,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "LanguageModelComponent-JpEyw", + "id": "LanguageModelComponent-2FSUA", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-ParserComponent-9koHg{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-9koHgœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-JpEyw{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-JpEywœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ParserComponent-sJ2Cp{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-sJ2Cpœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-2FSUA{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-2FSUAœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ParserComponent-9koHg", - "sourceHandle": "{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-9koHgœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}", - "target": "LanguageModelComponent-JpEyw", - "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-JpEywœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + "source": "ParserComponent-sJ2Cp", + "sourceHandle": "{œdataTypeœ: œParserComponentœ, œidœ: œParserComponent-sJ2Cpœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-2FSUA", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-2FSUAœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -180,7 +180,7 @@ "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-JpEyw", + "id": "LanguageModelComponent-2FSUA", "name": "text_output", "output_types": [ "Message" @@ -188,7 +188,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "TypeConverterComponent-shkBL", + "id": "TypeConverterComponent-WY9tm", "inputTypes": [ "Message", "Data", @@ -197,46 +197,41 @@ "type": "other" } }, - "id": "xy-edge__LanguageModelComponent-JpEyw{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-JpEywœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-TypeConverterComponent-shkBL{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-shkBLœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LanguageModelComponent-2FSUA{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-2FSUAœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-TypeConverterComponent-WY9tm{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-WY9tmœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "LanguageModelComponent-JpEyw", - "sourceHandle": "{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-JpEywœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}", - "target": "TypeConverterComponent-shkBL", - "targetHandle": "{œfieldNameœ:œinput_dataœ,œidœ:œTypeConverterComponent-shkBLœ,œinputTypesœ:[œMessageœ,œDataœ,œDataFrameœ],œtypeœ:œotherœ}" + "source": "LanguageModelComponent-2FSUA", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-2FSUAœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "TypeConverterComponent-WY9tm", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œTypeConverterComponent-WY9tmœ, œinputTypesœ: [œMessageœ, œDataœ, œDataFrameœ], œtypeœ: œotherœ}" }, { - "animated": false, - "className": "", "data": { "sourceHandle": { "dataType": "TypeConverterComponent", - "id": "TypeConverterComponent-shkBL", + "id": "TypeConverterComponent-WY9tm", "name": "data_output", - "output_types": [ - "Data" - ] + "output_types": [] }, "targetHandle": { "dataType": "LoopComponent", - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "name": "item", "output_types": [ "Data" ] } }, - "id": "xy-edge__TypeConverterComponent-shkBL{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-shkBLœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}-LoopComponent-T1P62{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", - "selected": false, - "source": "TypeConverterComponent-shkBL", - "sourceHandle": "{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-shkBLœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}", - "target": "LoopComponent-T1P62", - "targetHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-T1P62œ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}" + "id": "xy-edge__TypeConverterComponent-WY9tm{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-WY9tmœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}-LoopComponent-ukukr{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-ukukrœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", + "source": "TypeConverterComponent-WY9tm", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-WY9tmœ, œnameœ: œdata_outputœ, œoutput_typesœ: []}", + "target": "LoopComponent-ukukr", + "targetHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-ukukrœ, œnameœ: œitemœ, œoutput_typesœ: [œDataœ]}" } ], "nodes": [ { "data": { - "id": "ArXivComponent-XiOmX", + "id": "ArXivComponent-hsHDF", "node": { "base_classes": [ "DataFrame" @@ -373,9 +368,9 @@ "type": "ArXivComponent" }, "dragging": false, - "id": "ArXivComponent-XiOmX", + "id": "ArXivComponent-hsHDF", "measured": { - "height": 369, + "height": 367, "width": 320 }, "position": { @@ -387,7 +382,7 @@ }, { "data": { - "id": "ChatOutput-Owlsl", + "id": "ChatOutput-ou9sf", "node": { "base_classes": [ "Message" @@ -682,7 +677,7 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-Owlsl", + "id": "ChatOutput-ou9sf", "measured": { "height": 48, "width": 192 @@ -696,7 +691,7 @@ }, { "data": { - "id": "ChatInput-6hroM", + "id": "ChatInput-FQZVj", "node": { "base_classes": [ "Message" @@ -994,9 +989,9 @@ "type": "ChatInput" }, "dragging": false, - "id": "ChatInput-6hroM", + "id": "ChatInput-FQZVj", "measured": { - "height": 204, + "height": 203, "width": 320 }, "position": { @@ -1008,7 +1003,7 @@ }, { "data": { - "id": "note-Unlpq", + "id": "note-FkBwM", "node": { "description": "# **Langflow Loop Component Template - ArXiv search result Translator** \nThis template translates research paper summaries on ArXiv into Portuguese and summarizes them. \n Using **Langflow’s looping mechanism**, the template iterates through multiple research papers, translates them with the **OpenAI** model component, and outputs an aggregated version of all translated papers. \n\n## Quickstart \n 1. Add your OpenAI API key to the **Language Model** component. \n2. In the **Playground**, enter a query related to a research topic (for example, “Quantum Computing Advancements”). \n\n The flow fetches a list of research papers from ArXiv matching the query. Each paper in the retrieved list is processed one-by-one using the Langflow **Loop component**. \n\n The abstract of each paper is translated into Portuguese by the **OpenAI** model component. \n\n Once all papers are translated, the system aggregates them into a **single structured output**.", "display_name": "", @@ -1019,7 +1014,7 @@ }, "dragging": false, "height": 647, - "id": "note-Unlpq", + "id": "note-FkBwM", "measured": { "height": 647, "width": 576 @@ -1035,7 +1030,7 @@ }, { "data": { - "id": "ParserComponent-9koHg", + "id": "ParserComponent-sJ2Cp", "node": { "base_classes": [ "Message" @@ -1194,9 +1189,9 @@ "type": "ParserComponent" }, "dragging": false, - "id": "ParserComponent-9koHg", + "id": "ParserComponent-sJ2Cp", "measured": { - "height": 329, + "height": 327, "width": 320 }, "position": { @@ -1208,7 +1203,7 @@ }, { "data": { - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "node": { "base_classes": [ "Data", @@ -1309,9 +1304,9 @@ "type": "LoopComponent" }, "dragging": false, - "id": "LoopComponent-T1P62", + "id": "LoopComponent-ukukr", "measured": { - "height": 242, + "height": 241, "width": 320 }, "position": { @@ -1323,7 +1318,7 @@ }, { "data": { - "id": "TypeConverterComponent-FyeIR", + "id": "TypeConverterComponent-5rZSf", "node": { "base_classes": [ "Message" @@ -1437,9 +1432,9 @@ "type": "TypeConverterComponent" }, "dragging": false, - "id": "TypeConverterComponent-FyeIR", + "id": "TypeConverterComponent-5rZSf", "measured": { - "height": 262, + "height": 261, "width": 320 }, "position": { @@ -1451,7 +1446,7 @@ }, { "data": { - "id": "LanguageModelComponent-JpEyw", + "id": "LanguageModelComponent-2FSUA", "node": { "base_classes": [ "LanguageModel", @@ -1540,7 +1535,7 @@ "show": true, "title_case": false, "type": "str", - "value": "OPENAI_API_KEY" + "value": "" }, "code": { "advanced": true, @@ -1729,9 +1724,9 @@ "type": "LanguageModelComponent" }, "dragging": false, - "id": "LanguageModelComponent-JpEyw", + "id": "LanguageModelComponent-2FSUA", "measured": { - "height": 534, + "height": 531, "width": 320 }, "position": { @@ -1743,7 +1738,7 @@ }, { "data": { - "id": "TypeConverterComponent-shkBL", + "id": "TypeConverterComponent-WY9tm", "node": { "base_classes": [ "Message" @@ -1773,7 +1768,6 @@ "cache": true, "display_name": "Data Output", "group_outputs": false, - "hidden": null, "method": "convert_to_data", "name": "data_output", "options": null, @@ -1781,7 +1775,9 @@ "selected": "Data", "tool_mode": true, "types": [ - "Data" + "Message", + "Data", + "DataFrame" ], "value": "__UNDEFINED__" } @@ -1806,7 +1802,7 @@ "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 = [\n Output(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n )\n ]\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(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n ).to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"Data Output\",\n name=\"data_output\",\n method=\"convert_to_data\",\n ).to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\",\n name=\"dataframe_output\",\n 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 input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_message(input_value)\n self.status = result\n return result\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_data(input_value)\n self.status = result\n return result\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_dataframe(input_value)\n self.status = result\n return result\n" + "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 = [\n Output(\n display_name=\"Data Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n )\n ]\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(\n display_name=\"Data Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n ).to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"Data Output\",\n name=\"data_output\",\n method=\"convert_to_data\",\n ).to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\",\n name=\"dataframe_output\",\n 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 input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_message(input_value)\n self.status = result\n return result\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_data(input_value)\n self.status = result\n return result\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_dataframe(input_value)\n self.status = result\n return result\n" }, "input_data": { "_input_type": "HandleInput", @@ -1859,9 +1855,9 @@ "type": "TypeConverterComponent" }, "dragging": false, - "id": "TypeConverterComponent-shkBL", + "id": "TypeConverterComponent-WY9tm", "measured": { - "height": 262, + "height": 261, "width": 320 }, "position": { @@ -1873,14 +1869,14 @@ } ], "viewport": { - "x": 446.66109209865874, - "y": 293.1638977007619, - "zoom": 0.42727671233908193 + "x": -214.9740744044314, + "y": 116.1497506096415, + "zoom": 0.5583331791544022 } }, "description": "This template iterates over search results using LoopComponent and translates each result into Portuguese automatically. 🚀", "endpoint_name": null, - "id": "df6cb896-ee70-4429-bf5b-dbb2ac7669b9", + "id": "dbb63177-cc76-420a-a74e-8dc336a70d65", "is_component": false, "last_tested_version": "1.4.3", "name": "Research Translation Loop", From 256c5d59a6461df54b639e8424630800476a8e96 Mon Sep 17 00:00:00 2001 From: Yuqi Tang Date: Tue, 24 Jun 2025 09:22:30 -0700 Subject: [PATCH 6/6] update translation loop --- src/backend/base/langflow/initial_setup/setup.py | 1 + .../starter_projects/Research Translation Loop.json | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/setup.py b/src/backend/base/langflow/initial_setup/setup.py index 5ba8675f6207..9503ba5b5091 100644 --- a/src/backend/base/langflow/initial_setup/setup.py +++ b/src/backend/base/langflow/initial_setup/setup.py @@ -73,6 +73,7 @@ def update_projects_components_with_latest_component_versions(project_data, all_ is_tool_or_agent = node_data.get("tool_mode", False) or node_data.get("key") in { "Agent", "LanguageModelComponent", + "TypeConverterComponent", } has_tool_outputs = any(output.get("types") == ["Tool"] for output in node_data.get("outputs", [])) if "outputs" in latest_node and not has_tool_outputs and not is_tool_or_agent: diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json index 85a54cdf5fbc..053f593ce80c 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Research Translation Loop.json @@ -210,7 +210,9 @@ "dataType": "TypeConverterComponent", "id": "TypeConverterComponent-WY9tm", "name": "data_output", - "output_types": [] + "output_types": [ + "Data" + ] }, "targetHandle": { "dataType": "LoopComponent", @@ -223,7 +225,7 @@ }, "id": "xy-edge__TypeConverterComponent-WY9tm{œdataTypeœ:œTypeConverterComponentœ,œidœ:œTypeConverterComponent-WY9tmœ,œnameœ:œdata_outputœ,œoutput_typesœ:[œDataœ]}-LoopComponent-ukukr{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-ukukrœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}", "source": "TypeConverterComponent-WY9tm", - "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-WY9tmœ, œnameœ: œdata_outputœ, œoutput_typesœ: []}", + "sourceHandle": "{œdataTypeœ: œTypeConverterComponentœ, œidœ: œTypeConverterComponent-WY9tmœ, œnameœ: œdata_outputœ, œoutput_typesœ: [œDataœ]}", "target": "LoopComponent-ukukr", "targetHandle": "{œdataTypeœ: œLoopComponentœ, œidœ: œLoopComponent-ukukrœ, œnameœ: œitemœ, œoutput_typesœ: [œDataœ]}" } @@ -1741,7 +1743,9 @@ "id": "TypeConverterComponent-WY9tm", "node": { "base_classes": [ - "Message" + "Message", + "Data", + "DataFrame" ], "beta": false, "category": "processing", @@ -1802,7 +1806,7 @@ "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 = [\n Output(\n display_name=\"Data Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n )\n ]\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(\n display_name=\"Data Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n ).to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"Data Output\",\n name=\"data_output\",\n method=\"convert_to_data\",\n ).to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\",\n name=\"dataframe_output\",\n 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 input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_message(input_value)\n self.status = result\n return result\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_data(input_value)\n self.status = result\n return result\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_dataframe(input_value)\n self.status = result\n return result\n" + "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 = [\n Output(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n )\n ]\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(\n display_name=\"Message Output\",\n name=\"message_output\",\n method=\"convert_to_message\",\n ).to_dict()\n )\n elif field_value == \"Data\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"Data Output\",\n name=\"data_output\",\n method=\"convert_to_data\",\n ).to_dict()\n )\n elif field_value == \"DataFrame\":\n frontend_node[\"outputs\"].append(\n Output(\n display_name=\"DataFrame Output\",\n name=\"dataframe_output\",\n 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 input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_message(input_value)\n self.status = result\n return result\n\n def convert_to_data(self) -> Data:\n \"\"\"Convert input to Data type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_data(input_value)\n self.status = result\n return result\n\n def convert_to_dataframe(self) -> DataFrame:\n \"\"\"Convert input to DataFrame type.\"\"\"\n input_value = self.input_data[0] if isinstance(self.input_data, list) else self.input_data\n\n # Handle string input by converting to Message first\n if isinstance(input_value, str):\n input_value = Message(text=input_value)\n\n result = convert_to_dataframe(input_value)\n self.status = result\n return result\n" }, "input_data": { "_input_type": "HandleInput",