diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json b/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json
new file mode 100644
index 000000000..f451eaeb0
--- /dev/null
+++ b/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json
@@ -0,0 +1,3005 @@
+{
+ "data": {
+ "edges": [
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "File",
+ "id": "File-Ktatn",
+ "name": "data",
+ "output_types": [
+ "Data"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "data",
+ "id": "ParseData-gouVC",
+ "inputTypes": [
+ "Data"
+ ],
+ "type": "other"
+ }
+ },
+ "id": "reactflow__edge-File-Ktatn{œdataTypeœ:œFileœ,œidœ:œFile-Ktatnœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-gouVC{œfieldNameœ:œdataœ,œidœ:œParseData-gouVCœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
+ "selected": false,
+ "source": "File-Ktatn",
+ "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-Ktatnœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
+ "target": "ParseData-gouVC",
+ "targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-gouVCœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "ParseData",
+ "id": "ParseData-gouVC",
+ "name": "text",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "text",
+ "id": "Prompt-epiSD",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-ParseData-gouVC{œdataTypeœ:œParseDataœ,œidœ:œParseData-gouVCœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-epiSD{œfieldNameœ:œtextœ,œidœ:œPrompt-epiSDœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "ParseData-gouVC",
+ "sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-gouVCœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "Prompt-epiSD",
+ "targetHandle": "{œfieldNameœ: œtextœ, œidœ: œPrompt-epiSDœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "Prompt",
+ "id": "Prompt-epiSD",
+ "name": "prompt",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "system_message",
+ "id": "OpenAIModel-ppS3O",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-Prompt-epiSD{œdataTypeœ:œPromptœ,œidœ:œPrompt-epiSDœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-ppS3O{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-ppS3Oœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "Prompt-epiSD",
+ "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-epiSDœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "OpenAIModel-ppS3O",
+ "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-ppS3Oœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "OpenAIModel",
+ "id": "OpenAIModel-ppS3O",
+ "name": "text_output",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "summary",
+ "id": "Prompt-l9XAo",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-OpenAIModel-ppS3O{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-ppS3Oœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-l9XAo{œfieldNameœ:œsummaryœ,œidœ:œPrompt-l9XAoœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "OpenAIModel-ppS3O",
+ "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-ppS3Oœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "Prompt-l9XAo",
+ "targetHandle": "{œfieldNameœ: œsummaryœ, œidœ: œPrompt-l9XAoœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "Prompt",
+ "id": "Prompt-l9XAo",
+ "name": "prompt",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "system_message",
+ "id": "OpenAIModel-DxfrQ",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-Prompt-l9XAo{œdataTypeœ:œPromptœ,œidœ:œPrompt-l9XAoœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-DxfrQ{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-DxfrQœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "Prompt-l9XAo",
+ "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-l9XAoœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "OpenAIModel-DxfrQ",
+ "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-DxfrQœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "Prompt",
+ "id": "Prompt-LKleN",
+ "name": "prompt",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "system_message",
+ "id": "OpenAIModel-W1vhv",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-Prompt-LKleN{œdataTypeœ:œPromptœ,œidœ:œPrompt-LKleNœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-W1vhv{œfieldNameœ:œsystem_messageœ,œidœ:œOpenAIModel-W1vhvœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "Prompt-LKleN",
+ "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-LKleNœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "OpenAIModel-W1vhv",
+ "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œOpenAIModel-W1vhvœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "ParseData",
+ "id": "ParseData-gouVC",
+ "name": "text",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "input_value",
+ "id": "OpenAIModel-W1vhv",
+ "inputTypes": [
+ "Message"
+ ],
+ "type": "str"
+ }
+ },
+ "id": "reactflow__edge-ParseData-gouVC{œdataTypeœ:œParseDataœ,œidœ:œParseData-gouVCœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-W1vhv{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-W1vhvœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
+ "selected": false,
+ "source": "ParseData-gouVC",
+ "sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-gouVCœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "OpenAIModel-W1vhv",
+ "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-W1vhvœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "OpenAIModel",
+ "id": "OpenAIModel-W1vhv",
+ "name": "text_output",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "input_value",
+ "id": "ChatOutput-V5ZFA",
+ "inputTypes": [
+ "Data",
+ "DataFrame",
+ "Message"
+ ],
+ "type": "other"
+ }
+ },
+ "id": "reactflow__edge-OpenAIModel-W1vhv{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-W1vhvœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-V5ZFA{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-V5ZFAœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}",
+ "selected": false,
+ "source": "OpenAIModel-W1vhv",
+ "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-W1vhvœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "ChatOutput-V5ZFA",
+ "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-V5ZFAœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}"
+ },
+ {
+ "animated": false,
+ "className": "",
+ "data": {
+ "sourceHandle": {
+ "dataType": "OpenAIModel",
+ "id": "OpenAIModel-DxfrQ",
+ "name": "text_output",
+ "output_types": [
+ "Message"
+ ]
+ },
+ "targetHandle": {
+ "fieldName": "input_value",
+ "id": "ChatOutput-8y94b",
+ "inputTypes": [
+ "Data",
+ "DataFrame",
+ "Message"
+ ],
+ "type": "other"
+ }
+ },
+ "id": "reactflow__edge-OpenAIModel-DxfrQ{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-DxfrQœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-8y94b{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-8y94bœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}",
+ "selected": false,
+ "source": "OpenAIModel-DxfrQ",
+ "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-DxfrQœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
+ "target": "ChatOutput-8y94b",
+ "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-8y94bœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}"
+ }
+ ],
+ "nodes": [
+ {
+ "data": {
+ "id": "File-Ktatn",
+ "node": {
+ "base_classes": [
+ "Data"
+ ],
+ "beta": false,
+ "category": "data",
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Load a file to be used in your project.",
+ "display_name": "File",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "path",
+ "file_path",
+ "silent_errors",
+ "delete_server_file_after_processing",
+ "ignore_unsupported_extensions",
+ "ignore_unspecified_files",
+ "use_multithreading",
+ "concurrency_multithreading"
+ ],
+ "frozen": false,
+ "icon": "file-text",
+ "key": "File",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Data",
+ "method": "load_files",
+ "name": "data",
+ "required_inputs": [],
+ "selected": "Data",
+ "tool_mode": true,
+ "types": [
+ "Data"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "score": 0.0004124940109183525,
+ "template": {
+ "_type": "Component",
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langflow.base.data 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 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 = \"Load a file to be used in your project.\"\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,
+ "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": true,
+ "display_name": "Server File Path",
+ "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.",
+ "input_types": [
+ "Data",
+ "Message"
+ ],
+ "list": true,
+ "list_add_label": "Add More",
+ "name": "file_path",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "trace_as_metadata": true,
+ "type": "other",
+ "value": ""
+ },
+ "ignore_unspecified_files": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Ignore Unspecified Files",
+ "dynamic": false,
+ "info": "If true, Data with no 'file_path' property will be ignored.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "ignore_unspecified_files",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": 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,
+ "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,
+ "display_name": "Path",
+ "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": "43bb2a52-8dbf-4edf-a200-c54ee0e7fa1f\\2025-02-21_09-35-24_messages.json",
+ "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": false,
+ "list_add_label": "Add More",
+ "name": "path",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "trace_as_metadata": true,
+ "type": "file",
+ "value": ""
+ },
+ "silent_errors": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Silent Errors",
+ "dynamic": false,
+ "info": "If true, errors will not raise an exception.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "silent_errors",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "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": "File"
+ },
+ "dragging": false,
+ "id": "File-Ktatn",
+ "measured": {
+ "height": 228,
+ "width": 320
+ },
+ "position": {
+ "x": -2423.1133566137846,
+ "y": 736.6681755311793
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "ParseData-gouVC",
+ "node": {
+ "base_classes": [
+ "Data",
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Convert Data objects into Messages using any {field_name} from input data.",
+ "display_name": "Data to Message",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "data",
+ "template",
+ "sep"
+ ],
+ "frozen": false,
+ "icon": "message-square",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {
+ "legacy_name": "Parse Data"
+ },
+ "minimized": false,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "parse_data",
+ "name": "text",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ },
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Data List",
+ "method": "parse_data_as_list",
+ "name": "data_list",
+ "selected": "Data",
+ "tool_mode": true,
+ "types": [
+ "Data"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text, data_to_text_list\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Data to Message\"\n description = \"Convert Data objects into Messages using any {field_name} from input data.\"\n icon = \"message-square\"\n name = \"ParseData\"\n metadata = {\n \"legacy_name\": \"Parse Data\",\n }\n\n inputs = [\n DataInput(\n name=\"data\",\n display_name=\"Data\",\n info=\"The data to convert to text.\",\n is_list=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}, {data} or any other key in the Data.\",\n value=\"{text}\",\n required=True,\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(\n display_name=\"Message\",\n name=\"text\",\n info=\"Data as a single Message, with each input Data separated by Separator\",\n method=\"parse_data\",\n ),\n Output(\n display_name=\"Data List\",\n name=\"data_list\",\n info=\"Data as a list of new Data, each having `text` formatted by Template\",\n method=\"parse_data_as_list\",\n ),\n ]\n\n def _clean_args(self) -> tuple[list[Data], str, str]:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n sep = self.sep\n return data, template, sep\n\n def parse_data(self) -> Message:\n data, template, sep = self._clean_args()\n result_string = data_to_text(template, data, sep)\n self.status = result_string\n return Message(text=result_string)\n\n def parse_data_as_list(self) -> list[Data]:\n data, template, _ = self._clean_args()\n text_list, data_list = data_to_text_list(template, data)\n for item, text in zip(data_list, text_list, strict=True):\n item.set_text(text)\n self.status = data_list\n return data_list\n"
+ },
+ "data": {
+ "_input_type": "DataInput",
+ "advanced": false,
+ "display_name": "Data",
+ "dynamic": false,
+ "info": "The data to convert to text.",
+ "input_types": [
+ "Data"
+ ],
+ "list": true,
+ "list_add_label": "Add More",
+ "name": "data",
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "other",
+ "value": ""
+ },
+ "sep": {
+ "_input_type": "StrInput",
+ "advanced": true,
+ "display_name": "Separator",
+ "dynamic": false,
+ "info": "",
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "sep",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "\n"
+ },
+ "template": {
+ "_input_type": "MultilineInput",
+ "advanced": false,
+ "display_name": "Template",
+ "dynamic": false,
+ "info": "The template to use for formatting the data. It can contain the keys {text}, {data} or any other key in the Data.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "multiline": true,
+ "name": "template",
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "list of messages details: {text}"
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "ParseData"
+ },
+ "dragging": false,
+ "id": "ParseData-gouVC",
+ "measured": {
+ "height": 342,
+ "width": 320
+ },
+ "position": {
+ "x": -2048.092643605176,
+ "y": 650.2904629519279
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "Prompt-l9XAo",
+ "node": {
+ "base_classes": [
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {
+ "template": [
+ "summary"
+ ]
+ },
+ "description": "Create a prompt template with dynamic variables.",
+ "display_name": "Prompt",
+ "documentation": "",
+ "edited": false,
+ "error": null,
+ "field_order": [
+ "template",
+ "tool_placeholder"
+ ],
+ "frozen": false,
+ "full_path": null,
+ "icon": "prompts",
+ "is_composition": null,
+ "is_input": null,
+ "is_output": null,
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "name": "",
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Prompt Message",
+ "method": "build_prompt",
+ "name": "prompt",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\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 Message\", 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"
+ },
+ "summary": {
+ "advanced": false,
+ "display_name": "summary",
+ "dynamic": false,
+ "field_type": "str",
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "summary",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "type": "str",
+ "value": ""
+ },
+ "template": {
+ "_input_type": "PromptInput",
+ "advanced": false,
+ "display_name": "Template",
+ "dynamic": false,
+ "info": "",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "template",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "prompt",
+ "value": "Based on the following summary of discussions in the Langflow community, generate a well-structured and actionable technical recommendation for the development team. The recommendation should be clear, precise, and directly applicable to improve Langflow.\n\nGuidelines for the Action Item:\nBe Specific: Clearly define the issue, feature request, or improvement.\nProvide Context: Briefly justify why this action is necessary based on user discussions.\nSuggest a Next Step: Outline what the technical team should do to address the issue or enhance the platform.\nPrioritize if Relevant: If multiple issues are discussed, focus on the most urgent or impactful one.\n\nSummary:\n{summary}\n\nProvide a concise, structured, and technically sound action item that the Langflow team can implement.\n\nreturn:\n- {summary}\n- [action item]\n- [sentiment]\n- [start_date]\n- [end_date]\n\nyou need to return the data in json format\n\nReturn Format:\nEnsure that both \"summary\" and \"action_item\" are single string values, not lists and without nested objects or additional keys.\n\n"
+ },
+ "tool_placeholder": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Tool Placeholder",
+ "dynamic": false,
+ "info": "A placeholder input for tool mode.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "tool_placeholder",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": true,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "Prompt"
+ },
+ "dragging": false,
+ "id": "Prompt-l9XAo",
+ "measured": {
+ "height": 339,
+ "width": 320
+ },
+ "position": {
+ "x": -973.805557188769,
+ "y": 1081.112870159395
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "Prompt-epiSD",
+ "node": {
+ "base_classes": [
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {
+ "template": [
+ "text"
+ ]
+ },
+ "description": "Create a prompt template with dynamic variables.",
+ "display_name": "Prompt",
+ "documentation": "",
+ "edited": false,
+ "error": null,
+ "field_order": [
+ "template",
+ "tool_placeholder"
+ ],
+ "frozen": false,
+ "full_path": null,
+ "icon": "prompts",
+ "is_composition": null,
+ "is_input": null,
+ "is_output": null,
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "name": "",
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Prompt Message",
+ "method": "build_prompt",
+ "name": "prompt",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\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 Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
+ },
+ "template": {
+ "_input_type": "PromptInput",
+ "advanced": false,
+ "display_name": "Template",
+ "dynamic": false,
+ "info": "",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "template",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "prompt",
+ "value": "You are analyzing messages from an online community or discussion platform. Your task is to generate a well-structured, concise, and informative summary of the key discussions that took place in a specific channel or group during a given time period. This summary will provide actionable insights for community managers, stakeholders, or decision-makers. \n\nYou will receive a list of messages containing **message_id, created_at, and message_content**. \n\n### **Task 1: Summarization** \nFrom the **message_content**, generate a cohesive, flowing summary in paragraph form, avoiding bullet points or fragmented structures. The summary should be written in clear, professional English, ensuring a natural reading experience. \n\nFocus on the following key discussion areas (if applicable): \n- **Feature Requests & Enhancements**: Suggestions for new features, improvements, or emerging use cases. \n- **Issues & Problems**: Reports of challenges, technical difficulties, or troubleshooting discussions. \n- **Community Feedback**: Reactions to updates, usability concerns, and overall user sentiment. \n- **General Topics**: Broader discussions relevant to the platform, industry trends, or key interests of the community. \n\nMessages may be written in different languages, depending on the community. However, the summary must always be in English and should be structured as a natural, flowing narrative rather than a list of points. \n\n### **Task 2: Sentiment Analysis** \nAnalyze the overall **sentiment** of the summary based on user feedback, discussions, and general tone. The sentiment should reflect how the community perceives recent updates, issues, or the overall experience. \n\n#### **Classification Criteria:** \n- **Positive**: The discussion contains mostly favorable feedback, enthusiasm about new topics, successful problem resolutions, or constructive engagement. \n- **Neutral**: The discussion is balanced, with a mix of praise, constructive criticism, and open-ended conversations without strong emotions. \n- **Negative**: The discussion is dominated by frustrations, unresolved issues, strong criticisms, or concerns about the platform's direction. \n\n### **Input:** \nHere is the list of messages: \n{text} \n\n### **Return Format:** \n- **summary**: A cohesive, well-structured summary written in continuous prose, without bullet points. \n- **sentiment**: One of the sentiment categories (**Positive, Neutral, or Negative**). \n- **start_date**: The earliest message timestamp in the format **'yyyy-mm-dd'**. \n- **end_date**: The most recent message timestamp in the format **'yyyy-mm-dd'**. \n"
+ },
+ "text": {
+ "advanced": false,
+ "display_name": "text",
+ "dynamic": false,
+ "field_type": "str",
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "text",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "type": "str",
+ "value": ""
+ },
+ "tool_placeholder": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Tool Placeholder",
+ "dynamic": false,
+ "info": "A placeholder input for tool mode.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "tool_placeholder",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": true,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "Prompt"
+ },
+ "dragging": false,
+ "id": "Prompt-epiSD",
+ "measured": {
+ "height": 339,
+ "width": 320
+ },
+ "position": {
+ "x": -1662.7141678649673,
+ "y": 1010.953782441708
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "OpenAIModel-ppS3O",
+ "node": {
+ "base_classes": [
+ "LanguageModel",
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Generates text using OpenAI LLMs.",
+ "display_name": "OpenAI",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "input_value",
+ "system_message",
+ "stream",
+ "max_tokens",
+ "model_kwargs",
+ "json_mode",
+ "model_name",
+ "openai_api_base",
+ "api_key",
+ "temperature",
+ "seed",
+ "max_retries",
+ "timeout"
+ ],
+ "frozen": false,
+ "icon": "OpenAI",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "text_response",
+ "name": "text_output",
+ "required_inputs": [],
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ },
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Language Model",
+ "method": "build_model",
+ "name": "model_output",
+ "required_inputs": [
+ "api_key"
+ ],
+ "selected": "LanguageModel",
+ "tool_mode": true,
+ "types": [
+ "LanguageModel"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "api_key": {
+ "_input_type": "SecretStrInput",
+ "advanced": false,
+ "display_name": "OpenAI API Key",
+ "dynamic": false,
+ "info": "The OpenAI API Key to use for the OpenAI model.",
+ "input_types": [
+ "Message"
+ ],
+ "load_from_db": false,
+ "name": "api_key",
+ "password": true,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "str",
+ "value": ""
+ },
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\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,\n value=OPENAI_MODEL_NAMES[1],\n combobox=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\", display_name=\"Temperature\", value=0.1, range_spec=RangeSpec(min=0, max=1, step=0.01)\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 openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = self.json_mode\n seed = self.seed\n max_retries = self.max_retries\n timeout = self.timeout\n\n api_key = SecretStr(openai_api_key).get_secret_value() if openai_api_key else None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature if temperature is not None else 0.1,\n seed=seed,\n max_retries=max_retries,\n request_timeout=timeout,\n )\n if 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"
+ },
+ "input_value": {
+ "_input_type": "MessageInput",
+ "advanced": false,
+ "display_name": "Input",
+ "dynamic": false,
+ "info": "",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "input_value",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "json_mode": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "JSON Mode",
+ "dynamic": false,
+ "info": "If True, it will output JSON regardless of passing a schema.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "json_mode",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "max_retries": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Retries",
+ "dynamic": false,
+ "info": "The maximum number of retries to make when generating.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_retries",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 5
+ },
+ "max_tokens": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Tokens",
+ "dynamic": false,
+ "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_tokens",
+ "placeholder": "",
+ "range_spec": {
+ "max": 128000,
+ "min": 0,
+ "step": 0.1,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": ""
+ },
+ "model_kwargs": {
+ "_input_type": "DictInput",
+ "advanced": true,
+ "display_name": "Model Kwargs",
+ "dynamic": false,
+ "info": "Additional keyword arguments to pass to the model.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "model_kwargs",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "dict",
+ "value": {}
+ },
+ "model_name": {
+ "_input_type": "DropdownInput",
+ "advanced": false,
+ "combobox": true,
+ "dialog_inputs": {},
+ "display_name": "Model Name",
+ "dynamic": false,
+ "info": "",
+ "name": "model_name",
+ "options": [
+ "gpt-4o-mini",
+ "gpt-4o",
+ "gpt-4.5-preview",
+ "gpt-4-turbo",
+ "gpt-4-turbo-preview",
+ "gpt-4",
+ "gpt-3.5-turbo"
+ ],
+ "options_metadata": [],
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "gpt-4o-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": ""
+ },
+ "seed": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Seed",
+ "dynamic": false,
+ "info": "The seed controls the reproducibility of the job.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "seed",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 1
+ },
+ "stream": {
+ "_input_type": "BoolInput",
+ "advanced": false,
+ "display_name": "Stream",
+ "dynamic": false,
+ "info": "Stream the response from the model. Streaming works only in Chat.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "stream",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "system_message": {
+ "_input_type": "MultilineInput",
+ "advanced": false,
+ "display_name": "System Message",
+ "dynamic": false,
+ "info": "System message to pass to the model.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "multiline": true,
+ "name": "system_message",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "temperature": {
+ "_input_type": "SliderInput",
+ "advanced": false,
+ "display_name": "Temperature",
+ "dynamic": false,
+ "info": "",
+ "max_label": "",
+ "max_label_icon": "",
+ "min_label": "",
+ "min_label_icon": "",
+ "name": "temperature",
+ "placeholder": "",
+ "range_spec": {
+ "max": 1,
+ "min": 0,
+ "step": 0.01,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "slider_buttons": false,
+ "slider_buttons_options": [],
+ "slider_input": false,
+ "title_case": false,
+ "tool_mode": false,
+ "type": "slider",
+ "value": 0.1
+ },
+ "timeout": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Timeout",
+ "dynamic": false,
+ "info": "The timeout for requests to OpenAI completion API.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "timeout",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 700
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "OpenAIModel"
+ },
+ "dragging": false,
+ "id": "OpenAIModel-ppS3O",
+ "measured": {
+ "height": 656,
+ "width": 320
+ },
+ "position": {
+ "x": -1310.5197628463882,
+ "y": 973.1747844967928
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "OpenAIModel-DxfrQ",
+ "node": {
+ "base_classes": [
+ "LanguageModel",
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Generates text using OpenAI LLMs.",
+ "display_name": "OpenAI",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "input_value",
+ "system_message",
+ "stream",
+ "max_tokens",
+ "model_kwargs",
+ "json_mode",
+ "model_name",
+ "openai_api_base",
+ "api_key",
+ "temperature",
+ "seed",
+ "max_retries",
+ "timeout"
+ ],
+ "frozen": false,
+ "icon": "OpenAI",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "text_response",
+ "name": "text_output",
+ "required_inputs": [],
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ },
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Language Model",
+ "method": "build_model",
+ "name": "model_output",
+ "required_inputs": [
+ "api_key"
+ ],
+ "selected": "LanguageModel",
+ "tool_mode": true,
+ "types": [
+ "LanguageModel"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "api_key": {
+ "_input_type": "SecretStrInput",
+ "advanced": false,
+ "display_name": "OpenAI API Key",
+ "dynamic": false,
+ "info": "The OpenAI API Key to use for the OpenAI model.",
+ "input_types": [
+ "Message"
+ ],
+ "load_from_db": false,
+ "name": "api_key",
+ "password": true,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "str",
+ "value": ""
+ },
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\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,\n value=OPENAI_MODEL_NAMES[1],\n combobox=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\", display_name=\"Temperature\", value=0.1, range_spec=RangeSpec(min=0, max=1, step=0.01)\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 openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = self.json_mode\n seed = self.seed\n max_retries = self.max_retries\n timeout = self.timeout\n\n api_key = SecretStr(openai_api_key).get_secret_value() if openai_api_key else None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature if temperature is not None else 0.1,\n seed=seed,\n max_retries=max_retries,\n request_timeout=timeout,\n )\n if 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"
+ },
+ "input_value": {
+ "_input_type": "MessageInput",
+ "advanced": false,
+ "display_name": "Input",
+ "dynamic": false,
+ "info": "",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "input_value",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "json_mode": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "JSON Mode",
+ "dynamic": false,
+ "info": "If True, it will output JSON regardless of passing a schema.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "json_mode",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "max_retries": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Retries",
+ "dynamic": false,
+ "info": "The maximum number of retries to make when generating.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_retries",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 5
+ },
+ "max_tokens": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Tokens",
+ "dynamic": false,
+ "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_tokens",
+ "placeholder": "",
+ "range_spec": {
+ "max": 128000,
+ "min": 0,
+ "step": 0.1,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": ""
+ },
+ "model_kwargs": {
+ "_input_type": "DictInput",
+ "advanced": true,
+ "display_name": "Model Kwargs",
+ "dynamic": false,
+ "info": "Additional keyword arguments to pass to the model.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "model_kwargs",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "dict",
+ "value": {}
+ },
+ "model_name": {
+ "_input_type": "DropdownInput",
+ "advanced": false,
+ "combobox": true,
+ "dialog_inputs": {},
+ "display_name": "Model Name",
+ "dynamic": false,
+ "info": "",
+ "name": "model_name",
+ "options": [
+ "gpt-4o-mini",
+ "gpt-4o",
+ "gpt-4.5-preview",
+ "gpt-4-turbo",
+ "gpt-4-turbo-preview",
+ "gpt-4",
+ "gpt-3.5-turbo"
+ ],
+ "options_metadata": [],
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "gpt-4o-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": ""
+ },
+ "seed": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Seed",
+ "dynamic": false,
+ "info": "The seed controls the reproducibility of the job.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "seed",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 1
+ },
+ "stream": {
+ "_input_type": "BoolInput",
+ "advanced": false,
+ "display_name": "Stream",
+ "dynamic": false,
+ "info": "Stream the response from the model. Streaming works only in Chat.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "stream",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "system_message": {
+ "_input_type": "MultilineInput",
+ "advanced": false,
+ "display_name": "System Message",
+ "dynamic": false,
+ "info": "System message to pass to the model.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "multiline": true,
+ "name": "system_message",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "temperature": {
+ "_input_type": "SliderInput",
+ "advanced": false,
+ "display_name": "Temperature",
+ "dynamic": false,
+ "info": "",
+ "max_label": "",
+ "max_label_icon": "",
+ "min_label": "",
+ "min_label_icon": "",
+ "name": "temperature",
+ "placeholder": "",
+ "range_spec": {
+ "max": 1,
+ "min": 0,
+ "step": 0.01,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "slider_buttons": false,
+ "slider_buttons_options": [],
+ "slider_input": false,
+ "title_case": false,
+ "tool_mode": false,
+ "type": "slider",
+ "value": 0.1
+ },
+ "timeout": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Timeout",
+ "dynamic": false,
+ "info": "The timeout for requests to OpenAI completion API.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "timeout",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 700
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "OpenAIModel"
+ },
+ "dragging": false,
+ "id": "OpenAIModel-DxfrQ",
+ "measured": {
+ "height": 656,
+ "width": 320
+ },
+ "position": {
+ "x": -618.8663500710704,
+ "y": 974.1327278776446
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "Prompt-LKleN",
+ "node": {
+ "base_classes": [
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {
+ "template": []
+ },
+ "description": "Create a prompt template with dynamic variables.",
+ "display_name": "Prompt",
+ "documentation": "",
+ "edited": false,
+ "error": null,
+ "field_order": [
+ "template",
+ "tool_placeholder"
+ ],
+ "frozen": false,
+ "full_path": null,
+ "icon": "prompts",
+ "is_composition": null,
+ "is_input": null,
+ "is_output": null,
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "name": "",
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Prompt Message",
+ "method": "build_prompt",
+ "name": "prompt",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\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 Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
+ },
+ "template": {
+ "_input_type": "PromptInput",
+ "advanced": false,
+ "display_name": "Template",
+ "dynamic": false,
+ "info": "",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "template",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "prompt",
+ "value": "You are an NLP expert assisting the technical team in analyzing user messages. The analysis will be used by the team to make decisions more effectively.\n\n**Task 1:**\n#### **1. Message Classification **\nClassify the message into a **single category** that best describes its purpose. Choose the category from the predefined list below. This classification will help the technical team identify specific themes and make decisions faster.\n\n- **Release Notes**: A message announcing updates, new features, integrations, important information about new versions for users.\n- **Feature Request**: A suggestion or idea for a new feature or enhancement.\n- **Bug Report**: A message reporting a clear technical failure, malfunction, or unexpected behavior in Langflow. The user explicitly describes an issue where: A feature does not work as expected; The system crashes, freezes, or behaves unpredictably.\n- **User Question**: Any question or uncertainty regarding features, functionality, implementation, or general use. This includes both technical questions and general doubts, regardless of complexity or specificity.\n- **Complaint**: A message expressing dissatisfaction or frustration with a feature or issue.\n- **Positive Feedback**: Messages expressing gratitude alongside useful feedback, such as confirming that an issue has been resolved or a feature works as intended.\n\n\n##### **Important Instructions for Classification:**\n- Ensure each message is classified into **only one category** based on its primary intent or purpose. If multiple intents are detected, select the most relevant category that reflects the user's main goal.\n- Do not create new categories or use freeform text for classification. Always use one of the predefined categories exactly as they appear in the list above.\n\n**task 2**\nAnalyze the sentiment of the message and classify it into one of the following categories:\n- **Positive**: The message conveys positivity, satisfaction, gratitude, or encouragement.\n- **Neutral**: The message is factual, descriptive, or lacks any emotional tone.\n- **Negative**: The message conveys frustration, dissatisfaction, or criticism.\n\n\nreturn:\n- [message_id]\n- [message_category] \n- [message_sentiment]\n\nYou need to output the results in JSON format.\n"
+ },
+ "tool_placeholder": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Tool Placeholder",
+ "dynamic": false,
+ "info": "A placeholder input for tool mode.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "tool_placeholder",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": true,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "Prompt"
+ },
+ "dragging": false,
+ "id": "Prompt-LKleN",
+ "measured": {
+ "height": 256,
+ "width": 320
+ },
+ "position": {
+ "x": -1668.597994833308,
+ "y": 651.6625805464743
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "OpenAIModel-W1vhv",
+ "node": {
+ "base_classes": [
+ "LanguageModel",
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Generates text using OpenAI LLMs.",
+ "display_name": "OpenAI",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "input_value",
+ "system_message",
+ "stream",
+ "max_tokens",
+ "model_kwargs",
+ "json_mode",
+ "model_name",
+ "openai_api_base",
+ "api_key",
+ "temperature",
+ "seed",
+ "max_retries",
+ "timeout"
+ ],
+ "frozen": false,
+ "icon": "OpenAI",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": false,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "text_response",
+ "name": "text_output",
+ "required_inputs": [],
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ },
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Language Model",
+ "method": "build_model",
+ "name": "model_output",
+ "required_inputs": [
+ "api_key"
+ ],
+ "selected": "LanguageModel",
+ "tool_mode": true,
+ "types": [
+ "LanguageModel"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "api_key": {
+ "_input_type": "SecretStrInput",
+ "advanced": false,
+ "display_name": "OpenAI API Key",
+ "dynamic": false,
+ "info": "The OpenAI API Key to use for the OpenAI model.",
+ "input_types": [
+ "Message"
+ ],
+ "load_from_db": false,
+ "name": "api_key",
+ "password": true,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "str",
+ "value": ""
+ },
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\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,\n value=OPENAI_MODEL_NAMES[1],\n combobox=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\", display_name=\"Temperature\", value=0.1, range_spec=RangeSpec(min=0, max=1, step=0.01)\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 openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = self.json_mode\n seed = self.seed\n max_retries = self.max_retries\n timeout = self.timeout\n\n api_key = SecretStr(openai_api_key).get_secret_value() if openai_api_key else None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature if temperature is not None else 0.1,\n seed=seed,\n max_retries=max_retries,\n request_timeout=timeout,\n )\n if 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"
+ },
+ "input_value": {
+ "_input_type": "MessageInput",
+ "advanced": false,
+ "display_name": "Input",
+ "dynamic": false,
+ "info": "",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "input_value",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "json_mode": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "JSON Mode",
+ "dynamic": false,
+ "info": "If True, it will output JSON regardless of passing a schema.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "json_mode",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "max_retries": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Retries",
+ "dynamic": false,
+ "info": "The maximum number of retries to make when generating.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_retries",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 5
+ },
+ "max_tokens": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Max Tokens",
+ "dynamic": false,
+ "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "max_tokens",
+ "placeholder": "",
+ "range_spec": {
+ "max": 128000,
+ "min": 0,
+ "step": 0.1,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": ""
+ },
+ "model_kwargs": {
+ "_input_type": "DictInput",
+ "advanced": true,
+ "display_name": "Model Kwargs",
+ "dynamic": false,
+ "info": "Additional keyword arguments to pass to the model.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "model_kwargs",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "type": "dict",
+ "value": {}
+ },
+ "model_name": {
+ "_input_type": "DropdownInput",
+ "advanced": false,
+ "combobox": true,
+ "dialog_inputs": {},
+ "display_name": "Model Name",
+ "dynamic": false,
+ "info": "",
+ "name": "model_name",
+ "options": [
+ "gpt-4o-mini",
+ "gpt-4o",
+ "gpt-4.5-preview",
+ "gpt-4-turbo",
+ "gpt-4-turbo-preview",
+ "gpt-4",
+ "gpt-3.5-turbo"
+ ],
+ "options_metadata": [],
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "gpt-4o-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": ""
+ },
+ "seed": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Seed",
+ "dynamic": false,
+ "info": "The seed controls the reproducibility of the job.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "seed",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 1
+ },
+ "stream": {
+ "_input_type": "BoolInput",
+ "advanced": false,
+ "display_name": "Stream",
+ "dynamic": false,
+ "info": "Stream the response from the model. Streaming works only in Chat.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "stream",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": false
+ },
+ "system_message": {
+ "_input_type": "MultilineInput",
+ "advanced": false,
+ "display_name": "System Message",
+ "dynamic": false,
+ "info": "System message to pass to the model.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "multiline": true,
+ "name": "system_message",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "temperature": {
+ "_input_type": "SliderInput",
+ "advanced": false,
+ "display_name": "Temperature",
+ "dynamic": false,
+ "info": "",
+ "max_label": "",
+ "max_label_icon": "",
+ "min_label": "",
+ "min_label_icon": "",
+ "name": "temperature",
+ "placeholder": "",
+ "range_spec": {
+ "max": 1,
+ "min": 0,
+ "step": 0.01,
+ "step_type": "float"
+ },
+ "required": false,
+ "show": true,
+ "slider_buttons": false,
+ "slider_buttons_options": [],
+ "slider_input": false,
+ "title_case": false,
+ "tool_mode": false,
+ "type": "slider",
+ "value": 0.1
+ },
+ "timeout": {
+ "_input_type": "IntInput",
+ "advanced": true,
+ "display_name": "Timeout",
+ "dynamic": false,
+ "info": "The timeout for requests to OpenAI completion API.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "timeout",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "int",
+ "value": 700
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "OpenAIModel"
+ },
+ "dragging": false,
+ "id": "OpenAIModel-W1vhv",
+ "measured": {
+ "height": 656,
+ "width": 320
+ },
+ "position": {
+ "x": -1297.2469651919287,
+ "y": 254.12590003703684
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "note-oqDGB",
+ "node": {
+ "description": "# Sentiment Analysis Flow \n\nThis flow processes text data, analyzes sentiment, and provides structured insights. \n\n## Prerequisite\n\n* [OpenAI API key](https://platform.openai.com/docs/)\n\n## Quickstart\n\n1. Add your [OpenAI API key](https://platform.openai.com/docs/) to the OpenAI model components.\n2. In the **File Component**, load text data in `.txt`, `.csv`, or `.json` formats.\n3. Open the **Playground** to see the analysis and recommendation the flow constructs.\n\n## How It Works \n\n1. The **Data to Message** component converts raw data into structured messages. \n\n2. The **NLP expert** prompt provides sentiment analysis, while the **Analyzing messages** prompt summarizes messages from a discussion board.\n\n3. The final **Prompt Component** creates a structured recommendation prompt to ensure that the AI model receives well-formatted input. \n\n4. The **OpenAI Model Component** processes the text and classifies the sentiment as **Positive, Neutral, or Negative**. \n\n",
+ "display_name": "",
+ "documentation": "",
+ "template": {}
+ },
+ "type": "note"
+ },
+ "dragging": false,
+ "height": 574,
+ "id": "note-oqDGB",
+ "measured": {
+ "height": 574,
+ "width": 413
+ },
+ "position": {
+ "x": -2891.3528304066394,
+ "y": 384.370168260918
+ },
+ "resizing": false,
+ "selected": false,
+ "type": "noteNode",
+ "width": 412
+ },
+ {
+ "data": {
+ "id": "ChatOutput-V5ZFA",
+ "node": {
+ "base_classes": [
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Display a chat message in the Playground.",
+ "display_name": "Chat Output",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "input_value",
+ "should_store_message",
+ "sender",
+ "sender_name",
+ "session_id",
+ "data_template",
+ "background_color",
+ "chat_icon",
+ "text_color",
+ "clean_data"
+ ],
+ "frozen": false,
+ "icon": "MessagesSquare",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": true,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "message_response",
+ "name": "message",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "background_color": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Background Color",
+ "dynamic": false,
+ "info": "The background color of the icon.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "background_color",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "chat_icon": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Icon",
+ "dynamic": false,
+ "info": "The icon of the message.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "chat_icon",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "clean_data": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Basic Clean Data",
+ "dynamic": false,
+ "info": "Whether to clean the data",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "clean_data",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": true
+ },
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from collections.abc import Generator\nfrom typing import Any\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Text\",\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=\"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 # 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 _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 _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 if data.get_text() is None:\n msg = \"Empty Data object\"\n raise ValueError(msg)\n return data.get_text()\n if isinstance(data, DataFrame):\n if 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\n # Replace pipe characters to avoid markdown table issues\n processed_data = data.replace(r\"\\|\", r\"\\\\|\", regex=True)\n\n processed_data = processed_data.map(\n lambda x: str(x).replace(\"\\n\", \"
\") if isinstance(x, str) else x\n )\n\n return processed_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) -> 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([self._safe_convert(item) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return self._safe_convert(self.input_value)\n"
+ },
+ "data_template": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Data Template",
+ "dynamic": false,
+ "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "data_template",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "{text}"
+ },
+ "input_value": {
+ "_input_type": "HandleInput",
+ "advanced": false,
+ "display_name": "Text",
+ "dynamic": false,
+ "info": "Message to be passed as output.",
+ "input_types": [
+ "Data",
+ "DataFrame",
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "input_value",
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "trace_as_metadata": true,
+ "type": "other",
+ "value": ""
+ },
+ "sender": {
+ "_input_type": "DropdownInput",
+ "advanced": true,
+ "combobox": false,
+ "dialog_inputs": {},
+ "display_name": "Sender Type",
+ "dynamic": false,
+ "info": "Type of sender.",
+ "name": "sender",
+ "options": [
+ "Machine",
+ "User"
+ ],
+ "options_metadata": [],
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "Machine"
+ },
+ "sender_name": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Sender Name",
+ "dynamic": false,
+ "info": "Name of the sender.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "sender_name",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "AI"
+ },
+ "session_id": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Session ID",
+ "dynamic": false,
+ "info": "The session ID of the chat. If empty, the current session ID parameter will be used.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "session_id",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "should_store_message": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Store Messages",
+ "dynamic": false,
+ "info": "Store the message in the history.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "should_store_message",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": true
+ },
+ "text_color": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Text Color",
+ "dynamic": false,
+ "info": "The text color of the name",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "text_color",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "ChatOutput"
+ },
+ "dragging": false,
+ "id": "ChatOutput-V5ZFA",
+ "measured": {
+ "height": 192,
+ "width": 320
+ },
+ "position": {
+ "x": -957.5187396354046,
+ "y": 574.7669946378641
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "ChatOutput-8y94b",
+ "node": {
+ "base_classes": [
+ "Message"
+ ],
+ "beta": false,
+ "conditional_paths": [],
+ "custom_fields": {},
+ "description": "Display a chat message in the Playground.",
+ "display_name": "Chat Output",
+ "documentation": "",
+ "edited": false,
+ "field_order": [
+ "input_value",
+ "should_store_message",
+ "sender",
+ "sender_name",
+ "session_id",
+ "data_template",
+ "background_color",
+ "chat_icon",
+ "text_color",
+ "clean_data"
+ ],
+ "frozen": false,
+ "icon": "MessagesSquare",
+ "legacy": false,
+ "lf_version": "1.2.0",
+ "metadata": {},
+ "minimized": true,
+ "output_types": [],
+ "outputs": [
+ {
+ "allows_loop": false,
+ "cache": true,
+ "display_name": "Message",
+ "method": "message_response",
+ "name": "message",
+ "selected": "Message",
+ "tool_mode": true,
+ "types": [
+ "Message"
+ ],
+ "value": "__UNDEFINED__"
+ }
+ ],
+ "pinned": false,
+ "template": {
+ "_type": "Component",
+ "background_color": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Background Color",
+ "dynamic": false,
+ "info": "The background color of the icon.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "background_color",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "chat_icon": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Icon",
+ "dynamic": false,
+ "info": "The icon of the message.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "chat_icon",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "clean_data": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Basic Clean Data",
+ "dynamic": false,
+ "info": "Whether to clean the data",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "clean_data",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": true
+ },
+ "code": {
+ "advanced": true,
+ "dynamic": true,
+ "fileTypes": [],
+ "file_path": "",
+ "info": "",
+ "list": false,
+ "load_from_db": false,
+ "multiline": true,
+ "name": "code",
+ "password": false,
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "type": "code",
+ "value": "from collections.abc import Generator\nfrom typing import Any\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Text\",\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=\"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 # 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 _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 _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 if data.get_text() is None:\n msg = \"Empty Data object\"\n raise ValueError(msg)\n return data.get_text()\n if isinstance(data, DataFrame):\n if 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\n # Replace pipe characters to avoid markdown table issues\n processed_data = data.replace(r\"\\|\", r\"\\\\|\", regex=True)\n\n processed_data = processed_data.map(\n lambda x: str(x).replace(\"\\n\", \"
\") if isinstance(x, str) else x\n )\n\n return processed_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) -> 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([self._safe_convert(item) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return self._safe_convert(self.input_value)\n"
+ },
+ "data_template": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Data Template",
+ "dynamic": false,
+ "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "data_template",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "{text}"
+ },
+ "input_value": {
+ "_input_type": "HandleInput",
+ "advanced": false,
+ "display_name": "Text",
+ "dynamic": false,
+ "info": "Message to be passed as output.",
+ "input_types": [
+ "Data",
+ "DataFrame",
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "input_value",
+ "placeholder": "",
+ "required": true,
+ "show": true,
+ "title_case": false,
+ "trace_as_metadata": true,
+ "type": "other",
+ "value": ""
+ },
+ "sender": {
+ "_input_type": "DropdownInput",
+ "advanced": true,
+ "combobox": false,
+ "dialog_inputs": {},
+ "display_name": "Sender Type",
+ "dynamic": false,
+ "info": "Type of sender.",
+ "name": "sender",
+ "options": [
+ "Machine",
+ "User"
+ ],
+ "options_metadata": [],
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "Machine"
+ },
+ "sender_name": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Sender Name",
+ "dynamic": false,
+ "info": "Name of the sender.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "sender_name",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": "AI"
+ },
+ "session_id": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Session ID",
+ "dynamic": false,
+ "info": "The session ID of the chat. If empty, the current session ID parameter will be used.",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "session_id",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ },
+ "should_store_message": {
+ "_input_type": "BoolInput",
+ "advanced": true,
+ "display_name": "Store Messages",
+ "dynamic": false,
+ "info": "Store the message in the history.",
+ "list": false,
+ "list_add_label": "Add More",
+ "name": "should_store_message",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_metadata": true,
+ "type": "bool",
+ "value": true
+ },
+ "text_color": {
+ "_input_type": "MessageTextInput",
+ "advanced": true,
+ "display_name": "Text Color",
+ "dynamic": false,
+ "info": "The text color of the name",
+ "input_types": [
+ "Message"
+ ],
+ "list": false,
+ "list_add_label": "Add More",
+ "load_from_db": false,
+ "name": "text_color",
+ "placeholder": "",
+ "required": false,
+ "show": true,
+ "title_case": false,
+ "tool_mode": false,
+ "trace_as_input": true,
+ "trace_as_metadata": true,
+ "type": "str",
+ "value": ""
+ }
+ },
+ "tool_mode": false
+ },
+ "showNode": true,
+ "type": "ChatOutput"
+ },
+ "dragging": false,
+ "id": "ChatOutput-8y94b",
+ "measured": {
+ "height": 192,
+ "width": 320
+ },
+ "position": {
+ "x": -278.71239101147415,
+ "y": 1127.8168564025477
+ },
+ "selected": false,
+ "type": "genericNode"
+ },
+ {
+ "data": {
+ "id": "note-fOJpn",
+ "node": {
+ "description": "### 💡 Add your OpenAI API key here 👇",
+ "display_name": "",
+ "documentation": "",
+ "template": {
+ "backgroundColor": "transparent"
+ }
+ },
+ "type": "note"
+ },
+ "dragging": false,
+ "height": 326,
+ "id": "note-fOJpn",
+ "measured": {
+ "height": 326,
+ "width": 352
+ },
+ "position": {
+ "x": -1296.8149861584823,
+ "y": 204.32200781235414
+ },
+ "resizing": false,
+ "selected": false,
+ "type": "noteNode",
+ "width": 353
+ },
+ {
+ "data": {
+ "id": "note-PJ0gY",
+ "node": {
+ "description": "### 💡 Add your OpenAI API key here 👇",
+ "display_name": "",
+ "documentation": "",
+ "template": {
+ "backgroundColor": "transparent"
+ }
+ },
+ "type": "note"
+ },
+ "dragging": false,
+ "id": "note-PJ0gY",
+ "measured": {
+ "height": 324,
+ "width": 324
+ },
+ "position": {
+ "x": -628.2217875041375,
+ "y": 923.6296541872401
+ },
+ "selected": false,
+ "type": "noteNode"
+ },
+ {
+ "data": {
+ "id": "note-yzNaN",
+ "node": {
+ "description": "### 💡 Add your OpenAI API key here 👇",
+ "display_name": "",
+ "documentation": "",
+ "template": {
+ "backgroundColor": "transparent"
+ }
+ },
+ "type": "note"
+ },
+ "dragging": false,
+ "id": "note-yzNaN",
+ "measured": {
+ "height": 324,
+ "width": 324
+ },
+ "position": {
+ "x": -1309.8036340068625,
+ "y": 925.6888138443481
+ },
+ "selected": true,
+ "type": "noteNode"
+ }
+ ],
+ "viewport": {
+ "x": 1436.086487750284,
+ "y": 17.453112924488266,
+ "zoom": 0.48563499996133874
+ }
+ },
+ "description": "Load text data from various file formats, process it into structured messages, and analyze sentiment using AI-powered classification.",
+ "endpoint_name": null,
+ "id": "4d068861-cb44-46b0-a70f-15467baab096",
+ "is_component": false,
+ "last_tested_version": "1.2.0",
+ "name": "Text Sentiment Analysis",
+ "tags": [
+ "classification"
+ ]
+}
\ No newline at end of file
diff --git a/src/frontend/tests/core/integrations/Text Sentiment Analysis.spec.ts b/src/frontend/tests/core/integrations/Text Sentiment Analysis.spec.ts
new file mode 100644
index 000000000..d12cc996a
--- /dev/null
+++ b/src/frontend/tests/core/integrations/Text Sentiment Analysis.spec.ts
@@ -0,0 +1,59 @@
+import { expect, test } from "@playwright/test";
+import * as dotenv from "dotenv";
+import path from "path";
+import { awaitBootstrapTest } from "../../utils/await-bootstrap-test";
+import { initialGPTsetup } from "../../utils/initialGPTsetup";
+import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes";
+
+withEventDeliveryModes(
+ "user should be able to analyze text sentiment",
+ { tag: ["@release", "@starter-projects"] },
+ async ({ page }) => {
+ test.skip(
+ !process?.env?.OPENAI_API_KEY,
+ "OPENAI_API_KEY required to run this test",
+ );
+
+ if (!process.env.CI) {
+ dotenv.config({ path: path.resolve(__dirname, "../../.env") });
+ }
+
+ await awaitBootstrapTest(page);
+
+ await page.getByTestId("side_nav_options_all-templates").click();
+ await page
+ .getByRole("heading", { name: "Text Sentiment Analysis" })
+ .click();
+ await initialGPTsetup(page);
+
+ const fileChooserPromise = page.waitForEvent("filechooser");
+ await page.getByTestId("button_upload_file").click();
+ const fileChooser = await fileChooserPromise;
+ await fileChooser.setFiles(
+ path.join(__dirname, "../../assets/test_file.txt"),
+ );
+ await page.getByText("test_file.txt").isVisible();
+
+ await page.waitForSelector('[data-testid="button_run_chat output"]', {
+ timeout: 3000,
+ });
+
+ await page.getByTestId("button_run_chat output").last().click();
+ await page.waitForSelector("text=built successfully", { timeout: 30000 });
+
+ await page.getByText("built successfully").last().click({
+ timeout: 15000,
+ });
+
+ await page.getByText("Playground", { exact: true }).last().click();
+ await page
+ .getByText("Add a Chat Input component to your flow to send messages.", {
+ exact: true,
+ })
+ .last()
+ .isVisible();
+
+ const textAnalysis = await page.locator(".markdown").last().textContent();
+ expect(textAnalysis?.length).toBeGreaterThan(100);
+ },
+);