{
  "data": {
    "edges": [
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "CustomComponent",
            "id": "CustomComponent-y0t72",
            "name": "output",
            "output_types": [
              "Data"
            ]
          },
          "targetHandle": {
            "fieldName": "data",
            "id": "LoopComponent-PTNzd",
            "inputTypes": [
              "Data"
            ],
            "type": "other"
          }
        },
        "id": "reactflow__edge-CustomComponent-y0t72{œdataTypeœ:œCustomComponentœ,œidœ:œCustomComponent-y0t72œ,œnameœ:œoutputœ,œoutput_typesœ:[œDataœ]}-LoopComponent-PTNzd{œfieldNameœ:œdataœ,œidœ:œLoopComponent-PTNzdœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
        "selected": false,
        "source": "CustomComponent-y0t72",
        "sourceHandle": "{œdataTypeœ:œCustomComponentœ,œidœ:œCustomComponent-y0t72œ,œnameœ:œoutputœ,œoutput_typesœ:[œDataœ]}",
        "target": "LoopComponent-PTNzd",
        "targetHandle": "{œfieldNameœ:œdataœ,œidœ:œLoopComponent-PTNzdœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "MessagetoData",
            "id": "MessagetoData-8O7uJ",
            "name": "data",
            "output_types": [
              "Data"
            ]
          },
          "targetHandle": {
            "dataType": "LoopComponent",
            "id": "LoopComponent-PTNzd",
            "name": "item",
            "output_types": [
              "Data"
            ]
          }
        },
        "id": "reactflow__edge-MessagetoData-8O7uJ{œdataTypeœ:œMessagetoDataœ,œidœ:œMessagetoData-8O7uJœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-LoopComponent-PTNzd{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}",
        "selected": false,
        "source": "MessagetoData-8O7uJ",
        "sourceHandle": "{œdataTypeœ:œMessagetoDataœ,œidœ:œMessagetoData-8O7uJœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}",
        "target": "LoopComponent-PTNzd",
        "targetHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "LoopComponent",
            "id": "LoopComponent-PTNzd",
            "name": "item",
            "output_types": [
              "Data"
            ]
          },
          "targetHandle": {
            "fieldName": "data",
            "id": "ParseData-qyLj8",
            "inputTypes": [
              "Data"
            ],
            "type": "other"
          }
        },
        "id": "reactflow__edge-LoopComponent-PTNzd{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}-ParseData-qyLj8{œfieldNameœ:œdataœ,œidœ:œParseData-qyLj8œ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
        "selected": false,
        "source": "LoopComponent-PTNzd",
        "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œitemœ,œoutput_typesœ:[œDataœ]}",
        "target": "ParseData-qyLj8",
        "targetHandle": "{œfieldNameœ:œdataœ,œidœ:œParseData-qyLj8œ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "ParseData",
            "id": "ParseData-qyLj8",
            "name": "text",
            "output_types": [
              "Message"
            ]
          },
          "targetHandle": {
            "fieldName": "message",
            "id": "MessagetoData-8O7uJ",
            "inputTypes": [
              "Message"
            ],
            "type": "str"
          }
        },
        "id": "reactflow__edge-ParseData-qyLj8{œdataTypeœ:œParseDataœ,œidœ:œParseData-qyLj8œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-MessagetoData-8O7uJ{œfieldNameœ:œmessageœ,œidœ:œMessagetoData-8O7uJœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
        "selected": false,
        "source": "ParseData-qyLj8",
        "sourceHandle": "{œdataTypeœ:œParseDataœ,œidœ:œParseData-qyLj8œ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}",
        "target": "MessagetoData-8O7uJ",
        "targetHandle": "{œfieldNameœ:œmessageœ,œidœ:œMessagetoData-8O7uJœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "CustomComponent",
            "id": "CustomComponent-y0t72",
            "name": "output",
            "output_types": [
              "Data"
            ]
          },
          "targetHandle": {
            "fieldName": "list2",
            "id": "MyZipper-xVGrn",
            "inputTypes": [
              "Data"
            ],
            "type": "other"
          }
        },
        "id": "reactflow__edge-CustomComponent-y0t72{œdataTypeœ:œCustomComponentœ,œidœ:œCustomComponent-y0t72œ,œnameœ:œoutputœ,œoutput_typesœ:[œDataœ]}-MyZipper-xVGrn{œfieldNameœ:œlist2œ,œidœ:œMyZipper-xVGrnœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
        "selected": false,
        "source": "CustomComponent-y0t72",
        "sourceHandle": "{œdataTypeœ:œCustomComponentœ,œidœ:œCustomComponent-y0t72œ,œnameœ:œoutputœ,œoutput_typesœ:[œDataœ]}",
        "target": "MyZipper-xVGrn",
        "targetHandle": "{œfieldNameœ:œlist2œ,œidœ:œMyZipper-xVGrnœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "LoopComponent",
            "id": "LoopComponent-PTNzd",
            "name": "done",
            "output_types": [
              "Data"
            ]
          },
          "targetHandle": {
            "fieldName": "list1",
            "id": "MyZipper-xVGrn",
            "inputTypes": [
              "Data"
            ],
            "type": "other"
          }
        },
        "id": "reactflow__edge-LoopComponent-PTNzd{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œdoneœ,œoutput_typesœ:[œDataœ]}-MyZipper-xVGrn{œfieldNameœ:œlist1œ,œidœ:œMyZipper-xVGrnœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
        "selected": false,
        "source": "LoopComponent-PTNzd",
        "sourceHandle": "{œdataTypeœ:œLoopComponentœ,œidœ:œLoopComponent-PTNzdœ,œnameœ:œdoneœ,œoutput_typesœ:[œDataœ]}",
        "target": "MyZipper-xVGrn",
        "targetHandle": "{œfieldNameœ:œlist1œ,œidœ:œMyZipper-xVGrnœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}"
      },
      {
        "animated": false,
        "className": "",
        "data": {
          "sourceHandle": {
            "dataType": "MyZipper",
            "id": "MyZipper-xVGrn",
            "name": "output",
            "output_types": [
              "Message"
            ]
          },
          "targetHandle": {
            "fieldName": "input_value",
            "id": "ChatOutput-tF7vz",
            "inputTypes": [
              "Data",
              "DataFrame",
              "Message"
            ],
            "type": "other"
          }
        },
        "id": "xy-edge__MyZipper-xVGrn{œdataTypeœ:œMyZipperœ,œidœ:œMyZipper-xVGrnœ,œnameœ:œoutputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-tF7vz{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-tF7vzœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}",
        "selected": false,
        "source": "MyZipper-xVGrn",
        "sourceHandle": "{œdataTypeœ:œMyZipperœ,œidœ:œMyZipper-xVGrnœ,œnameœ:œoutputœ,œoutput_typesœ:[œMessageœ]}",
        "target": "ChatOutput-tF7vz",
        "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-tF7vzœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}"
      }
    ],
    "nodes": [
      {
        "data": {
          "id": "MyZipper-xVGrn",
          "node": {
            "base_classes": [
              "Message"
            ],
            "beta": false,
            "conditional_paths": [],
            "custom_fields": {},
            "description": "Use as a template to create your own component.",
            "display_name": "C MyZipper",
            "documentation": "https://docs.langflow.org/components-custom-components",
            "edited": true,
            "field_order": [
              "list1",
              "list2"
            ],
            "frozen": false,
            "icon": "code",
            "legacy": false,
            "lf_version": "1.4.1",
            "metadata": {},
            "minimized": false,
            "output_types": [],
            "outputs": [
              {
                "allows_loop": false,
                "cache": true,
                "display_name": "Output",
                "hidden": false,
                "method": "build_output",
                "name": "output",
                "required_inputs": null,
                "selected": "Message",
                "tool_mode": true,
                "types": [
                  "Message"
                ],
                "value": "__UNDEFINED__"
              }
            ],
            "pinned": false,
            "template": {
              "_type": "Component",
              "code": {
                "advanced": true,
                "dynamic": true,
                "fileTypes": [],
                "file_path": "",
                "info": "",
                "list": false,
                "load_from_db": false,
                "multiline": true,
                "name": "code",
                "password": false,
                "placeholder": "",
                "required": true,
                "show": true,
                "title_case": false,
                "type": "code",
                "value": "# from lfx.field_typing import Data\nfrom lfx.custom import Component\nfrom lfx.io import MessageTextInput, Output\nfrom lfx.schema import Message\nfrom fastapi.encoders import jsonable_encoder\n\nclass CustomComponent(Component):\n    display_name = \"C MyZipper\"\n    description = \"Use as a template to create your own component.\"\n    documentation: str = \"https://docs.langflow.org/components-custom-components\"\n    icon = \"code\"\n    name = \"MyZipper\"\n\n    inputs = [\n        DataInput(\n            name=\"list1\",\n            display_name=\"List One\",\n            is_list=True,\n            required=True,\n        ),\n        DataInput(\n            name=\"list2\",\n            display_name=\"List Two\",\n            is_list=True,\n            required=True,\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"build_output\"),\n    ]\n\n    def build_output(self) -> Message:\n        list1 = self.list1\n        list2 = self.list2\n        lists = list(zip(list1, list2))\n        self.status = lists\n        msg = Message(text=json.dumps(jsonable_encoder(lists)))\n        return msg\n"
              },
              "list1": {
                "_input_type": "DataInput",
                "advanced": false,
                "display_name": "List One",
                "dynamic": false,
                "info": "",
                "input_types": [
                  "Data"
                ],
                "list": true,
                "list_add_label": "Add More",
                "name": "list1",
                "placeholder": "",
                "required": true,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "other",
                "value": ""
              },
              "list2": {
                "_input_type": "DataInput",
                "advanced": false,
                "display_name": "List Two",
                "dynamic": false,
                "info": "",
                "input_types": [
                  "Data"
                ],
                "list": true,
                "list_add_label": "Add More",
                "name": "list2",
                "placeholder": "",
                "required": true,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "other",
                "value": ""
              }
            },
            "tool_mode": false
          },
          "showNode": true,
          "type": "MyZipper"
        },
        "id": "MyZipper-xVGrn",
        "measured": {
          "height": 256,
          "width": 320
        },
        "position": {
          "x": 1273.5574899204412,
          "y": 939.9104384225966
        },
        "selected": false,
        "type": "genericNode"
      },
      {
        "data": {
          "id": "CustomComponent-y0t72",
          "node": {
            "base_classes": [
              "Data"
            ],
            "beta": false,
            "conditional_paths": [],
            "custom_fields": {},
            "description": "Use as a template to create your own component.",
            "display_name": "C SequenceMaker",
            "documentation": "https://docs.langflow.org/components-custom-components",
            "edited": true,
            "field_order": [],
            "frozen": false,
            "icon": "code",
            "legacy": false,
            "lf_version": "1.4.1",
            "metadata": {},
            "minimized": false,
            "output_types": [],
            "outputs": [
              {
                "allows_loop": false,
                "cache": true,
                "display_name": "Output",
                "hidden": false,
                "method": "build_output",
                "name": "output",
                "required_inputs": null,
                "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 lfx.field_typing import Data\nfrom lfx.custom import Component\nfrom lfx.io import MessageTextInput, Output\nfrom lfx.schema import Data\n\n\nclass CustomComponent(Component):\n    display_name = \"C SequenceMaker\"\n    description = \"Use as a template to create your own component.\"\n    documentation: str = \"https://docs.langflow.org/components-custom-components\"\n    icon = \"code\"\n    name = \"CustomComponent\"\n\n    outputs = [\n        Output(display_name=\"Output\", name=\"output\", method=\"build_output\"),\n    ]\n\n    def build_output(self) -> Data:\n        return [Data(q=i) for i in range(10)]\n"
              }
            },
            "tool_mode": false
          },
          "showNode": true,
          "type": "CustomComponent"
        },
        "id": "CustomComponent-y0t72",
        "measured": {
          "height": 167,
          "width": 320
        },
        "position": {
          "x": 197,
          "y": 979.6063779114629
        },
        "selected": false,
        "type": "genericNode"
      },
      {
        "data": {
          "description": "Iterates over a list of Data objects, outputting one item at a time and aggregating results from loop inputs.",
          "display_name": "Loop",
          "id": "LoopComponent-PTNzd",
          "node": {
            "base_classes": [
              "Data"
            ],
            "beta": false,
            "conditional_paths": [],
            "custom_fields": {},
            "description": "Iterates over a list of Data objects, outputting one item at a time and aggregating results from loop inputs.",
            "display_name": "Loop",
            "documentation": "",
            "edited": false,
            "field_order": [
              "data"
            ],
            "frozen": false,
            "icon": "infinity",
            "legacy": false,
            "lf_version": "1.4.1",
            "metadata": {},
            "minimized": false,
            "output_types": [],
            "outputs": [
              {
                "allows_loop": true,
                "cache": true,
                "display_name": "Item",
                "hidden": false,
                "method": "item_output",
                "name": "item",
                "options": null,
                "required_inputs": null,
                "selected": "Data",
                "tool_mode": true,
                "types": [
                  "Data"
                ],
                "value": "__UNDEFINED__"
              },
              {
                "allows_loop": false,
                "cache": true,
                "display_name": "Done",
                "hidden": false,
                "method": "done_output",
                "name": "done",
                "options": null,
                "required_inputs": null,
                "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 lfx.custom import Component\nfrom lfx.io import DataInput, Output\nfrom lfx.schema import Data\n\n\nclass LoopComponent(Component):\n    display_name = \"Loop\"\n    description = (\n        \"Iterates over a list of Data objects, outputting one item at a time and aggregating results from loop inputs.\"\n    )\n    icon = \"infinity\"\n\n    inputs = [\n        DataInput(\n            name=\"data\",\n            display_name=\"Data\",\n            info=\"The initial list of Data objects to iterate over.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Item\", name=\"item\", method=\"item_output\", allows_loop=True),\n        Output(display_name=\"Done\", name=\"done\", method=\"done_output\"),\n    ]\n\n    def initialize_data(self) -> None:\n        \"\"\"Initialize the data list, context index, and aggregated list.\"\"\"\n        if self.ctx.get(f\"{self._id}_initialized\", False):\n            return\n\n        # Ensure data is a list of Data objects\n        data_list = self._validate_data(self.data)\n\n        # Store the initial data and context variables\n        self.update_ctx(\n            {\n                f\"{self._id}_data\": data_list,\n                f\"{self._id}_index\": 0,\n                f\"{self._id}_aggregated\": [],\n                f\"{self._id}_initialized\": True,\n            }\n        )\n\n    def _validate_data(self, data):\n        \"\"\"Validate and return a list of Data objects.\"\"\"\n        if isinstance(data, Data):\n            return [data]\n        if isinstance(data, list) and all(isinstance(item, Data) for item in data):\n            return data\n        msg = \"The 'data' input must be a list of Data objects or a single Data object.\"\n        raise TypeError(msg)\n\n    def evaluate_stop_loop(self) -> bool:\n        \"\"\"Evaluate whether to stop item or done output.\"\"\"\n        current_index = self.ctx.get(f\"{self._id}_index\", 0)\n        data_length = len(self.ctx.get(f\"{self._id}_data\", []))\n        return current_index > data_length\n\n    def item_output(self) -> Data:\n        \"\"\"Output the next item in the list or stop if done.\"\"\"\n        self.initialize_data()\n        current_item = Data(text=\"\")\n\n        if self.evaluate_stop_loop():\n            self.stop(\"item\")\n        else:\n            # Get data list and current index\n            data_list, current_index = self.loop_variables()\n            if current_index < len(data_list):\n                # Output current item and increment index\n                try:\n                    current_item = data_list[current_index]\n                except IndexError:\n                    current_item = Data(text=\"\")\n            self.aggregated_output()\n            self.update_ctx({f\"{self._id}_index\": current_index + 1})\n\n        # Now we need to update the dependencies for the next run\n        return current_item\n\n    def update_dependency(self):\n        item_dependency_id = self.get_incoming_edge_by_target_param(\"item\")\n\n        self.graph.run_manager.run_predecessors[self._id].append(item_dependency_id)\n\n    def done_output(self) -> Data:\n        \"\"\"Trigger the done output when iteration is complete.\"\"\"\n        self.initialize_data()\n\n        if self.evaluate_stop_loop():\n            self.stop(\"item\")\n            self.start(\"done\")\n\n            return self.ctx.get(f\"{self._id}_aggregated\", [])\n        self.stop(\"done\")\n        return Data(text=\"\")\n\n    def loop_variables(self):\n        \"\"\"Retrieve loop variables from context.\"\"\"\n        return (\n            self.ctx.get(f\"{self._id}_data\", []),\n            self.ctx.get(f\"{self._id}_index\", 0),\n        )\n\n    def aggregated_output(self) -> Data:\n        \"\"\"Return the aggregated list once all items are processed.\"\"\"\n        self.initialize_data()\n\n        # Get data list and aggregated list\n        data_list = self.ctx.get(f\"{self._id}_data\", [])\n        aggregated = self.ctx.get(f\"{self._id}_aggregated\", [])\n\n        # Check if loop input is provided and append to aggregated list\n        if self.item is not None and not isinstance(self.item, str) and len(aggregated) <= len(data_list):\n            aggregated.append(self.item)\n            self.update_ctx({f\"{self._id}_aggregated\": aggregated})\n        return aggregated\n"
              },
              "data": {
                "_input_type": "DataInput",
                "advanced": false,
                "display_name": "Data",
                "dynamic": false,
                "info": "The initial list of Data objects to iterate over.",
                "input_types": [
                  "Data"
                ],
                "list": false,
                "list_add_label": "Add More",
                "name": "data",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "other",
                "value": ""
              }
            },
            "tool_mode": false
          },
          "showNode": true,
          "type": "LoopComponent"
        },
        "id": "LoopComponent-PTNzd",
        "measured": {
          "height": 280,
          "width": 320
        },
        "position": {
          "x": 585.4137083070362,
          "y": 505.0807090732918
        },
        "selected": false,
        "type": "genericNode"
      },
      {
        "data": {
          "id": "MessagetoData-8O7uJ",
          "node": {
            "base_classes": [
              "Data"
            ],
            "beta": true,
            "conditional_paths": [],
            "custom_fields": {},
            "description": "Convert a Message object to a Data object",
            "display_name": "Message to Data",
            "documentation": "",
            "edited": false,
            "field_order": [
              "message"
            ],
            "frozen": false,
            "icon": "message-square-share",
            "legacy": false,
            "lf_version": "1.4.1",
            "metadata": {},
            "minimized": false,
            "output_types": [],
            "outputs": [
              {
                "allows_loop": false,
                "cache": true,
                "display_name": "Data",
                "hidden": false,
                "method": "convert_message_to_data",
                "name": "data",
                "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 lfx.log.logger import logger\n\nfrom lfx.custom import Component\nfrom lfx.io import MessageInput, Output\nfrom lfx.schema import Data\nfrom lfx.schema.message import Message\n\n\nclass MessageToDataComponent(Component):\n    display_name = \"Message to Data\"\n    description = \"Convert a Message object to a Data object\"\n    icon = \"message-square-share\"\n    beta = True\n    name = \"MessagetoData\"\n\n    inputs = [\n        MessageInput(\n            name=\"message\",\n            display_name=\"Message\",\n            info=\"The Message object to convert to a Data object\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Data\", name=\"data\", method=\"convert_message_to_data\"),\n    ]\n\n    def convert_message_to_data(self) -> Data:\n        if isinstance(self.message, Message):\n            # Convert Message to Data\n            return Data(data=self.message.data)\n\n        msg = \"Error converting Message to Data: Input must be a Message object\"\n        logger.debug(msg, exc_info=True)\n        self.status = msg\n        return Data(data={\"error\": msg})\n"
              },
              "message": {
                "_input_type": "MessageInput",
                "advanced": false,
                "display_name": "Message",
                "dynamic": false,
                "info": "The Message object to convert to a Data object",
                "input_types": [
                  "Message"
                ],
                "list": false,
                "list_add_label": "Add More",
                "load_from_db": false,
                "name": "message",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "str",
                "value": ""
              }
            },
            "tool_mode": false
          },
          "showNode": true,
          "type": "MessagetoData"
        },
        "id": "MessagetoData-8O7uJ",
        "measured": {
          "height": 230,
          "width": 320
        },
        "position": {
          "x": 1343.3046986106053,
          "y": 472.9775668087468
        },
        "selected": false,
        "type": "genericNode"
      },
      {
        "data": {
          "description": "Convert Data objects into Messages using any {field_name} from input data.",
          "display_name": "Data to Message",
          "id": "ParseData-qyLj8",
          "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": true,
            "lf_version": "1.4.1",
            "metadata": {
              "legacy_name": "Parse Data"
            },
            "minimized": false,
            "output_types": [],
            "outputs": [
              {
                "allows_loop": false,
                "cache": true,
                "display_name": "Message",
                "hidden": false,
                "method": "parse_data",
                "name": "text",
                "options": null,
                "required_inputs": null,
                "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",
                "options": null,
                "required_inputs": null,
                "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 lfx.custom import Component\nfrom lfx.helpers.data import data_to_text, data_to_text_list\nfrom lfx.io import DataInput, MultilineInput, Output, StrInput\nfrom lfx.schema import Data\nfrom lfx.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    legacy = True\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,
                "copy_field": 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": "THIS IS Q ==> {q}"
              }
            },
            "tool_mode": false
          },
          "showNode": true,
          "type": "ParseData"
        },
        "id": "ParseData-qyLj8",
        "measured": {
          "height": 342,
          "width": 320
        },
        "position": {
          "x": 991.9841408151478,
          "y": 418
        },
        "selected": false,
        "type": "genericNode"
      },
      {
        "data": {
          "id": "ChatOutput-tF7vz",
          "node": {
            "base_classes": [
              "Message"
            ],
            "beta": false,
            "conditional_paths": [],
            "custom_fields": {},
            "description": "Display a chat message in the Playground.",
            "display_name": "Chat Output",
            "documentation": "",
            "edited": false,
            "field_order": [
              "input_value",
              "should_store_message",
              "sender",
              "sender_name",
              "session_id",
              "data_template",
              "background_color",
              "chat_icon",
              "text_color",
              "clean_data"
            ],
            "frozen": false,
            "icon": "MessagesSquare",
            "legacy": false,
            "lf_version": "1.4.1",
            "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 lfx.base.io.chat import ChatComponent\nfrom lfx.inputs import BoolInput\nfrom lfx.inputs.inputs import HandleInput\nfrom lfx.io import DropdownInput, MessageTextInput, Output\nfrom lfx.schema.data import Data\nfrom lfx.schema.dataframe import DataFrame\nfrom lfx.schema.message import Message\nfrom lfx.schema.properties import Source\nfrom lfx.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\", \"<br/>\") 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,
                "toggle": false,
                "tool_mode": false,
                "trace_as_metadata": true,
                "type": "str",
                "value": "Machine"
              },
              "sender_name": {
                "_input_type": "MessageTextInput",
                "advanced": true,
                "display_name": "Sender Name",
                "dynamic": false,
                "info": "Name of the sender.",
                "input_types": [
                  "Message"
                ],
                "list": false,
                "list_add_label": "Add More",
                "load_from_db": false,
                "name": "sender_name",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "str",
                "value": "AI"
              },
              "session_id": {
                "_input_type": "MessageTextInput",
                "advanced": true,
                "display_name": "Session ID",
                "dynamic": false,
                "info": "The session ID of the chat. If empty, the current session ID parameter will be used.",
                "input_types": [
                  "Message"
                ],
                "list": false,
                "list_add_label": "Add More",
                "load_from_db": false,
                "name": "session_id",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "str",
                "value": ""
              },
              "should_store_message": {
                "_input_type": "BoolInput",
                "advanced": true,
                "display_name": "Store Messages",
                "dynamic": false,
                "info": "Store the message in the history.",
                "list": false,
                "list_add_label": "Add More",
                "name": "should_store_message",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_metadata": true,
                "type": "bool",
                "value": true
              },
              "text_color": {
                "_input_type": "MessageTextInput",
                "advanced": true,
                "display_name": "Text Color",
                "dynamic": false,
                "info": "The text color of the name",
                "input_types": [
                  "Message"
                ],
                "list": false,
                "list_add_label": "Add More",
                "load_from_db": false,
                "name": "text_color",
                "placeholder": "",
                "required": false,
                "show": true,
                "title_case": false,
                "tool_mode": false,
                "trace_as_input": true,
                "trace_as_metadata": true,
                "type": "str",
                "value": ""
              }
            },
            "tool_mode": false
          },
          "showNode": false,
          "type": "ChatOutput"
        },
        "dragging": false,
        "id": "ChatOutput-tF7vz",
        "measured": {
          "height": 66,
          "width": 192
        },
        "position": {
          "x": 1919.7453579471505,
          "y": 967.5942772860075
        },
        "selected": false,
        "type": "genericNode"
      }
    ],
    "viewport": {
      "x": -59.74646157524057,
      "y": 33.37710013512529,
      "zoom": 0.5875454902296473
    }
  },
  "description": "Where Language Meets Logic.",
  "endpoint_name": null,
  "id": "692d3c55-f461-44b8-89ba-5c32a745e224",
  "is_component": false,
  "last_tested_version": "1.4.1",
  "name": "Untitled document",
  "tags": []
}