{
 "name": "Industry Report - Complete",
 "description": "The full eight-stage pipeline. Edit the Brief, then open the Playground and run it.",
 "data": {
  "nodes": [
   {
    "id": "ChatInput-a37b32",
    "type": "genericNode",
    "position": {
     "x": 0,
     "y": 300
    },
    "data": {
     "id": "ChatInput-a37b32",
     "type": "ChatInput",
     "node": {
      "base_classes": [
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Get chat inputs from the Playground.",
      "display_name": "Brief",
      "documentation": "https://docs.langflow.org/chat-input-and-output",
      "edited": false,
      "field_order": [
       "input_value",
       "should_store_message",
       "sender",
       "sender_name",
       "session_id",
       "context_id",
       "files"
      ],
      "frozen": false,
      "icon": "MessagesSquare",
      "legacy": false,
      "metadata": {
       "code_hash": "45974e6b1909",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.input_output.chat.ChatInput"
      },
      "minimized": true,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Chat Message",
        "group_outputs": false,
        "method": "message_response",
        "name": "message",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom lfx.base.io.chat import ChatComponent\nfrom lfx.inputs.inputs import BoolInput\nfrom lfx.io import (\n    DropdownInput,\n    FileInput,\n    MessageTextInput,\n    MultilineInput,\n    Output,\n)\nfrom lfx.schema.image import get_file_paths\nfrom lfx.schema.message import Message\nfrom lfx.services.deps import get_settings_service\nfrom lfx.utils.constants import (\n    MESSAGE_SENDER_AI,\n    MESSAGE_SENDER_NAME_USER,\n    MESSAGE_SENDER_USER,\n)\nfrom lfx.utils.file_path_security import (\n    component_file_access_scopes,\n    enforce_local_file_access,\n    is_local_file_access_restricted,\n)\n\n\nclass ChatInput(ChatComponent):\n    display_name = \"Chat Input\"\n    description = \"Get chat inputs from the Playground.\"\n    documentation: str = \"https://docs.langflow.org/chat-input-and-output\"\n    icon = \"MessagesSquare\"\n    name = \"ChatInput\"\n    minimized = True\n\n    inputs = [\n        MultilineInput(\n            name=\"input_value\",\n            display_name=\"Input Text\",\n            value=\"\",\n            info=\"Message to be passed as input.\",\n            input_types=[],\n        ),\n        BoolInput(\n            name=\"should_store_message\",\n            display_name=\"Store Messages\",\n            info=\"Store the message in the history.\",\n            value=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"sender\",\n            display_name=\"Sender Type\",\n            options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n            value=MESSAGE_SENDER_USER,\n            info=\"Type of sender.\",\n            advanced=True,\n        ),\n        MessageTextInput(\n            name=\"sender_name\",\n            display_name=\"Sender Name\",\n            info=\"Name of the sender.\",\n            value=MESSAGE_SENDER_NAME_USER,\n            advanced=True,\n        ),\n        MessageTextInput(\n            name=\"session_id\",\n            display_name=\"Session ID\",\n            info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n            advanced=True,\n        ),\n        MessageTextInput(\n            name=\"context_id\",\n            display_name=\"Context ID\",\n            info=\"The context ID of the chat. Adds an extra layer to the local memory.\",\n            value=\"\",\n            advanced=True,\n        ),\n        FileInput(\n            name=\"files\",\n            display_name=\"Files\",\n            file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n            info=\"Files to be sent with the message.\",\n            advanced=True,\n            is_list=True,\n            temp_file=True,\n        ),\n    ]\n    outputs = [\n        Output(display_name=\"Chat Message\", name=\"message\", method=\"message_response\"),\n    ]\n\n    async def message_response(self) -> Message:\n        # Ensure files is a list and filter out empty/None values\n        files = self.files if self.files else []\n        if files and not isinstance(files, list):\n            files = [files]\n        # Filter out None/empty values\n        files = [f for f in files if f is not None and f != \"\"]\n\n        # Build endpoints inject caller-controlled file references directly into\n        # ChatInput. Resolve and validate local references before the Message can\n        # later read them while constructing model attachment content.\n        if files:\n            settings_service = get_settings_service()\n            storage_type = getattr(getattr(settings_service, \"settings\", None), \"storage_type\", \"local\")\n            # The storage factory falls back to local storage for unsupported\n            # values, so only the explicitly configured S3 backend may bypass\n            # filesystem containment.\n            if str(storage_type).lower() != \"s3\" and is_local_file_access_restricted():\n                scope_ids = list(component_file_access_scopes(self))\n                # Public executions replace graph.flow_id with a per-visitor\n                # virtual ID. Only the server-populated source provenance may\n                # restore the already-validated public attachment namespace.\n                source_flow_id = getattr(self.graph, \"source_flow_id\", None)\n                if source_flow_id:\n                    scope_ids.append(source_flow_id)\n                for file_path in get_file_paths(files):\n                    enforce_local_file_access(file_path, scope_ids=scope_ids)\n\n        session_id = self.session_id or self.graph.session_id or \"\"\n        message = await Message.create(\n            text=self.input_value,\n            sender=self.sender,\n            sender_name=self.sender_name,\n            session_id=session_id,\n            context_id=self.context_id,\n            files=files,\n        )\n        if session_id and isinstance(message, Message) and self.should_store_message:\n            stored_message = await self.send_message(\n                message,\n            )\n            self.message.value = stored_message\n            message = stored_message\n\n        self.status = message\n        return message\n"
       },
       "context_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Context ID",
        "dynamic": false,
        "info": "The context ID of the chat. Adds an extra layer to the local memory.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "context_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "files": {
        "_input_type": "FileInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Files",
        "dynamic": false,
        "fileTypes": [
         "csv",
         "json",
         "pdf",
         "txt",
         "md",
         "mdx",
         "yaml",
         "yml",
         "xml",
         "html",
         "htm",
         "docx",
         "py",
         "sh",
         "sql",
         "js",
         "ts",
         "tsx",
         "jpg",
         "jpeg",
         "png",
         "bmp",
         "image"
        ],
        "file_path": "",
        "info": "Files to be sent with the message.",
        "list": true,
        "list_add_label": "Add More",
        "name": "files",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "temp_file": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "file",
        "value": ""
       },
       "input_value": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "Input Text",
        "dynamic": false,
        "info": "Message to be passed as input.",
        "input_types": [],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "input_value",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "Company: \nIndustry: \nStrategic question: "
       },
       "sender": {
        "_input_type": "DropdownInput",
        "advanced": true,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Sender Type",
        "dynamic": false,
        "external_options": {},
        "info": "Type of sender.",
        "name": "sender",
        "options": [
         "Machine",
         "User"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "User"
       },
       "sender_name": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "User"
       },
       "session_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "should_store_message": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Store Messages",
        "dynamic": false,
        "info": "Store the message in the history.",
        "list": false,
        "list_add_label": "Add More",
        "name": "should_store_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-db48ef",
    "type": "genericNode",
    "position": {
     "x": 460,
     "y": 300
    },
    "data": {
     "id": "LanguageModelComponent-db48ef",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "1. Scoping",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a research scoping specialist for an industry analysis team.\nYou will receive a brief: company name, industry sector, and the strategic\nquestion the final report must answer.\nYour only job is to produce a structured research plan built around two\nframeworks the pipeline will apply later: Porter's Five Forces and SWOT.\nDo not write any part of the final report, and do not analyse the company\nyourself.\nOutput strictly as JSON:\n{\n\"company\": \"<company>\",\n\"industry\": \"<industry>\",\n\"strategic_question\": \"<the student's question>\",\n\"five_forces_evidence_needed\": {\n\"threat_of_new_entrants\": [\"<what evidence would confirm or rule this in/out>\"],\n\"supplier_power\": [\"...\"],\n\"buyer_power\": [\"...\"],\n\"threat_of_substitutes\": [\"...\"],\n\"competitive_rivalry\": [\"...\"]\n},\n\"internal_evidence_needed\": [\"<evidence about the company itself: capabilities, resources, recent performance, weaknesses>\"]\n}\nIf the brief is missing a company, industry, or question, do not guess.\nOutput {\"clarification_needed\": \"<one specific question>\"} instead."
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "WebResearch-8ac626",
    "type": "genericNode",
    "position": {
     "x": 880,
     "y": 700
    },
    "data": {
     "id": "WebResearch-8ac626",
     "type": "ext:reports:WebResearch@extra",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "type": "code",
        "required": true,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "\"\"\"Web Research: web search via Claude's built-in web search tool, usable as an Agent tool.\n\nRuns a small Claude Haiku call with Anthropic's server-side web_search tool,\nusing the server's ANTHROPIC_API_KEY, so no extra search account is needed.\nReturns a short evidence summary with a source URL on every fact.\nCost: $10 per 1,000 searches plus tokens; MAX_SEARCHES caps each call.\n\"\"\"\n\nimport os\n\nimport anthropic\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import MessageTextInput, Output\nfrom lfx.schema.message import Message\n\nMODEL = \"claude-haiku-4-5\"\nMAX_SEARCHES = 3\nMAX_PAUSE_RESUMES = 2\n\nINSTRUCTIONS = (\n    \"Search the web to answer the research request below. Report only facts you found, \"\n    \"as a bulleted list; end every bullet with its source URL in parentheses. \"\n    \"If you find nothing reliable, say 'No reliable evidence found.' Do not speculate.\\n\\n\"\n    \"Research request: \"\n)\n\n\nclass WebResearch(Component):\n    display_name = \"Web Research\"\n    description = \"Searches the web and returns evidence bullets, each with its source URL.\"\n    icon = \"search\"\n    name = \"WebResearch\"\n\n    inputs = [\n        MessageTextInput(\n            name=\"query\",\n            display_name=\"Research request\",\n            info=\"What to look up, e.g. 'Qantas market share in Australian domestic aviation 2026'.\",\n            tool_mode=True,\n            required=True,\n        ),\n    ]\n\n    outputs = [\n        Output(name=\"results\", display_name=\"Results\", method=\"search\"),\n    ]\n\n    def search(self) -> Message:\n        key = os.environ.get(\"ANTHROPIC_API_KEY\", \"\")\n        if not key:\n            raise ValueError(\"Web search is not configured on this server. Ask your instructor.\")\n\n        client = anthropic.Anthropic(api_key=key)\n        messages = [{\"role\": \"user\", \"content\": INSTRUCTIONS + str(self.query)[:1000]}]\n        tools = [{\"type\": \"web_search_20250305\", \"name\": \"web_search\", \"max_uses\": MAX_SEARCHES}]\n\n        for _ in range(MAX_PAUSE_RESUMES + 1):\n            response = client.messages.create(model=MODEL, max_tokens=2048, messages=messages, tools=tools)\n            if response.stop_reason != \"pause_turn\":\n                break\n            messages.append({\"role\": \"assistant\", \"content\": response.content})\n\n        text_parts, sources = [], {}\n        for block in response.content:\n            if block.type == \"text\":\n                text_parts.append(block.text)\n                for c in block.citations or []:\n                    if getattr(c, \"url\", None):\n                        sources.setdefault(c.url, c.title or c.url)\n\n        text = \"\".join(text_parts).strip() or \"No reliable evidence found.\"\n        if sources:\n            text += \"\\n\\nSources:\\n\" + \"\\n\".join(f\"- {title}: {url}\" for url, title in sources.items())\n        self.status = text\n        return Message(text=text)\n",
        "fileTypes": [],
        "file_path": "",
        "password": false,
        "name": "code",
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "info": "",
        "load_from_db": false,
        "title_case": false
       },
       "query": {
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": true,
        "placeholder": "",
        "show": true,
        "name": "query",
        "value": "",
        "display_name": "Research request",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "What to look up, e.g. 'Qantas market share in Australian domestic aviation 2026'.",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "tools_metadata": {
        "tool_mode": false,
        "trace_as_metadata": true,
        "is_list": true,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "tools_metadata",
        "value": [
         {
          "name": "search",
          "description": "Searches the web and returns evidence bullets, each with its source URL.",
          "tags": [
           "search"
          ],
          "status": true,
          "approval_actions": [],
          "display_name": "search",
          "display_description": "Searches the web and returns evidence bullets, each with its source URL.",
          "readonly": false,
          "args": {
           "query": {
            "description": "What to look up, e.g. 'Qantas market share in Australian domestic aviation 2026'.",
            "title": "Query",
            "type": "string"
           }
          }
         }
        ],
        "display_name": "Actions",
        "advanced": false,
        "api_editable": false,
        "dynamic": false,
        "info": "Modify tool names and descriptions to help agents understand when to use each tool.",
        "real_time_refresh": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "tools",
        "_input_type": "ToolsInput"
       }
      },
      "description": "Searches the web and returns evidence bullets, each with its source URL.",
      "icon": "search",
      "base_classes": [
       "Message"
      ],
      "display_name": "Web Research",
      "documentation": "",
      "minimized": false,
      "custom_fields": {},
      "output_types": [],
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Tool"
        ],
        "selected": "Tool",
        "name": "component_as_tool",
        "hidden": null,
        "display_name": "Toolset",
        "method": "to_toolkit",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "query"
      ],
      "beta": false,
      "legacy": false,
      "edited": false,
      "metadata": {
       "module": "custom_components.web_research",
       "code_hash": "3a116b12a59e",
       "dependencies": {
        "total_dependencies": 2,
        "dependencies": [
         {
          "name": "anthropic",
          "version": "0.120.0"
         },
         {
          "name": "lfx",
          "version": "1.12.2"
         }
        ]
       }
      },
      "tool_mode": true
     },
     "showNode": true
    }
   },
   {
    "id": "Agent-42850e",
    "type": "genericNode",
    "position": {
     "x": 920,
     "y": 300
    },
    "data": {
     "id": "Agent-42850e",
     "type": "Agent",
     "node": {
      "base_classes": [
       "Data",
       "JSON",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Define the agent's instructions, then enter a task to complete using tools.",
      "display_name": "2. Research",
      "documentation": "https://docs.langflow.org/agents",
      "edited": false,
      "field_order": [
       "model",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "system_prompt",
       "context_id",
       "n_messages",
       "max_tokens",
       "format_instructions",
       "output_schema",
       "tools",
       "input_value",
       "handle_parsing_errors",
       "max_iterations",
       "stream",
       "add_current_date_tool",
       "add_calculator_tool"
      ],
      "frozen": false,
      "icon": "bot",
      "legacy": false,
      "metadata": {
       "code_hash": "52a387b4d967",
       "dependencies": {
        "dependencies": [
         {
          "name": "langchain",
          "version": "1.3.14"
         },
         {
          "name": "langgraph",
          "version": "1.2.9"
         },
         {
          "name": "lfx",
          "version": null
         },
         {
          "name": "langchain_core",
          "version": "1.5.1"
         }
        ],
        "total_dependencies": 4
       },
       "module": "lfx.components.models_and_agents.agent.AgentComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Response",
        "group_outputs": false,
        "method": "message_response",
        "name": "response",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Structured Response",
        "group_outputs": false,
        "method": "json_response",
        "name": "structured_response",
        "selected": "Data",
        "tool_mode": true,
        "types": [
         "Data",
         "JSON"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "add_calculator_tool": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Calculator",
        "dynamic": false,
        "info": "If true, adds a zero-config arithmetic calculator tool to the agent (safe: only +, -, *, /, ** operators via AST).",
        "list": false,
        "list_add_label": "Add More",
        "name": "add_calculator_tool",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "add_current_date_tool": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Current Date",
        "dynamic": false,
        "info": "If true, will add a tool to the agent that returns the current date.",
        "list": false,
        "list_add_label": "Add More",
        "name": "add_current_date_tool",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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 __future__ import annotations\n\nimport uuid\nfrom collections.abc import Mapping\nfrom contextlib import contextmanager\nfrom datetime import datetime, timezone\nfrom typing import TYPE_CHECKING, Any, cast\n\nfrom langchain.agents import create_agent\nfrom langchain.agents.middleware import (\n    HumanInTheLoopMiddleware,\n    ModelCallLimitMiddleware,\n    ToolRetryMiddleware,\n)\nfrom langgraph.types import Command\n\nfrom lfx.components.models_and_agents.agent_helpers.graph_event_adapter import (\n    adapt_graph_events_to_executor_shape,\n)\nfrom lfx.components.models_and_agents.agent_helpers.messages_input_builder import (\n    build_initial_messages,\n)\nfrom lfx.components.models_and_agents.agent_helpers.placeholder_corrective_middleware import (\n    WatsonXPlaceholderMiddleware,\n)\nfrom lfx.components.models_and_agents.agent_helpers.single_tool_call_middleware import (\n    SingleToolCallMiddleware,\n)\nfrom lfx.components.models_and_agents.agent_helpers.tool_approval import ToolApprovalMixin\nfrom lfx.components.models_and_agents.agent_helpers.tool_call_id_middleware import ToolCallIDMiddleware\nfrom lfx.components.models_and_agents.memory import MemoryComponent, _safe_graph_user_id, aget_agent_chat_history\n\nif TYPE_CHECKING:\n    from collections.abc import Awaitable, Callable\n\n    from langchain_core.tools import Tool\n\n    from lfx.schema.log import OnTokenFunctionType, SendMessageFunctionType\n\nfrom lfx.base.agents.agent import LCToolsAgentComponent\nfrom lfx.base.agents.callback import AgentAsyncHandler\nfrom lfx.base.agents.default_system_prompt import DEFAULT_SYSTEM_PROMPT_TEMPLATE\nfrom lfx.base.agents.events import AgentPausedError, ExceptionWithMessageError, process_agent_events\nfrom lfx.base.agents.token_callback import TokenUsageCallbackHandler\nfrom lfx.base.agents.utils import get_chat_output_sender_name\nfrom lfx.base.constants import STREAM_INFO_TEXT\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.agentics.helpers.model_config import validate_model_selection\nfrom lfx.components.helpers import CalculatorComponent, CurrentDateComponent\nfrom lfx.components.langchain_utilities.ibm_granite_handler import is_watsonx_model\nfrom lfx.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom lfx.custom.custom_component.component import get_component_toolkit\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, ModelInput, StrInput\nfrom lfx.io import IntInput, MessageTextInput, MultilineInput, Output, SecretStrInput, TableInput\nfrom lfx.log.logger import logger\nfrom lfx.memory import delete_message\nfrom lfx.schema.data import Data\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.schema.table import EditMode\nfrom lfx.utils.constants import MESSAGE_SENDER_AI\n\n\ndef set_advanced_true(component_input):\n    component_input.advanced = True\n    return component_input\n\n\ndef _agent_base_inputs():\n    \"\"\"Return base inputs tailored to AgentComponent's create_agent path.\n\n    `get_base_inputs()` returns a shared list \u2014 replace, don't mutate. We drop\n    inputs that are no-ops here and override info text on the inputs whose\n    semantics shifted under create_agent.\n\n    `verbose` is dropped because the create_agent event stream already surfaces\n    every agent step via the \"Agent Steps\" content blocks; the legacy boolean\n    has nothing to toggle. Saved flows that still carry a `verbose` value just\n    ignore it on load (the schema no longer declares it).\n    \"\"\"\n    drop = {\"verbose\"}\n    overrides = {\n        \"handle_parsing_errors\": BoolInput(\n            name=\"handle_parsing_errors\",\n            display_name=\"Handle Parse Errors\",\n            value=True,\n            advanced=True,\n            info=(\n                \"Adds tool-execution retry as a safety net. `create_agent` already \"\n                \"feeds tool-call validation errors back to the LLM automatically; \"\n                \"this flag layers `ToolRetryMiddleware` on top so transient tool \"\n                \"runtime failures are retried (max 2 retries).\"\n            ),\n        ),\n        \"max_iterations\": IntInput(\n            name=\"max_iterations\",\n            display_name=\"Max Iterations\",\n            value=15,\n            advanced=True,\n            range_spec=RangeSpec(min=1, max=128000, step=1, step_type=\"int\"),\n            info=(\n                \"Maximum number of model calls the agent can make before stopping \"\n                \"(maps to `ModelCallLimitMiddleware.run_limit` on the create_agent \"\n                \"path). Must be at least 1 \u2014 it is a safety cap, never 'unlimited'.\"\n            ),\n        ),\n    }\n    return [overrides.get(inp.name, inp) for inp in LCToolsAgentComponent.get_base_inputs() if inp.name not in drop]\n\n\ndef _extract_text_content(value) -> str:\n    \"\"\"Pull a string payload from a Message-like, AIMessage-like, or string value.\"\"\"\n    if isinstance(value, str):\n        return value\n    text = getattr(value, \"text\", None)\n    if isinstance(text, str):\n        return text\n    content = getattr(value, \"content\", None)\n    if isinstance(content, str):\n        return content\n    return str(value) if value is not None else \"\"\n\n\n@contextmanager\ndef _suppress_send_message(component: Any):\n    \"\"\"Temporarily replace component.send_message with a no-op for the duration of the block.\n\n    Used during the structured-output prompt fallback: run_agent streams the agent's\n    final answer through self.send_message (correct for message_response), but in\n    json_response the orchestrator parses that text into structured Data which the\n    downstream Chat Output emits \u2014 leaving the original emission in place produces a\n    duplicate message in the playground. The original method is always restored on exit,\n    even when the wrapped call raises.\n    \"\"\"\n    original = component.send_message\n\n    async def _noop(message, *_args, **_kwargs):\n        return message\n\n    component.send_message = _noop\n    try:\n        yield\n    finally:\n        component.send_message = original\n\n\nclass AgentComponent(ToolApprovalMixin, ToolCallingAgentComponent):\n    display_name: str = \"Agent\"\n    description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n    documentation: str = \"https://docs.langflow.org/agents\"\n    icon = \"bot\"\n    beta = False\n    name = \"Agent\"\n\n    memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n            # Agents require tool calling \u2014 the filter is honored by\n            # ``handle_model_input_update`` so models that can't run with\n            # tools never reach the picker (and any saved selection that\n            # no longer satisfies the constraint is auto-replaced).\n            filters={\"tool_calling\": True},\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        MultilineInput(\n            name=\"system_prompt\",\n            display_name=\"Agent Instructions\",\n            info=(\n                \"System Prompt: Initial instructions and context provided to guide the agent's behavior. \"\n                \"Supports dynamic placeholders: {current_date}, {model_name}, {optional_user_context}.\"\n            ),\n            value=DEFAULT_SYSTEM_PROMPT_TEMPLATE,\n            advanced=False,\n        ),\n        MessageTextInput(\n            name=\"context_id\",\n            display_name=\"Context ID\",\n            info=\"The context ID of the chat. Adds an extra layer to the local memory.\",\n            value=\"\",\n            advanced=True,\n        ),\n        IntInput(\n            name=\"n_messages\",\n            display_name=\"Number of Chat History Messages\",\n            value=100,\n            info=\"Number of chat history messages to retrieve.\",\n            advanced=True,\n            show=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n        MultilineInput(\n            name=\"format_instructions\",\n            display_name=\"Output Format Instructions\",\n            info=\"Generic Template for structured output formatting. Valid only with Structured response.\",\n            value=(\n                \"You are an AI that extracts structured JSON objects from unstructured text. \"\n                \"Use a predefined schema with expected types (str, int, float, bool, dict). \"\n                \"Extract ALL relevant instances that match the schema - if multiple patterns exist, capture them all. \"\n                \"Fill missing or ambiguous values with defaults: null for missing values. \"\n                \"Remove exact duplicates but keep variations that have different field values. \"\n                \"Always return valid JSON in the expected format, never throw errors. \"\n                \"If multiple objects can be extracted, return them all in the structured format.\"\n            ),\n            advanced=True,\n        ),\n        TableInput(\n            name=\"output_schema\",\n            display_name=\"Output Schema\",\n            info=(\n                \"Schema Validation: Define the structure and data types for structured output. \"\n                \"No validation if no output schema.\"\n            ),\n            advanced=True,\n            required=False,\n            value=[],\n            table_schema=[\n                {\n                    \"name\": \"name\",\n                    \"display_name\": \"Name\",\n                    \"type\": \"str\",\n                    \"description\": \"Specify the name of the output field.\",\n                    \"default\": \"field\",\n                    \"edit_mode\": EditMode.INLINE,\n                },\n                {\n                    \"name\": \"description\",\n                    \"display_name\": \"Description\",\n                    \"type\": \"str\",\n                    \"description\": \"Describe the purpose of the output field.\",\n                    \"default\": \"description of field\",\n                    \"edit_mode\": EditMode.POPOVER,\n                },\n                {\n                    \"name\": \"type\",\n                    \"display_name\": \"Type\",\n                    \"type\": \"str\",\n                    \"edit_mode\": EditMode.INLINE,\n                    \"description\": (\"Indicate the data type of the output field (e.g., str, int, float, bool, dict).\"),\n                    \"options\": [\"str\", \"int\", \"float\", \"bool\", \"dict\"],\n                    \"default\": \"str\",\n                },\n                {\n                    \"name\": \"multiple\",\n                    \"display_name\": \"As List\",\n                    \"type\": \"boolean\",\n                    \"description\": \"Set to True if this output field should be a list of the specified type.\",\n                    \"default\": \"False\",\n                    \"edit_mode\": EditMode.INLINE,\n                },\n            ],\n        ),\n        *_agent_base_inputs(),\n        # removed memory inputs from agent component\n        # *memory_inputs,\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=STREAM_INFO_TEXT,\n            value=True,\n            advanced=True,\n        ),\n        BoolInput(\n            name=\"add_current_date_tool\",\n            display_name=\"Current Date\",\n            advanced=True,\n            info=\"If true, will add a tool to the agent that returns the current date.\",\n            value=True,\n        ),\n        BoolInput(\n            name=\"add_calculator_tool\",\n            display_name=\"Calculator\",\n            advanced=True,\n            info=(\n                \"If true, adds a zero-config arithmetic calculator tool to the agent \"\n                \"(safe: only +, -, *, /, ** operators via AST).\"\n            ),\n            value=True,\n        ),\n    ]\n    outputs = [\n        Output(name=\"response\", display_name=\"Response\", method=\"message_response\"),\n        Output(\n            name=\"structured_response\",\n            display_name=\"Structured Response\",\n            method=\"json_response\",\n            types=[\"Data\"],\n        ),\n    ]\n\n    async def _additional_model_provider_policy_ids(self, purpose, parameters=None) -> tuple[str, ...]:\n        \"\"\"Gate the provider selector retained by legacy Agent/ALTK flows.\"\"\"\n        _ = purpose\n        from lfx.base.models.provider_registry import resolve_provider_id\n\n        effective_parameters = parameters if isinstance(parameters, Mapping) else getattr(self, \"_parameters\", None)\n        if not isinstance(effective_parameters, Mapping):\n            effective_parameters = {}\n        selected_model = effective_parameters.get(\"model\", getattr(self, \"model\", None))\n        if selected_model:\n            # The shared Component hook already handles a selected ModelInput;\n            # connected model objects are gated by their upstream vertex.\n            return ()\n        legacy_provider = effective_parameters.get(\"agent_llm\", getattr(self, \"agent_llm\", None))\n        if not isinstance(legacy_provider, str) or not legacy_provider.strip() or legacy_provider == \"Custom\":\n            return ()\n        return (resolve_provider_id(legacy_provider),)\n\n    async def _filter_legacy_provider_options(self, build_config: Mapping[str, Any]) -> None:\n        \"\"\"Filter ALTK/legacy Agent provider choices through the active scope.\"\"\"\n        from lfx.services.model_provider_policy import ModelProviderPolicyPurpose, aresolve_model_provider_policy\n\n        provider_field = build_config.get(\"agent_llm\")\n        if not isinstance(provider_field, dict):\n            return\n        options = provider_field.get(\"options\")\n        if not isinstance(options, list):\n            return\n        candidates = [option for option in options if isinstance(option, str) and option != \"Custom\"]\n        if not candidates:\n            return\n        snapshot = await aresolve_model_provider_policy(\n            user_id=self.user_id,\n            providers=candidates,\n            purpose=ModelProviderPolicyPurpose.CONFIGURE,\n        )\n        allowed = set(snapshot.filter(candidates))\n        keep_indexes = [index for index, option in enumerate(options) if option == \"Custom\" or option in allowed]\n        provider_field[\"options\"] = [options[index] for index in keep_indexes]\n        metadata = provider_field.get(\"options_metadata\")\n        if isinstance(metadata, list) and len(metadata) == len(options):\n            provider_field[\"options_metadata\"] = [metadata[index] for index in keep_indexes]\n\n    def _resolve_selected_model(self):\n        \"\"\"Resolve the selected model, including legacy agent_llm/model_name inputs.\"\"\"\n        try:\n            from langchain_core.language_models import BaseLanguageModel\n\n            if isinstance(self.model, BaseLanguageModel):\n                return self.model\n        except ImportError:\n            pass\n\n        if isinstance(self.model, list) and self.model:\n            return self.model\n\n        legacy_provider = getattr(self, \"agent_llm\", None)\n        legacy_model_name = getattr(self, \"model_name\", None)\n        if not legacy_provider or not legacy_model_name:\n            return self.model\n\n        options = get_language_model_options(user_id=self.user_id)\n        for option in options:\n            if option.get(\"provider\") == legacy_provider and option.get(\"name\") == legacy_model_name:\n                return [option]\n\n        return [\n            {\n                \"name\": legacy_model_name,\n                \"provider\": legacy_provider,\n                \"metadata\": {},\n            }\n        ]\n\n    def _get_max_tokens_value(self):\n        \"\"\"Return the user-supplied max_tokens or None when unset/zero.\"\"\"\n        val = getattr(self, \"max_tokens\", None)\n        if val in {\"\", 0}:\n            return None\n        return val\n\n    def _get_llm(self):\n        \"\"\"Override parent to include max_tokens from the Agent's input field.\n\n        Streaming is mandatory for AgentComponent: ``runnable.astream_events(v2)`` only\n        emits ``on_chat_model_stream`` chunks when the underlying chat model is\n        instantiated with ``streaming=True``. Unlike the LanguageModel component (where\n        ``stream`` is a user-facing toggle), the Agent has no opt-out \u2014 the toggle is\n        kept in the UI for backwards compatibility but is intentionally ignored here.\n        Without ``stream=True``, the chat model accumulates the whole response and\n        only emits ``on_chat_model_end``, silently disabling the Playground's live-\n        typing view and breaking the streaming contract on the /events surface.\n        \"\"\"\n        return get_llm(\n            model=self.model,\n            user_id=self.user_id,\n            api_key=getattr(self, \"api_key\", None),\n            stream=True,\n            max_tokens=self._get_max_tokens_value(),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            overrides=getattr(self, \"_model_overrides\", None),\n        )\n\n    async def get_agent_requirements(self):\n        \"\"\"Get the agent requirements for the agent.\"\"\"\n        from langchain_core.tools import StructuredTool\n\n        from lfx.services.model_provider_policy import ModelProviderPolicyPurpose\n\n        await self.arequire_model_provider_policy(ModelProviderPolicyPurpose.USE)\n\n        selected_model = self._resolve_selected_model()\n        try:\n            from langchain_core.language_models import BaseLanguageModel\n\n            is_connected_model = isinstance(selected_model, BaseLanguageModel)\n        except ImportError:\n            is_connected_model = False\n\n        if not is_connected_model:\n            validate_model_selection(selected_model)\n\n        # Ensure _get_llm() uses the resolved model (e.g. from legacy agent_llm/model_name)\n        self.model = selected_model\n        llm_model = self._get_llm()\n        if llm_model is None:\n            msg = \"No language model selected. Please choose a model to proceed.\"\n            raise ValueError(msg)\n\n        # Get memory data\n        self.chat_history = await self.get_memory_data()\n        await logger.adebug(f\"Retrieved {len(self.chat_history)} chat history messages\")\n        if isinstance(self.chat_history, Message):\n            self.chat_history = [self.chat_history]\n\n        # Add current date tool if enabled\n        if self.add_current_date_tool:\n            if not isinstance(self.tools, list):  # type: ignore[has-type]\n                self.tools = []\n            current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n\n            if not isinstance(current_date_tool, StructuredTool):\n                msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n                raise TypeError(msg)\n            # Skip if an externally-connected tool already provides the same name.\n            # Duplicate tool names are rejected by Anthropic/Gemini with HTTP 400.\n            if not any(getattr(t, \"name\", None) == current_date_tool.name for t in self.tools):\n                self.tools.append(current_date_tool)\n\n        # Add calculator tool if enabled (zero-config arithmetic)\n        if getattr(self, \"add_calculator_tool\", False):\n            if not isinstance(self.tools, list):  # type: ignore[has-type]\n                self.tools = []\n            calculator_tool = (await CalculatorComponent(**self.get_base_args()).to_toolkit()).pop(0)\n\n            if not isinstance(calculator_tool, StructuredTool):\n                msg = \"CalculatorComponent must be converted to a StructuredTool\"\n                raise TypeError(msg)\n            # Skip if an externally-connected tool already provides the same name.\n            # Duplicate tool names are rejected by Anthropic/Gemini with HTTP 400.\n            if not any(getattr(t, \"name\", None) == calculator_tool.name for t in self.tools):\n                self.tools.append(calculator_tool)\n\n        # Set shared callbacks for tracing the tools used by the agent\n        self.set_tools_callbacks(self.tools, self._get_shared_callbacks())\n\n        return llm_model, self.chat_history, self.tools\n\n    def _get_resolved_model_name(self) -> str:\n        \"\"\"Best-effort human-readable model name for {model_name} injection.\"\"\"\n        try:\n            from langchain_core.language_models import BaseLanguageModel\n\n            if isinstance(self.model, BaseLanguageModel):\n                return type(self.model).__name__\n        except ImportError:\n            pass\n\n        if isinstance(self.model, list) and self.model:\n            first = self.model[0]\n            if isinstance(first, dict):\n                name = first.get(\"name\")\n                if isinstance(name, str) and name:\n                    return name\n\n        legacy_model_name = getattr(self, \"model_name\", None)\n        if isinstance(legacy_model_name, str) and legacy_model_name:\n            return legacy_model_name\n        return \"\"\n\n    def _inject_dynamic_prompt_values(self, prompt: Any | None) -> str | None:\n        \"\"\"Replace known env placeholders in the system prompt.\n\n        Handles {current_date}, {model_name}, and {optional_user_context} (the\n        last one ships with the structured DEFAULT_SYSTEM_PROMPT_TEMPLATE and\n        is currently unused at the AgentComponent layer, so it resolves to \"\").\n        Uses str.replace (not str.format) so user prompts containing literal\n        braces such as JSON examples ({\"key\": 1}) never break the agent.\n\n        `system_prompt` is a connectable MultilineInput, so the value can arrive\n        as a Message (e.g. a Prompt node wired in). Normalize it to text first \u2014\n        a raw Message has no `.replace` and used to crash the agent build.\n        \"\"\"\n        if prompt is None:\n            return None\n        prompt = _extract_text_content(prompt)\n        if not prompt:\n            return prompt\n        replacements = {\n            \"{current_date}\": datetime.now(tz=timezone.utc).strftime(\"%Y-%m-%d %H:%M:%S UTC\"),\n            \"{model_name}\": self._get_resolved_model_name(),\n            \"{optional_user_context}\": \"\",\n        }\n        for placeholder, value in replacements.items():\n            prompt = prompt.replace(placeholder, value)\n        return prompt\n\n    def create_agent_runnable(self, *, allow_interrupts: bool = True):\n        \"\"\"Build the LangGraph `CompiledStateGraph` via `langchain.agents.create_agent`.\n\n        Replaces the legacy `AgentExecutor` runnable inherited from\n        `ToolCallingAgentComponent`. Other agent components (tool_calling, csv, json,\n        openapi, sql*, vector_store_router) keep the legacy path \u2014 only AgentComponent\n        runs on the new graph API.\n\n        `max_iterations` and `handle_parsing_errors` (legacy AgentExecutor knobs) are\n        translated to LangGraph middleware. Without that translation those user inputs\n        would silently become no-ops on the new API.\n\n        Provider notes:\n        - WatsonX/Granite work natively with create_agent \u2014 `ChatWatsonx.bind_tools`\n          handles tool_choice correctly. The legacy `create_granite_agent` path was\n          dropped because it hardcoded `tool_choice='required'`, which the WatsonX\n          API now rejects.\n        - Ollama and other small/local models often emit malformed tool args. The\n          ToolRetryMiddleware (default `retry_on=(Exception,)`, `on_failure='continue'`)\n          catches Pydantic ValidationErrors from bad args and feeds the error back\n          to the LLM as a retry signal, so the agent recovers gracefully.\n        \"\"\"\n        llm = self._get_llm()\n        tools = self.tools or []\n\n        # Eager bind_tools validation. `create_agent(...)` is lazy \u2014 without this,\n        # an LLM that doesn't support tool calling fails on the first user message\n        # instead of when the user wires up the component, which is a much worse UX.\n        # Gated on a non-empty tools list so a no-tool Agent on a plain chat model\n        # (which legitimately has no `bind_tools`) isn't shut out at flow-build time.\n        # Providers signal \"no tool calling\" inconsistently \u2014 `NotImplementedError`\n        # (langchain default), `AttributeError` (no `bind_tools` attr), or `TypeError`\n        # (signature mismatch). Treat all three as the same UX failure.\n        if tools:\n            try:\n                llm.bind_tools(tools)\n            except (NotImplementedError, AttributeError, TypeError) as exc:\n                # Include the underlying error so a broken tool schema or a\n                # provider implementation bug is not silently disguised as a\n                # \"model can't call tools\" UX error.\n                msg = (\n                    f\"{self.display_name} does not support tool calling, \"\n                    \"or one of the connected tools failed to bind. \"\n                    \"Please connect a tool-calling capable language model and \"\n                    f\"verify your tools are well-formed. Underlying error: {exc!s}\"\n                )\n                raise NotImplementedError(msg) from exc\n\n        middleware = self._build_middleware(llm, allow_interrupts=allow_interrupts)\n        checkpointer = self._build_agent_checkpointer() if allow_interrupts else None\n        return create_agent(\n            model=llm,\n            tools=tools,\n            system_prompt=self.system_prompt or \"\",\n            middleware=middleware or None,\n            checkpointer=checkpointer,\n        )\n\n    def _compute_recursion_limit(self) -> int:\n        \"\"\"Derive the LangGraph recursion_limit from the user-set max_iterations.\n\n        Mirrors the clamp in `_build_middleware` (max(1, max_iterations)) so a\n        saved 0 or negative value cannot under-cap the graph below one full\n        iteration. The +5 buffer covers start/end/router overhead.\n        \"\"\"\n        raw = getattr(self, \"max_iterations\", None)\n        run_limit = max(1, int(raw)) if raw is not None else 15\n        return run_limit * 2 + 5\n\n    def _build_middleware(self, llm: Any, *, allow_interrupts: bool = True) -> list:\n        # `llm` is passed in (rather than re-fetched via `self._get_llm()`)\n        # because some providers do credential resolution / client instantiation\n        # lazily on each call. The caller \u2014 `create_agent_runnable` \u2014 already\n        # resolved it once for `bind_tools`, so reuse that instance here.\n        middleware: list = []\n        # LangChain accepts missing IDs on AIMessage.tool_calls, but LangGraph's\n        # invalid-call path and ToolRetryMiddleware both require a string when\n        # they construct an error ToolMessage. Normalize at the model boundary\n        # so either recovery path can return the error to the model instead of\n        # crashing the flow.\n        if self.tools:\n            middleware.append(ToolCallIDMiddleware())\n        max_iterations = getattr(self, \"max_iterations\", None)\n        if max_iterations is not None:\n            # `max_iterations` is a safety cap, not an \"unlimited\" toggle. A saved\n            # 0 or negative value (falsy) must NOT silently drop the limiter and\n            # allow an unbounded model/tool loop \u2014 clamp it to a real minimum.\n            run_limit = max(1, int(max_iterations))\n            middleware.append(ModelCallLimitMiddleware(run_limit=run_limit))\n        # ToolRetryMiddleware only matters when there ARE tools to retry. Attaching\n        # it on a no-tools agent inflates the compiled graph and adds per-invocation\n        # middleware overhead for nothing, which is a measurable contributor to\n        # trivial-prompt latency (QA UI-003).\n        if getattr(self, \"handle_parsing_errors\", False) and self.tools:\n            middleware.append(ToolRetryMiddleware(max_retries=2))\n        # WatsonX models have two known platform quirks; both still reproduce on\n        # the current API, so we keep the protections from the legacy\n        # `create_granite_agent` path.\n        # 1. Multi-tool-call assistant turns are rejected (\"This model only\n        #    supports single tool-calls at once!\"). Clamp to one per turn.\n        # 2. Tool args occasionally come back as literal placeholder strings\n        #    (e.g. `<result-from-search>`). Re-invoke once with a corrective\n        #    SystemMessage.\n        # Order: SingleToolCallMiddleware first (outermost) so the clamp is\n        # applied to the final response, including any corrective re-invoke\n        # produced by WatsonXPlaceholderMiddleware.\n        if is_watsonx_model(llm):\n            middleware.append(SingleToolCallMiddleware())\n            middleware.append(WatsonXPlaceholderMiddleware())\n        # Human-in-the-loop: attach only when a tool is gated AND interrupts are allowed\n        # (the structured-output path disables them), keeping ungated flows unchanged.\n        interrupt_on = self._gated_interrupt_on() if allow_interrupts else {}\n        if interrupt_on:\n            middleware.append(HumanInTheLoopMiddleware(interrupt_on=interrupt_on))\n        return middleware\n\n    async def run_agent(self, agent) -> Message:\n        \"\"\"Run the LangGraph `CompiledStateGraph` and return the final agent Message.\n\n        Overrides the legacy `LCAgentComponent.run_agent` (which builds an\n        `{\"input\": str, \"chat_history\": [...]}` dict for `AgentExecutor`). The graph\n        wants `{\"messages\": [BaseMessage, ...]}`. The event stream is wrapped with\n        `adapt_graph_events_to_executor_shape` so the legacy `process_agent_events`\n        (in `lfx.base.agents.events`) can be reused unchanged.\n        \"\"\"\n        messages = build_initial_messages(\n            input_value=self.input_value,\n            chat_history=getattr(self, \"chat_history\", None),\n        )\n        input_dict = {\"messages\": messages}\n\n        agent_message = self._build_initial_agent_message()\n        token_usage_handler = TokenUsageCallbackHandler()\n\n        # Stream tokens to the event manager when running inside the Langflow runtime.\n        # This is what powers the live-typing view in the chat UI.\n        on_token_callback: OnTokenFunctionType | None = None\n        if getattr(self, \"_event_manager\", None):\n            on_token_callback = cast(\"OnTokenFunctionType\", self._event_manager.on_token)\n\n        # Align LangGraph's `recursion_limit` with `max_iterations` so the\n        # middleware cap (ModelCallLimitMiddleware) is what bounds the loop \u2014\n        # not LangGraph's default 25-step guard, which fires at ~12 model+tool\n        # iterations and raises a raw GraphRecursionError (QA UI-009/UI-010).\n        # Each iteration is ~2 graph steps (model node + tools node); add 5\n        # for start/end overhead.\n        recursion_limit = self._compute_recursion_limit()\n\n        agent_config: dict[str, Any] = {\n            \"callbacks\": [\n                AgentAsyncHandler(self.log),\n                token_usage_handler,\n                *self._get_shared_callbacks(),\n            ],\n            \"recursion_limit\": recursion_limit,\n        }\n        # The durable checkpointer keys on the per-run thread_id; it must be in the\n        # astream_events config (not just create_agent) for both initial run and resume.\n        thread_id = self._agent_thread_id()\n        get_pending_interrupt = None\n        stream_input: Any = input_dict\n        # A checkpointer is only present when the agent was built with interrupts enabled\n        # (the structured-output path builds without one); never probe its state otherwise.\n        interrupts_enabled = getattr(agent, \"checkpointer\", None) is not None\n        if interrupts_enabled and thread_id and self._gated_interrupt_on():\n            agent_config[\"configurable\"] = {\"thread_id\": thread_id}\n            get_pending_interrupt = self._pending_interrupt_getter(agent, agent_config)\n            if self._has_candidate_decision(thread_id):\n                # The injected decision must match the pending interrupt's nonce, so read\n                # the interrupt first; a matched decision resumes the checkpointed thread.\n                value, interrupt_id = await self._read_pending_interrupt(agent, agent_config)\n                decision = self._injected_agent_decision(thread_id, interrupt_id)\n                if decision is not None:\n                    action_requests = (value or {}).get(\"action_requests\") or []\n                    stream_input = Command(\n                        resume={\"decisions\": self._build_resume_decisions(decision, action_requests)}\n                    )\n        stream = adapt_graph_events_to_executor_shape(\n            agent.astream_events(stream_input, config=agent_config, version=\"v2\")\n        )\n        try:\n            result = await process_agent_events(\n                stream,\n                agent_message,\n                cast(\"SendMessageFunctionType\", self.send_message),\n                on_token_callback,\n                get_pending_interrupt=get_pending_interrupt,\n            )\n        except AgentPausedError as e:\n            # Why: retract the empty partial bubble (leaks as \"[]\"); the HITL card supersedes it, resume re-emits it.\n            paused_message = e.agent_message or agent_message\n            msg_id = paused_message.get_id()\n            if msg_id:\n                await delete_message(id_=msg_id)\n            await self._send_message_event(paused_message, category=\"remove_message\")\n            return self._suspend_for_tool_approval(e.request, paused_message)\n        except ExceptionWithMessageError as e:\n            # Drop the half-stored partial message from the DB (only if it was\n            # actually persisted) and tell the frontend to remove the stale bubble.\n            if hasattr(e, \"agent_message\"):\n                msg_id = e.agent_message.get_id()\n                if msg_id:\n                    await delete_message(id_=msg_id)\n                await self._send_message_event(e.agent_message, category=\"remove_message\")\n            # exception(), not error(f\"...{e}\"): this class's str() interpolates the model's\n            # partial completion, and passing the exception rather than formatting it is what\n            # gives the exported record an error.type to triage on.\n            logger.exception(\"Agent run failed after a partial message was emitted\")\n            raise\n\n        usage_data = token_usage_handler.get_usage()\n        if usage_data:\n            self._token_usage = usage_data\n            result.properties.usage = usage_data\n            # Only round-trip the DB when the message was stored (Chat Output wired).\n            # `_should_skip_message=True` leaves `result.get_id()` empty; persisting\n            # then would create a phantom row.\n            if result.get_id():\n                stored_result = await self._update_stored_message(result)\n                await self._send_message_event(stored_result)\n                result = stored_result\n\n        self.status = result\n        return result\n\n    def _build_initial_agent_message(self) -> Message:\n        \"\"\"Construct the placeholder agent Message that `process_agent_events` mutates.\"\"\"\n        if hasattr(self, \"graph\"):\n            session_id = self.graph.session_id\n        elif hasattr(self, \"_session_id\"):\n            session_id = self._session_id\n        else:\n            session_id = None\n\n        sender_name = get_chat_output_sender_name(self) or self.display_name or \"AI\"\n        return Message(\n            sender=MESSAGE_SENDER_AI,\n            sender_name=sender_name,\n            properties={\"icon\": \"Bot\", \"state\": \"partial\"},\n            # `text=\"\"` sentinel so MessageTable's no_content check accepts\n            # an in-flight agent message whose content_blocks haven't been\n            # populated yet. Mirrors ChatInput's convention.\n            text=\"\",\n            # Flat chronological event log; see lfx.base.agents.events.\n            content_blocks=[],\n            session_id=session_id or uuid.uuid4(),\n        )\n\n    def _selected_model_remediation_context(self) -> tuple[str | None, str | None, Any | None]:\n        \"\"\"Return provider/name plus a connected model target, when present.\"\"\"\n        try:\n            selected = self._resolve_selected_model()\n            if isinstance(selected, list) and selected and isinstance(selected[0], dict):\n                return selected[0].get(\"provider\"), selected[0].get(\"name\"), None\n\n            from langchain_core.language_models import BaseLanguageModel\n\n            if isinstance(selected, BaseLanguageModel):\n                model_name = None\n                for attr in (\"model_name\", \"model\", \"model_id\"):\n                    value = getattr(selected, attr, None)\n                    if isinstance(value, str) and value:\n                        model_name = value\n                        break\n                return self._connected_model_provider(selected), model_name, selected\n        except (AttributeError, TypeError, ValueError, KeyError, ImportError):\n            pass\n        return None, None, None\n\n    def _connected_model_provider(self, model: Any) -> str | None:\n        \"\"\"Resolve a connected model's provider from the source component.\n\n        Runtime model classes are not provider identities: OpenAI, OpenRouter,\n        and compatible endpoints can all produce ``ChatOpenAI``. The incoming\n        model edge preserves the source component, so prefer its explicit\n        provider override or selected-model metadata, then its provider display\n        name. If the Agent is used without graph provenance, leave the provider\n        unknown so provider-scoped remediations remain disabled.\n        \"\"\"\n        vertex = getattr(self, \"_vertex\", None)\n        if vertex is None:\n            return None\n        source_id = vertex.get_incoming_edge_by_target_param(\"model\")\n        if not source_id:\n            return None\n        source = vertex.graph.get_vertex(source_id)\n        component = getattr(source, \"custom_component\", None)\n        if component is None:\n            return None\n\n        candidate = getattr(component, \"provider\", None)\n\n        if not isinstance(candidate, str) or not candidate:\n            selected_model = getattr(component, \"model\", None)\n            if isinstance(selected_model, list) and selected_model and isinstance(selected_model[0], dict):\n                candidate = selected_model[0].get(\"provider\")\n\n        if not isinstance(candidate, str) or not candidate:\n            candidate = getattr(component, \"display_name\", None)\n        if not isinstance(candidate, str) or not candidate:\n            return None\n\n        from lfx.base.models.unified_models.provider_queries import get_model_provider_metadata\n\n        provider_metadata = get_model_provider_metadata().get(candidate, {})\n        expected_class = provider_metadata.get(\"mapping\", {}).get(\"model_class\")\n        model_classes = {base.__name__ for base in type(model).__mro__}\n        return candidate if expected_class in model_classes else None\n\n    async def _run_agent_with_model_remediation(\n        self,\n        run_once: Callable[[], Awaitable[Message]],\n    ) -> Message:\n        \"\"\"Run one Agent operation, retrying safe provider-validation failures.\n\n        Registered remediations must identify request-validation failures that\n        occur before tool execution. A failure that can happen after a tool runs\n        must instead retry at the model-call boundary to avoid repeating tool\n        side effects.\n        \"\"\"\n        from lfx.base.models.model_remediation import apply_overrides_to_model, find_remediation, remember\n\n        provider, model_name, connected_model = self._selected_model_remediation_context()\n        applied: set[str] = set()\n        while True:\n            try:\n                result = await run_once()\n            except (ValueError, TypeError, KeyError):\n                await logger.aexception(\"Agent run failed\")\n                raise\n            except Exception as exc:\n                # run_agent may wrap the provider error in ExceptionWithMessageError, whose\n                # str() carries the model's partial completion. Matching on it locally is fine;\n                # logging it is not, so the record below passes the exception instead.\n                error_text = f\"{exc} {getattr(exc, '__cause__', '') or ''}\"\n                remediation = find_remediation(error_text, provider, already_applied=applied)\n                if remediation is None:\n                    await logger.aexception(\"Agent run failed with no remediation available\")\n                    raise\n                if connected_model is not None and not apply_overrides_to_model(connected_model, remediation.overrides):\n                    await logger.aerror(\n                        f\"model.remediation.unapplied name={remediation.name} provider={provider} model={model_name}\"\n                    )\n                    raise\n                applied.add(remediation.name)\n                if connected_model is None:\n                    self._model_overrides = {\n                        **(getattr(self, \"_model_overrides\", None) or {}),\n                        **remediation.overrides,\n                    }\n                else:\n                    # Connected outputs are shared objects across downstream flow\n                    # branches. The mutation is intentionally flow-scoped: sibling\n                    # branches holding this model will see the matched override too.\n                    await logger.adebug(\n                        f\"model.remediation.shared_model name={remediation.name} provider={provider} model={model_name}\"\n                    )\n                await logger.awarning(\n                    f\"model.remediation.applied name={remediation.name} provider={provider} model={model_name}\"\n                )\n            else:\n                if connected_model is None and getattr(self, \"_model_overrides\", None):\n                    remember(provider, model_name, self._model_overrides)\n                return result\n\n    async def message_response(self) -> Message:\n        async def _run_once() -> Message:\n            llm_model, self.chat_history, self.tools = await self.get_agent_requirements()\n            self.set(\n                llm=llm_model,\n                tools=self.tools or [],\n                chat_history=self.chat_history,\n                input_value=self.input_value,\n                system_prompt=self._inject_dynamic_prompt_values(self.system_prompt),\n            )\n            agent = self.create_agent_runnable()\n            return await self.run_agent(agent)\n\n        result = await self._run_agent_with_model_remediation(_run_once)\n        self._agent_result = result\n        return result\n\n    async def json_response(self) -> Data:\n        \"\"\"Produce structured Data via native LLM structured output, with prompt-based fallback.\n\n        Native path (no tools, llm has with_structured_output) bypasses the agent loop and\n        returns provider-validated JSON. When tools are attached, falls back to running the\n        agent with a schema-augmented system prompt and parsing the final message content.\n        \"\"\"\n        from lfx.components.models_and_agents.structured_output.structured_output_orchestrator import (\n            orchestrate_structured_output,\n        )\n\n        try:\n            llm_model, self.chat_history, self.tools = await self.get_agent_requirements()\n        except (ValueError, TypeError) as exc:\n            await logger.aexception(\"json_response.requirements_failed\")\n            return Data(data={\"content\": \"\", \"error\": str(exc)})\n\n        injected_system_prompt = self._inject_dynamic_prompt_values(getattr(self, \"system_prompt\", \"\") or \"\") or \"\"\n        format_instructions = getattr(self, \"format_instructions\", \"\") or \"\"\n        output_schema = getattr(self, \"output_schema\", None) or []\n        has_tools = bool(self.tools)\n\n        async def _run_agent_for_fallback(augmented_prompt: str) -> str:\n            first_attempt = True\n\n            async def _run_once() -> Message:\n                nonlocal first_attempt, llm_model\n                if first_attempt:\n                    first_attempt = False\n                else:\n                    llm_model, self.chat_history, self.tools = await self.get_agent_requirements()\n                self.set(\n                    llm=llm_model,\n                    tools=self.tools or [],\n                    chat_history=self.chat_history,\n                    input_value=self.input_value,\n                    system_prompt=augmented_prompt,\n                )\n                # Structured output cannot suspend mid-parse: disable tool-approval interrupts.\n                agent_runnable = self.create_agent_runnable(allow_interrupts=False)\n                return await self.run_agent(agent_runnable)\n\n            with _suppress_send_message(self):\n                result = await self._run_agent_with_model_remediation(_run_once)\n            return _extract_text_content(result)\n\n        try:\n            return await orchestrate_structured_output(\n                llm=llm_model,\n                output_schema=output_schema,\n                system_prompt=injected_system_prompt,\n                format_instructions=format_instructions,\n                input_value=_extract_text_content(self.input_value),\n                run_prompt_fallback=_run_agent_for_fallback,\n                prefer_native=not has_tools,\n            )\n        except (\n            ExceptionWithMessageError,\n            ValueError,\n            TypeError,\n            NotImplementedError,\n            AttributeError,\n        ) as exc:\n            await logger.aexception(\"json_response.orchestration_failed\")\n            return Data(data={\"content\": \"\", \"error\": str(exc)})\n\n    async def get_memory_data(self):\n        # Scope by flow_id so default playground session names (e.g. \"New Session 0\")\n        # cannot leak chat history across unrelated flows. See issue #13059.\n        # The helper also returns [] when n_messages == 0, preserving the\n        # explicit \"memory disabled\" contract from MemoryComponent.retrieve_messages.\n        messages = await aget_agent_chat_history(\n            session_id=self.graph.session_id,\n            flow_id=getattr(self.graph, \"flow_id\", None),\n            context_id=self.context_id,\n            n_messages=self.n_messages,\n            user_id=_safe_graph_user_id(self),\n        )\n        return [\n            message for message in messages if getattr(message, \"id\", None) != getattr(self.input_value, \"id\", None)\n        ]\n\n    def update_input_types(self, build_config: dotdict) -> dotdict:\n        \"\"\"Update input types for all fields in build_config.\"\"\"\n        for key, value in build_config.items():\n            if isinstance(value, dict):\n                if value.get(\"input_types\") is None:\n                    build_config[key][\"input_types\"] = []\n            elif hasattr(value, \"input_types\") and value.input_types is None:\n                value.input_types = []\n        return build_config\n\n    async def update_build_config(\n        self,\n        build_config: dotdict,\n        field_value: list[dict],\n        field_name: str | None = None,\n    ) -> dotdict:\n        from lfx.services.model_provider_policy import ModelProviderPolicyPurpose\n\n        policy_parameters = dict(getattr(self, \"_parameters\", {}) or {})\n        if field_name:\n            policy_parameters[field_name] = field_value\n        await self.arequire_model_provider_policy(\n            ModelProviderPolicyPurpose.CONFIGURE,\n            parameters=policy_parameters,\n        )\n        # Update model options with caching (for all field changes).\n        # The tool-calling constraint lives on the ModelInput's ``filters``\n        # field (declared above); ``handle_model_input_update`` reads it\n        # and applies the filter to both the dropdown options and the\n        # sticky-default re-injection path.\n        build_config = handle_model_input_update(\n            component=self,\n            build_config=dict(build_config),\n            field_value=field_value,\n            field_name=field_name,\n        )\n        build_config = dotdict(build_config)\n\n        if field_name == \"model\":\n            build_config = self.update_input_types(build_config)\n\n            # Validate required keys. `verbose` was dropped from the input set\n            # (see `_agent_base_inputs` \u2014 the create_agent event stream already\n            # surfaces every step), so it is intentionally NOT required here.\n            # Saved flows that still carry a `verbose` value just ignore it on\n            # load.\n            default_keys = [\n                \"code\",\n                \"_type\",\n                \"model\",\n                \"tools\",\n                \"input_value\",\n                \"add_current_date_tool\",\n                \"add_calculator_tool\",\n                \"system_prompt\",\n                \"max_iterations\",\n                \"handle_parsing_errors\",\n            ]\n            missing_keys = [key for key in default_keys if key not in build_config]\n            if missing_keys:\n                msg = f\"Missing required keys in build_config: {missing_keys}\"\n                raise ValueError(msg)\n        await self._filter_legacy_provider_options(build_config)\n        return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n    async def _get_tools(self) -> list[Tool]:\n        component_toolkit = get_component_toolkit()\n\n        tools = component_toolkit(component=self).get_tools(\n            tool_name=\"Call_Agent\",\n            # here we do not use the shared callbacks as we are exposing the agent as a tool\n            callbacks=self.get_langchain_callbacks(),\n        )\n        if hasattr(self, \"tools_metadata\"):\n            tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n\n        return tools\n"
       },
       "context_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Context ID",
        "dynamic": false,
        "info": "The context ID of the chat. Adds an extra layer to the local memory.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "context_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "format_instructions": {
        "_input_type": "MultilineInput",
        "advanced": true,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "Output Format Instructions",
        "dynamic": false,
        "info": "Generic Template for structured output formatting. Valid only with Structured response.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "format_instructions",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are an AI that extracts structured JSON objects from unstructured text. Use a predefined schema with expected types (str, int, float, bool, dict). Extract ALL relevant instances that match the schema - if multiple patterns exist, capture them all. Fill missing or ambiguous values with defaults: null for missing values. Remove exact duplicates but keep variations that have different field values. Always return valid JSON in the expected format, never throw errors. If multiple objects can be extracted, return them all in the structured format."
       },
       "handle_parsing_errors": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Handle Parse Errors",
        "dynamic": false,
        "info": "Adds tool-execution retry as a safety net. `create_agent` already feeds tool-call validation errors back to the LLM automatically; this flag layers `ToolRetryMiddleware` on top so transient tool runtime failures are retried (max 2 retries).",
        "list": false,
        "list_add_label": "Add More",
        "name": "handle_parsing_errors",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input provided by the user for the agent to process.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_iterations": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Iterations",
        "dynamic": false,
        "info": "Maximum number of model calls the agent can make before stopping (maps to `ModelCallLimitMiddleware.run_limit` on the create_agent path). Must be at least 1 \u2014 it is a safety cap, never 'unlimited'.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_iterations",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 1.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 15
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "filters": {
         "tool_calling": true
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "n_messages": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Number of Chat History Messages",
        "dynamic": false,
        "info": "Number of chat history messages to retrieve.",
        "list": false,
        "list_add_label": "Add More",
        "name": "n_messages",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 100
       },
       "output_schema": {
        "_input_type": "TableInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Output Schema",
        "dynamic": false,
        "info": "Schema Validation: Define the structure and data types for structured output. No validation if no output schema.",
        "input_types": [
         "DataFrame",
         "Table"
        ],
        "is_list": true,
        "list_add_label": "Add More",
        "name": "output_schema",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "table_icon": "Table",
        "table_schema": [
         {
          "default": "field",
          "description": "Specify the name of the output field.",
          "display_name": "Name",
          "edit_mode": "inline",
          "name": "name",
          "type": "str"
         },
         {
          "default": "description of field",
          "description": "Describe the purpose of the output field.",
          "display_name": "Description",
          "edit_mode": "popover",
          "name": "description",
          "type": "str"
         },
         {
          "default": "str",
          "description": "Indicate the data type of the output field (e.g., str, int, float, bool, dict).",
          "display_name": "Type",
          "edit_mode": "inline",
          "name": "type",
          "options": [
           "str",
           "int",
           "float",
           "bool",
           "dict"
          ],
          "type": "str"
         },
         {
          "default": "False",
          "description": "Set to True if this output field should be a list of the specified type.",
          "display_name": "As List",
          "edit_mode": "inline",
          "name": "multiple",
          "type": "boolean"
         }
        ],
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "trigger_icon": "Table",
        "trigger_text": "Open table",
        "type": "table",
        "value": []
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": 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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "system_prompt": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "Agent Instructions",
        "dynamic": false,
        "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior. Supports dynamic placeholders: {current_date}, {model_name}, {optional_user_context}.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_prompt",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a research analyst gathering evidence for an industry and company report.\nYou have one tool: Web Research. Use it at most 6 times in total, so\ncombine related evidence items into a single request.\nYou will receive the research plan from the scoping stage.\nFor every item under five_forces_evidence_needed and internal_evidence_needed,\nsearch the web for supporting evidence. If nothing useful turns up, mark it\nOPEN. Do not fabricate a fact to fill the gap.\nTag every fact with the source URL you found it at. Do not draft prose\nparagraphs; this stage extracts and organises, it does not write.\nOutput strictly as JSON:\n{\n\"strategic_question\": \"<copied from the research plan>\",\n\"five_forces_findings\": {\n\"threat_of_new_entrants\": [{\"fact\": \"...\", \"source\": \"...\"}],\n\"supplier_power\": [...],\n\"buyer_power\": [...],\n\"threat_of_substitutes\": [...],\n\"competitive_rivalry\": [...]\n},\n\"internal_findings\": [{\"fact\": \"...\", \"source\": \"...\"}],\n\"open_gaps\": [\"<evidence items with nothing found>\"]\n}"
       },
       "tools": {
        "_input_type": "HandleInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Tools",
        "dynamic": false,
        "info": "These are the tools that the agent can use to help with tasks.",
        "input_types": [
         "Tool"
        ],
        "list": true,
        "list_add_label": "Add More",
        "name": "tools",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "other",
        "value": ""
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-b2706d",
    "type": "genericNode",
    "position": {
     "x": 1180,
     "y": 650
    },
    "data": {
     "id": "PromptComponent-b2706d",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Research findings:\n{research}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "research": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "research",
        "display_name": "research",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "3. Five Forces input",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "research"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-def4cd",
    "type": "genericNode",
    "position": {
     "x": 1380,
     "y": 300
    },
    "data": {
     "id": "LanguageModelComponent-def4cd",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "3. Five Forces",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are an industry structure analyst applying Porter's Five Forces.\nYou will receive five_forces_findings from the research stage.\nFor each of the five forces, assign a rating of High, Medium, or Low\npressure on the industry, and write a 2-3 sentence rationale that cites the\nspecific findings behind it. If a force has no findings (check open_gaps),\nsay so explicitly rather than guessing a rating.\nOutput strictly as JSON:\n{\n\"five_forces\": {\n\"threat_of_new_entrants\": {\"rating\": \"High|Medium|Low|Insufficient evidence\", \"rationale\": \"...\"},\n\"supplier_power\": {...},\n\"buyer_power\": {...},\n\"threat_of_substitutes\": {...},\n\"competitive_rivalry\": {...}\n},\n\"overall_industry_attractiveness\": \"<one sentence synthesising the five ratings>\"\n}"
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-68f723",
    "type": "genericNode",
    "position": {
     "x": 1840,
     "y": 650
    },
    "data": {
     "id": "PromptComponent-68f723",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Research findings:\n{research}\n\nFive Forces analysis:\n{five_forces}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "research": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "research",
        "display_name": "research",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "five_forces": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "five_forces",
        "display_name": "five_forces",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "4. SWOT inputs",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "research",
        "five_forces"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-96d726",
    "type": "genericNode",
    "position": {
     "x": 2300,
     "y": 650
    },
    "data": {
     "id": "LanguageModelComponent-96d726",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "4. SWOT",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a company analyst building a SWOT.\nYou will receive internal_findings from the research stage and the\nfive_forces output from the previous stage.\nBuild Strengths and Weaknesses from internal_findings only. Build\nOpportunities and Threats primarily from the five_forces ratings and\nrationale (a Low-pressure force is a source of opportunity, a High-pressure\nforce is a source of threat), supplemented by any external items in\ninternal_findings.\nEvery SWOT item must cite what it's drawn from: a specific finding or a\nspecific force. Do not add items with no traceable basis.\nOutput strictly as JSON:\n{\n\"strengths\": [{\"item\": \"...\", \"source\": \"...\"}],\n\"weaknesses\": [{\"item\": \"...\", \"source\": \"...\"}],\n\"opportunities\": [{\"item\": \"...\", \"source\": \"...\"}],\n\"threats\": [{\"item\": \"...\", \"source\": \"...\"}]\n}"
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-60e59a",
    "type": "genericNode",
    "position": {
     "x": 2760,
     "y": 300
    },
    "data": {
     "id": "PromptComponent-60e59a",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Strategic question and research:\n{research}\n\nSWOT:\n{swot}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "research": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "research",
        "display_name": "research",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "swot": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "swot",
        "display_name": "swot",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "5. TOWS inputs",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "research",
        "swot"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-c3d9df",
    "type": "genericNode",
    "position": {
     "x": 3220,
     "y": 300
    },
    "data": {
     "id": "LanguageModelComponent-c3d9df",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "5. TOWS",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a strategy analyst building a TOWS confrontation matrix from a SWOT.\nFor each pairing, generate 1-2 concrete strategic options, each naming\nexactly which SWOT items it combines:\n- SO (Strength-Opportunity): use a strength to capture an opportunity.\n- WO (Weakness-Opportunity): overcome a weakness by using an opportunity.\n- ST (Strength-Threat): use a strength to defend against a threat.\n- WT (Weakness-Threat): defensive options that minimise weakness and avoid threat.\nThen, using the strategic_question provided, rank the top three options\nacross all four quadrants by relevance to that specific question, with one\nsentence on why each is relevant.\nOutput strictly as JSON:\n{\n\"tows\": {\"SO\": [...], \"WO\": [...], \"ST\": [...], \"WT\": [...]},\n\"top_options_for_question\": [\n{\"option\": \"...\", \"quadrant\": \"SO|WO|ST|WT\", \"why_relevant\": \"...\"}\n]\n}"
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-a5d4ec",
    "type": "genericNode",
    "position": {
     "x": 3680,
     "y": 650
    },
    "data": {
     "id": "PromptComponent-a5d4ec",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Strategic question and research:\n{research}\n\nFive Forces:\n{five_forces}\n\nSWOT:\n{swot}\n\nTOWS:\n{tows}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "research": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "research",
        "display_name": "research",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "five_forces": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "five_forces",
        "display_name": "five_forces",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "swot": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "swot",
        "display_name": "swot",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "tows": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "tows",
        "display_name": "tows",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "6. Drafting inputs",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "research",
        "five_forces",
        "swot",
        "tows"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-390257",
    "type": "genericNode",
    "position": {
     "x": 4140,
     "y": 650
    },
    "data": {
     "id": "LanguageModelComponent-390257",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "6. Drafting",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are an industry analyst writing the first draft of a client-facing report.\nYou will receive five_forces, swot, tows, and the original strategic_question.\nUse only these as your source of fact; do not add outside knowledge.\nStructure:\n- Executive Summary (150-200 words) that states the strategic question and\n  previews the recommendation.\n- Industry Structure: prose synthesis of the five_forces output, followed by\n  a table with columns Force | Rating | Key evidence.\n- Company Position: prose synthesis of the swot output.\n- Strategic Options: prose synthesis of the tows output, built around\n  top_options_for_question.\n- Recommendation: a direct answer to the strategic question, citing which\n  strategic option(s) it rests on.\n- Sources: a bulleted list of every source URL used.\nEvery claim must trace to the inputs provided. Mark anything without a\nclear source [UNSOURCED] rather than inventing support.\nTone: professional analyst report, plain language, no marketing language,\nno unearned certainty.\nFormat the report in Markdown: first line \"# <Company>: Industry and\nStrategic Analysis\", then \"## \" for each section heading. Do not output JSON."
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-b31840",
    "type": "genericNode",
    "position": {
     "x": 4400,
     "y": 0
    },
    "data": {
     "id": "PromptComponent-b31840",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Draft report to review:\n{draft}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "draft": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "draft",
        "display_name": "draft",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "7. Critique input",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "draft"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-dbf74f",
    "type": "genericNode",
    "position": {
     "x": 4600,
     "y": 300
    },
    "data": {
     "id": "LanguageModelComponent-dbf74f",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "7. Critique",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a rigorous internal reviewer. You do not write or rewrite report\ncontent; you only evaluate it.\nYou will receive the draft report.\nScore the draft against this rubric:\n1. Framework correctness: is each factor correctly classified (a Five\n   Forces item isn't presented as a SWOT item and vice versa)?\n2. Traceability: is every claim traceable to a prior-stage output, or\n   marked [UNSOURCED]?\n3. No invented certainty: flag any [UNSOURCED] claim stated as fact.\n4. Answers the question: does the Recommendation actually answer the\n   strategic question, using the top-ranked TOWS options?\n5. Clarity: flag unclear, repetitive, or overlong sentences.\nOutput strictly as JSON:\n{\n\"verdict\": \"PASS\" or \"REVISE\",\n\"fixes\": [\n{\"section\": \"<section name>\", \"issue\": \"<what is wrong>\", \"fix\": \"<specific instruction to correct it>\"}\n]\n}\nReturn \"PASS\" only if there are zero fixes required."
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "ConditionalRouter-ae00e4",
    "type": "genericNode",
    "position": {
     "x": 5060,
     "y": 650
    },
    "data": {
     "id": "ConditionalRouter-ae00e4",
     "type": "ConditionalRouter",
     "node": {
      "base_classes": [
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Routes an input message to a corresponding output based on text comparison.",
      "display_name": "PASS or REVISE?",
      "documentation": "https://docs.langflow.org/if-else",
      "edited": false,
      "field_order": [
       "input_text",
       "operator",
       "match_text",
       "case_sensitive",
       "true_case_message",
       "false_case_message",
       "max_iterations",
       "default_route"
      ],
      "frozen": false,
      "icon": "split",
      "legacy": false,
      "metadata": {
       "code_hash": "1d14fd882a0a",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.flow_controls.conditional_router.ConditionalRouterComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "True",
        "group_outputs": true,
        "method": "true_response",
        "name": "true_result",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "False",
        "group_outputs": true,
        "method": "false_response",
        "name": "false_result",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "case_sensitive": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Case Sensitive",
        "dynamic": false,
        "info": "If true, the comparison will be case sensitive.",
        "list": false,
        "list_add_label": "Add More",
        "name": "case_sensitive",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "import re\n\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import BoolInput, DropdownInput, IntInput, MessageInput, MessageTextInput, Output\nfrom lfx.schema.message import Message\n\n\nclass ConditionalRouterComponent(Component):\n    display_name = \"If-Else\"\n    description = \"Routes an input message to a corresponding output based on text comparison.\"\n    documentation: str = \"https://docs.langflow.org/if-else\"\n    icon = \"split\"\n    name = \"ConditionalRouter\"\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.__iteration_updated = False\n\n    inputs = [\n        MessageTextInput(\n            name=\"input_text\",\n            display_name=\"Text Input\",\n            info=\"The primary text input for the operation.\",\n            required=True,\n        ),\n        DropdownInput(\n            name=\"operator\",\n            display_name=\"Operator\",\n            options=[\n                \"equals\",\n                \"not equals\",\n                \"contains\",\n                \"starts with\",\n                \"ends with\",\n                \"regex\",\n                \"less than\",\n                \"less than or equal\",\n                \"greater than\",\n                \"greater than or equal\",\n            ],\n            info=\"The operator to apply for comparing the texts.\",\n            value=\"equals\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"match_text\",\n            display_name=\"Match Text\",\n            info=\"The text input to compare against.\",\n            required=True,\n        ),\n        BoolInput(\n            name=\"case_sensitive\",\n            display_name=\"Case Sensitive\",\n            info=\"If true, the comparison will be case sensitive.\",\n            value=True,\n            advanced=True,\n        ),\n        MessageInput(\n            name=\"true_case_message\",\n            display_name=\"Case True\",\n            info=\"The message to pass if the condition is True.\",\n            advanced=True,\n        ),\n        MessageInput(\n            name=\"false_case_message\",\n            display_name=\"Case False\",\n            info=\"The message to pass if the condition is False.\",\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_iterations\",\n            display_name=\"Max Iterations\",\n            info=\"The maximum number of iterations for the conditional router.\",\n            value=10,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"default_route\",\n            display_name=\"Default Route\",\n            options=[\"true_result\", \"false_result\"],\n            info=\"The default route to take when max iterations are reached.\",\n            value=\"false_result\",\n            advanced=True,\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"True\", name=\"true_result\", method=\"true_response\", group_outputs=True),\n        Output(display_name=\"False\", name=\"false_result\", method=\"false_response\", group_outputs=True),\n    ]\n\n    def _pre_run_setup(self):\n        self.__iteration_updated = False\n\n    def evaluate_condition(self, input_text: str, match_text: str, operator: str, *, case_sensitive: bool) -> bool:\n        if not case_sensitive and operator != \"regex\":\n            input_text = input_text.lower()\n            match_text = match_text.lower()\n\n        if operator == \"equals\":\n            return input_text == match_text\n        if operator == \"not equals\":\n            return input_text != match_text\n        if operator == \"contains\":\n            return match_text in input_text\n        if operator == \"starts with\":\n            return input_text.startswith(match_text)\n        if operator == \"ends with\":\n            return input_text.endswith(match_text)\n        if operator == \"regex\":\n            try:\n                return bool(re.match(match_text, input_text))\n            except re.error:\n                return False  # Return False if the regex is invalid\n        if operator in [\"less than\", \"less than or equal\", \"greater than\", \"greater than or equal\"]:\n            try:\n                input_num = float(input_text)\n                match_num = float(match_text)\n                if operator == \"less than\":\n                    return input_num < match_num\n                if operator == \"less than or equal\":\n                    return input_num <= match_num\n                if operator == \"greater than\":\n                    return input_num > match_num\n                if operator == \"greater than or equal\":\n                    return input_num >= match_num\n            except ValueError:\n                return False  # Invalid number format for comparison\n        return False\n\n    def iterate_and_stop_once(self, route_to_stop: str):\n        \"\"\"Handles cycle iteration counting and branch exclusion.\n\n        Uses two complementary mechanisms:\n        1. stop() - ACTIVE/INACTIVE state for cycle management (gets reset each iteration)\n        2. exclude_branch_conditionally() - Persistent exclusion for conditional routing\n\n        When max_iterations is reached, breaks the cycle by allowing the default_route to execute.\n        \"\"\"\n        if not self.__iteration_updated:\n            self.update_ctx({f\"{self._id}_iteration\": self.ctx.get(f\"{self._id}_iteration\", 0) + 1})\n            self.__iteration_updated = True\n            current_iteration = self.ctx.get(f\"{self._id}_iteration\", 0)\n\n            # Check if max iterations reached and we're trying to stop the default route\n            if current_iteration >= self.max_iterations and route_to_stop == self.default_route:\n                # Clear ALL conditional exclusions to allow default route to execute\n                if self._id in self.graph.conditional_exclusion_sources:\n                    previous_exclusions = self.graph.conditional_exclusion_sources[self._id]\n                    self.graph.conditionally_excluded_vertices -= previous_exclusions\n                    del self.graph.conditional_exclusion_sources[self._id]\n\n                # Switch which route to stop - stop the NON-default route to break the cycle\n                route_to_stop = \"true_result\" if route_to_stop == \"false_result\" else \"false_result\"\n\n                # Call stop to break the cycle\n                self.stop(route_to_stop)\n                # Don't apply conditional exclusion when breaking cycle\n                return\n\n            # Normal case: Use BOTH mechanisms\n            # 1. stop() for cycle management (marks INACTIVE, updates run manager, gets reset)\n            self.stop(route_to_stop)\n\n            # 2. Conditional exclusion for persistent routing (doesn't get reset except by this router)\n            self.graph.exclude_branch_conditionally(self._id, output_name=route_to_stop)\n\n    def _resolve_case_message(self, case_message: Message | str | None) -> Message:\n        \"\"\"Return the override case message, falling back to the input text when it is blank.\n\n        ``Case True`` and ``Case False`` are optional overrides. When left blank the\n        ``MessageInput`` resolves to an empty ``Message``, so the original ``Text Input``\n        is routed through instead of an empty message.\n\n        A ``Message`` is only treated as blank when it carries no payload at all. An\n        override with empty text but meaningful content (files or content blocks) is\n        preserved as-is so that payload is not dropped.\n\n        Blankness is judged by payload only (``text``, ``files``, ``content_blocks``).\n        A ``Message`` carrying just metadata (``sender``, ``properties``, ``error`` ...)\n        and no payload is intentionally treated as blank: every empty ``Message`` also\n        defaults ``timestamp`` / ``category`` / ``properties``, so keying on metadata\n        would defeat the blank-field fallback. With no payload there is nothing to\n        route, so the ``Text Input`` is used.\n\n        ``case_message`` is typed defensively as ``Message | str | None``. ``MessageInput``\n        wraps user input into a ``Message`` on the validated graph path, but a raw ``str``\n        can still arrive via direct/programmatic assignment (``.set(...)`` stores the raw\n        value), so the ``str`` branch is retained rather than dropping such an override.\n        \"\"\"\n        if isinstance(case_message, Message):\n            if case_message.text or case_message.files or case_message.content_blocks:\n                return case_message\n            return Message(text=self.input_text)\n        if isinstance(case_message, str) and case_message:\n            return Message(text=case_message)\n        return Message(text=self.input_text)\n\n    def true_response(self) -> Message:\n        result = self.evaluate_condition(\n            self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive\n        )\n\n        # Check if we should force output due to max_iterations on default route\n        current_iteration = self.ctx.get(f\"{self._id}_iteration\", 0)\n        force_output = current_iteration >= self.max_iterations and self.default_route == \"true_result\"\n\n        if result or force_output:\n            true_message = self._resolve_case_message(self.true_case_message)\n            self.status = true_message\n            if not force_output:  # Only stop the other branch if not forcing due to max iterations\n                self.iterate_and_stop_once(\"false_result\")\n            return true_message\n        self.iterate_and_stop_once(\"true_result\")\n        return Message(text=\"\")\n\n    def false_response(self) -> Message:\n        result = self.evaluate_condition(\n            self.input_text, self.match_text, self.operator, case_sensitive=self.case_sensitive\n        )\n\n        if not result:\n            false_message = self._resolve_case_message(self.false_case_message)\n            self.status = false_message\n            self.iterate_and_stop_once(\"true_result\")\n            return false_message\n\n        self.iterate_and_stop_once(\"false_result\")\n        return Message(text=\"\")\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:\n        if field_name == \"operator\":\n            if field_value == \"regex\":\n                build_config.pop(\"case_sensitive\", None)\n            elif \"case_sensitive\" not in build_config:\n                case_sensitive_input = next(\n                    (input_field for input_field in self.inputs if input_field.name == \"case_sensitive\"), None\n                )\n                if case_sensitive_input:\n                    build_config[\"case_sensitive\"] = case_sensitive_input.to_dict()\n        return build_config\n"
       },
       "default_route": {
        "_input_type": "DropdownInput",
        "advanced": true,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Default Route",
        "dynamic": false,
        "external_options": {},
        "info": "The default route to take when max iterations are reached.",
        "name": "default_route",
        "options": [
         "true_result",
         "false_result"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "false_result"
       },
       "false_case_message": {
        "_input_type": "MessageInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Case False",
        "dynamic": false,
        "info": "The message to pass if the condition is False.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "false_case_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "input_text": {
        "_input_type": "MessageTextInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Text Input",
        "dynamic": false,
        "info": "The primary text input for the operation.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_text",
        "override_skip": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "match_text": {
        "_input_type": "MessageTextInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Match Text",
        "dynamic": false,
        "info": "The text input to compare against.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "match_text",
        "override_skip": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "\"verdict\"\\s*:\\s*\"PASS\""
       },
       "max_iterations": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Iterations",
        "dynamic": false,
        "info": "The maximum number of iterations for the conditional router.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_iterations",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 10
       },
       "operator": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Operator",
        "dynamic": false,
        "external_options": {},
        "info": "The operator to apply for comparing the texts.",
        "name": "operator",
        "options": [
         "equals",
         "not equals",
         "contains",
         "starts with",
         "ends with",
         "regex",
         "less than",
         "less than or equal",
         "greater than",
         "greater than or equal"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "regex"
       },
       "true_case_message": {
        "_input_type": "MessageInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Case True",
        "dynamic": false,
        "info": "The message to pass if the condition is True.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "true_case_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "ExportReport-377b2d",
    "type": "genericNode",
    "position": {
     "x": 5520,
     "y": 300
    },
    "data": {
     "id": "ExportReport-377b2d",
     "type": "ext:reports:ExportReport@extra",
     "node": {
      "template": {
       "_type": "Component",
       "author": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "author",
        "value": "",
        "display_name": "Your name",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "code": {
        "type": "code",
        "required": true,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "\"\"\"Export Report: sends the final report to the portal service, which renders\nWord and/or PDF files and returns download links.\n\nConfigured by environment variables on the Langflow service (students can't\nsee or edit these):\n    PORTAL_URL             public base URL of the portal service\n    EXPORT_SHARED_SECRET   must match the portal's EXPORT_SHARED_SECRET\n\"\"\"\n\nimport json\nimport os\nimport re\n\nimport httpx\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import DropdownInput, MessageTextInput, Output\nfrom lfx.schema.message import Message\n\nFORMATS = {\n    \"Word + PDF\": [\"docx\", \"pdf\"],\n    \"Word (.docx)\": [\"docx\"],\n    \"PDF\": [\"pdf\"],\n}\n\n\ndef reviewer_notes(critique: str) -> str:\n    \"\"\"Turn a Critique agent's JSON verdict into a Markdown 'Reviewer notes' section.\"\"\"\n    critique = (critique or \"\").strip()\n    if not critique:\n        return \"\"\n    match = re.search(r\"\\{.*\\}\", critique, re.S)\n    try:\n        data = json.loads(match.group(0)) if match else None\n    except ValueError:\n        data = None\n    lines = [\"\", \"\", \"## Reviewer notes\", \"\"]\n    if not isinstance(data, dict):\n        lines += [\"The final review could not be read as a verdict. Its full text:\", \"\", critique]\n        return \"\\n\".join(lines)\n    verdict = str(data.get(\"verdict\", \"UNKNOWN\")).upper()\n    fixes = [f for f in data.get(\"fixes\") or [] if isinstance(f, dict)]\n    lines.append(f\"**Final review: {verdict}**\")\n    lines.append(\"\")\n    if fixes:\n        lines += [\"The automated reviewer flagged these issues that remain in this report:\", \"\"]\n        for f in fixes:\n            section, issue, fix = (str(f.get(k, \"\")).strip() for k in (\"section\", \"issue\", \"fix\"))\n            item = f\"- **{section or 'General'}:** {issue}\"\n            if fix:\n                item += f\" \u2014 *Suggested fix:* {fix}\"\n            lines.append(item)\n    else:\n        lines.append(\"The automated reviewer found no remaining issues.\")\n    return \"\\n\".join(lines)\n\n\nclass ExportReport(Component):\n    display_name = \"Export Report\"\n    description = \"Turns the final report (Markdown) into a downloadable Word and/or PDF file.\"\n    icon = \"file-down\"\n    name = \"ExportReport\"\n\n    inputs = [\n        MessageTextInput(\n            name=\"report\",\n            display_name=\"Report\",\n            info=\"Connect the final report here (Revision or Critique output).\",\n            required=True,\n        ),\n        MessageTextInput(\n            name=\"review\",\n            display_name=\"Reviewer notes\",\n            info=\"Optional: connect the final Critique output to add its verdict and outstanding fixes to the report.\",\n            value=\"\",\n        ),\n        MessageTextInput(\n            name=\"title\",\n            display_name=\"Report title\",\n            info=\"Leave blank to use the first heading in the report.\",\n            value=\"\",\n        ),\n        MessageTextInput(\n            name=\"author\",\n            display_name=\"Your name\",\n            value=\"\",\n        ),\n        DropdownInput(\n            name=\"file_format\",\n            display_name=\"Format\",\n            options=list(FORMATS),\n            value=\"Word + PDF\",\n        ),\n    ]\n\n    outputs = [\n        Output(name=\"links\", display_name=\"Download links\", method=\"export\"),\n    ]\n\n    def export(self) -> Message:\n        portal = os.environ.get(\"PORTAL_URL\", \"\").rstrip(\"/\")\n        secret = os.environ.get(\"EXPORT_SHARED_SECRET\", \"\")\n        if not portal or not secret:\n            raise ValueError(\"Export is not configured on this server. Ask your instructor.\")\n\n        report = self.report.text if isinstance(self.report, Message) else str(self.report)\n        review = self.review.text if isinstance(self.review, Message) else str(self.review or \"\")\n        report = report.rstrip() + reviewer_notes(review)\n        response = httpx.post(\n            f\"{portal}/api/render\",\n            headers={\"X-Export-Secret\": secret},\n            json={\n                \"markdown\": report,\n                \"title\": self.title or \"\",\n                \"author\": self.author or \"\",\n                \"formats\": FORMATS[self.file_format],\n            },\n            timeout=120,\n        )\n        response.raise_for_status()\n        files = response.json()[\"files\"]\n\n        lines = [\"**Your report is ready:**\", \"\"]\n        lines += [f\"- [{f['label']}]({f['url']})\" for f in files]\n        lines += [\"\", \"_Links expire after 7 days \u2014 download your copy now._\"]\n        text = \"\\n\".join(lines)\n        self.status = text\n        return Message(text=text)\n",
        "fileTypes": [],
        "file_path": "",
        "password": false,
        "name": "code",
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "info": "",
        "load_from_db": false,
        "title_case": false
       },
       "file_format": {
        "tool_mode": false,
        "trace_as_metadata": true,
        "options": [
         "Word + PDF",
         "Word (.docx)",
         "PDF"
        ],
        "options_metadata": [],
        "combobox": false,
        "dialog_inputs": {},
        "toggle": false,
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "file_format",
        "value": "Word + PDF",
        "display_name": "Format",
        "advanced": false,
        "api_editable": false,
        "dynamic": false,
        "info": "",
        "title_case": false,
        "track_in_telemetry": true,
        "external_options": {},
        "type": "str",
        "_input_type": "DropdownInput"
       },
       "report": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": true,
        "placeholder": "",
        "show": true,
        "name": "report",
        "value": "",
        "display_name": "Report",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Connect the final report here (Revision or Critique output).",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "review": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "review",
        "value": "",
        "display_name": "Reviewer notes",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Optional: connect the final Critique output to add its verdict and outstanding fixes to the report.",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "title": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "title",
        "value": "",
        "display_name": "Report title",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Leave blank to use the first heading in the report.",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       }
      },
      "description": "Turns the final report (Markdown) into a downloadable Word and/or PDF file.",
      "icon": "file-down",
      "base_classes": [
       "Message"
      ],
      "display_name": "Export (passed first time)",
      "documentation": "",
      "minimized": false,
      "custom_fields": {},
      "output_types": [],
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "links",
        "display_name": "Download links",
        "method": "export",
        "value": "__UNDEFINED__",
        "cache": true,
        "allows_loop": false,
        "group_outputs": false,
        "tool_mode": true
       }
      ],
      "field_order": [
       "report",
       "review",
       "title",
       "author",
       "file_format"
      ],
      "beta": false,
      "legacy": false,
      "edited": false,
      "metadata": {
       "module": "_lfx_ext.extra.reports.export_report",
       "code_hash": "4b8079df0dbb",
       "dependencies": {
        "total_dependencies": 2,
        "dependencies": [
         {
          "name": "httpx",
          "version": "0.28.1"
         },
         {
          "name": "lfx",
          "version": "1.12.2"
         }
        ]
       }
      },
      "tool_mode": false,
      "extension": "reports",
      "bundle": "reports",
      "extension_version": "0.0.0",
      "namespaced_id": "ext:reports:ExportReport@extra",
      "name": "ExportReport",
      "legacy_module": "export_report"
     },
     "showNode": true
    }
   },
   {
    "id": "ChatOutput-f0c919",
    "type": "genericNode",
    "position": {
     "x": 5980,
     "y": 300
    },
    "data": {
     "id": "ChatOutput-f0c919",
     "type": "ChatOutput",
     "node": {
      "base_classes": [
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Display a chat message in the Playground.",
      "display_name": "Download links",
      "documentation": "https://docs.langflow.org/chat-input-and-output",
      "edited": false,
      "field_order": [
       "input_value",
       "should_store_message",
       "sender",
       "sender_name",
       "session_id",
       "context_id",
       "data_template",
       "clean_data"
      ],
      "frozen": false,
      "icon": "MessagesSquare",
      "legacy": false,
      "metadata": {
       "code_hash": "84009527d08c",
       "dependencies": {
        "dependencies": [
         {
          "name": "orjson",
          "version": "3.11.9"
         },
         {
          "name": "fastapi",
          "version": "0.139.2"
         },
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 3
       },
       "module": "lfx.components.input_output.chat_output.ChatOutput"
      },
      "minimized": true,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Output Message",
        "group_outputs": false,
        "method": "message_response",
        "name": "message",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "clean_data": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Basic Clean Data",
        "dynamic": false,
        "info": "Whether to clean data before converting to string.",
        "list": false,
        "list_add_label": "Add More",
        "name": "clean_data",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom lfx.base.io.chat import ChatComponent\nfrom lfx.helpers.data import safe_convert\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\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.template.field.base import Output\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    documentation: str = \"https://docs.langflow.org/chat-input-and-output\"\n    icon = \"MessagesSquare\"\n    name = \"ChatOutput\"\n    minimized = True\n\n    inputs = [\n        HandleInput(\n            name=\"input_value\",\n            display_name=\"Inputs\",\n            info=\"Message to be passed as output.\",\n            input_types=[\"Data\", \"JSON\", \"DataFrame\", \"Table\", \"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=\"context_id\",\n            display_name=\"Context ID\",\n            info=\"The context ID of the chat. Adds an extra layer to the local memory.\",\n            value=\"\",\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        BoolInput(\n            name=\"clean_data\",\n            display_name=\"Basic Clean Data\",\n            value=True,\n            advanced=True,\n            info=\"Whether to clean data before converting to string.\",\n        ),\n    ]\n    outputs = [\n        Output(\n            display_name=\"Output Message\",\n            name=\"message\",\n            method=\"message_response\",\n        ),\n    ]\n\n    def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n        source_dict = {}\n        if id_:\n            source_dict[\"id\"] = id_\n        if display_name:\n            source_dict[\"display_name\"] = display_name\n        if source:\n            # Handle case where source is a ChatOpenAI object\n            if hasattr(source, \"model_name\"):\n                source_dict[\"source\"] = source.model_name\n            elif hasattr(source, \"model\"):\n                source_dict[\"source\"] = str(source.model)\n            else:\n                source_dict[\"source\"] = str(source)\n        return Source(**source_dict)\n\n    async def message_response(self) -> Message:\n        # First convert the input to string if needed\n        text = self.convert_to_string()\n\n        # Get source properties\n        source, _, display_name, source_id = self.get_properties_from_source_component()\n\n        # Create or use existing Message object\n        if isinstance(self.input_value, Message) and not self.is_connected_to_chat_input():\n            message = self.input_value\n            # Update message properties\n            message.text = text\n            # Preserve existing session_id from the incoming message if it exists\n            existing_session_id = message.session_id\n        else:\n            message = Message(text=text)\n            existing_session_id = None\n\n        # Set message properties\n        message.sender = self.sender\n        message.sender_name = self.sender_name\n        # Preserve session_id from incoming message, or use component/graph session_id\n        message.session_id = (\n            self.session_id or existing_session_id or (self.graph.session_id if hasattr(self, \"graph\") else None) or \"\"\n        )\n        message.context_id = self.context_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\n        # Store message if needed\n        if message.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        # Set accumulated token usage from all upstream LLM vertices.\n        # This must happen AFTER send_message() because streaming captures\n        # usage from chunks and would overwrite accumulated totals.\n        if hasattr(self, \"_vertex\") and self._vertex is not None:\n            accumulated_usage = self._vertex._accumulate_upstream_token_usage()  # noqa: SLF001\n            if accumulated_usage:\n                message.properties.usage = accumulated_usage\n                if self.should_store_message and message.get_id():\n                    message = await self._update_stored_message(message)\n                    await self._send_message_event(message, id_=message.get_id())\n\n        self.status = message\n        return message\n\n    def _serialize_data(self, data: Data) -> str:\n        \"\"\"Serialize Data object to JSON string.\"\"\"\n        # Convert data.data to JSON-serializable format\n        serializable_data = jsonable_encoder(data.data)\n        # Serialize with orjson, enabling pretty printing with indentation\n        json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n        # Convert bytes to string and wrap in Markdown code blocks\n        return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n    def _validate_input(self) -> None:\n        \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n        if self.input_value is None:\n            msg = \"Input data cannot be None\"\n            raise ValueError(msg)\n        if isinstance(self.input_value, list) and not all(\n            isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n        ):\n            invalid_types = [\n                type(item).__name__\n                for item in self.input_value\n                if not isinstance(item, Message | Data | DataFrame | str)\n            ]\n            msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n            raise TypeError(msg)\n        if not isinstance(\n            self.input_value,\n            Message | Data | DataFrame | str | list | Generator | type(None),\n        ):\n            type_name = type(self.input_value).__name__\n            msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n            raise TypeError(msg)\n\n    def convert_to_string(self) -> str | Generator[Any, None, None]:\n        \"\"\"Convert input data to string with proper error handling.\"\"\"\n        self._validate_input()\n        if isinstance(self.input_value, list):\n            clean_data: bool = getattr(self, \"clean_data\", False)\n            return \"\\n\".join([safe_convert(item, clean_data=clean_data) for item in self.input_value])\n        if isinstance(self.input_value, Generator):\n            return self.input_value\n        return safe_convert(self.input_value)\n"
       },
       "context_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Context ID",
        "dynamic": false,
        "info": "The context ID of the chat. Adds an extra layer to the local memory.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "context_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "data_template": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "{text}"
       },
       "input_value": {
        "_input_type": "HandleInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Inputs",
        "dynamic": false,
        "info": "Message to be passed as output.",
        "input_types": [
         "Data",
         "JSON",
         "DataFrame",
         "Table",
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "other",
        "value": ""
       },
       "sender": {
        "_input_type": "DropdownInput",
        "advanced": true,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Sender Type",
        "dynamic": false,
        "external_options": {},
        "info": "Type of sender.",
        "name": "sender",
        "options": [
         "Machine",
         "User"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "Machine"
       },
       "sender_name": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "AI"
       },
       "session_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "should_store_message": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Store Messages",
        "dynamic": false,
        "info": "Store the message in the history.",
        "list": false,
        "list_add_label": "Add More",
        "name": "should_store_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-2d545f",
    "type": "genericNode",
    "position": {
     "x": 5520,
     "y": 900
    },
    "data": {
     "id": "PromptComponent-2d545f",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Draft report:\n{draft}\n\nCritique fix list:\n{critique}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "draft": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "draft",
        "display_name": "draft",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       },
       "critique": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "critique",
        "display_name": "critique",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "8. Revision inputs",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "draft",
        "critique"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-5ceb31",
    "type": "genericNode",
    "position": {
     "x": 5980,
     "y": 900
    },
    "data": {
     "id": "LanguageModelComponent-5ceb31",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "8. Revision",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are the same analyst who wrote the draft report.\nYou will receive the original draft and a critique fix list.\nApply every fix exactly. Do not introduce any new factual claims that\nweren't already in the draft's underlying data. If a fix can't be applied\nwithout inventing information, mark that spot [UNSOURCED] instead.\nOutput the final report in Markdown, keeping the same structure: first line\n\"# <Company>: Industry and Strategic Analysis\", then \"## \" section headings,\nincluding the Industry Structure table and the Sources list."
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "ExportReport-7392ec",
    "type": "genericNode",
    "position": {
     "x": 6440,
     "y": 700
    },
    "data": {
     "id": "ExportReport-7392ec",
     "type": "ext:reports:ExportReport@extra",
     "node": {
      "template": {
       "_type": "Component",
       "author": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "author",
        "value": "",
        "display_name": "Your name",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "code": {
        "type": "code",
        "required": true,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "\"\"\"Export Report: sends the final report to the portal service, which renders\nWord and/or PDF files and returns download links.\n\nConfigured by environment variables on the Langflow service (students can't\nsee or edit these):\n    PORTAL_URL             public base URL of the portal service\n    EXPORT_SHARED_SECRET   must match the portal's EXPORT_SHARED_SECRET\n\"\"\"\n\nimport json\nimport os\nimport re\n\nimport httpx\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.io import DropdownInput, MessageTextInput, Output\nfrom lfx.schema.message import Message\n\nFORMATS = {\n    \"Word + PDF\": [\"docx\", \"pdf\"],\n    \"Word (.docx)\": [\"docx\"],\n    \"PDF\": [\"pdf\"],\n}\n\n\ndef reviewer_notes(critique: str) -> str:\n    \"\"\"Turn a Critique agent's JSON verdict into a Markdown 'Reviewer notes' section.\"\"\"\n    critique = (critique or \"\").strip()\n    if not critique:\n        return \"\"\n    match = re.search(r\"\\{.*\\}\", critique, re.S)\n    try:\n        data = json.loads(match.group(0)) if match else None\n    except ValueError:\n        data = None\n    lines = [\"\", \"\", \"## Reviewer notes\", \"\"]\n    if not isinstance(data, dict):\n        lines += [\"The final review could not be read as a verdict. Its full text:\", \"\", critique]\n        return \"\\n\".join(lines)\n    verdict = str(data.get(\"verdict\", \"UNKNOWN\")).upper()\n    fixes = [f for f in data.get(\"fixes\") or [] if isinstance(f, dict)]\n    lines.append(f\"**Final review: {verdict}**\")\n    lines.append(\"\")\n    if fixes:\n        lines += [\"The automated reviewer flagged these issues that remain in this report:\", \"\"]\n        for f in fixes:\n            section, issue, fix = (str(f.get(k, \"\")).strip() for k in (\"section\", \"issue\", \"fix\"))\n            item = f\"- **{section or 'General'}:** {issue}\"\n            if fix:\n                item += f\" \u2014 *Suggested fix:* {fix}\"\n            lines.append(item)\n    else:\n        lines.append(\"The automated reviewer found no remaining issues.\")\n    return \"\\n\".join(lines)\n\n\nclass ExportReport(Component):\n    display_name = \"Export Report\"\n    description = \"Turns the final report (Markdown) into a downloadable Word and/or PDF file.\"\n    icon = \"file-down\"\n    name = \"ExportReport\"\n\n    inputs = [\n        MessageTextInput(\n            name=\"report\",\n            display_name=\"Report\",\n            info=\"Connect the final report here (Revision or Critique output).\",\n            required=True,\n        ),\n        MessageTextInput(\n            name=\"review\",\n            display_name=\"Reviewer notes\",\n            info=\"Optional: connect the final Critique output to add its verdict and outstanding fixes to the report.\",\n            value=\"\",\n        ),\n        MessageTextInput(\n            name=\"title\",\n            display_name=\"Report title\",\n            info=\"Leave blank to use the first heading in the report.\",\n            value=\"\",\n        ),\n        MessageTextInput(\n            name=\"author\",\n            display_name=\"Your name\",\n            value=\"\",\n        ),\n        DropdownInput(\n            name=\"file_format\",\n            display_name=\"Format\",\n            options=list(FORMATS),\n            value=\"Word + PDF\",\n        ),\n    ]\n\n    outputs = [\n        Output(name=\"links\", display_name=\"Download links\", method=\"export\"),\n    ]\n\n    def export(self) -> Message:\n        portal = os.environ.get(\"PORTAL_URL\", \"\").rstrip(\"/\")\n        secret = os.environ.get(\"EXPORT_SHARED_SECRET\", \"\")\n        if not portal or not secret:\n            raise ValueError(\"Export is not configured on this server. Ask your instructor.\")\n\n        report = self.report.text if isinstance(self.report, Message) else str(self.report)\n        review = self.review.text if isinstance(self.review, Message) else str(self.review or \"\")\n        report = report.rstrip() + reviewer_notes(review)\n        response = httpx.post(\n            f\"{portal}/api/render\",\n            headers={\"X-Export-Secret\": secret},\n            json={\n                \"markdown\": report,\n                \"title\": self.title or \"\",\n                \"author\": self.author or \"\",\n                \"formats\": FORMATS[self.file_format],\n            },\n            timeout=120,\n        )\n        response.raise_for_status()\n        files = response.json()[\"files\"]\n\n        lines = [\"**Your report is ready:**\", \"\"]\n        lines += [f\"- [{f['label']}]({f['url']})\" for f in files]\n        lines += [\"\", \"_Links expire after 7 days \u2014 download your copy now._\"]\n        text = \"\\n\".join(lines)\n        self.status = text\n        return Message(text=text)\n",
        "fileTypes": [],
        "file_path": "",
        "password": false,
        "name": "code",
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "info": "",
        "load_from_db": false,
        "title_case": false
       },
       "file_format": {
        "tool_mode": false,
        "trace_as_metadata": true,
        "options": [
         "Word + PDF",
         "Word (.docx)",
         "PDF"
        ],
        "options_metadata": [],
        "combobox": false,
        "dialog_inputs": {},
        "toggle": false,
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "file_format",
        "value": "Word + PDF",
        "display_name": "Format",
        "advanced": false,
        "api_editable": false,
        "dynamic": false,
        "info": "",
        "title_case": false,
        "track_in_telemetry": true,
        "external_options": {},
        "type": "str",
        "_input_type": "DropdownInput"
       },
       "report": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": true,
        "placeholder": "",
        "show": true,
        "name": "report",
        "value": "",
        "display_name": "Report",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Connect the final report here (Revision or Critique output).",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "review": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "review",
        "value": "",
        "display_name": "Reviewer notes",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Optional: connect the final Critique output to add its verdict and outstanding fixes to the report.",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       },
       "title": {
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "load_from_db": false,
        "list": false,
        "list_add_label": "Add More",
        "override_skip": false,
        "required": false,
        "placeholder": "",
        "show": true,
        "name": "title",
        "value": "",
        "display_name": "Report title",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "Leave blank to use the first heading in the report.",
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "_input_type": "MessageTextInput"
       }
      },
      "description": "Turns the final report (Markdown) into a downloadable Word and/or PDF file.",
      "icon": "file-down",
      "base_classes": [
       "Message"
      ],
      "display_name": "Export (revised)",
      "documentation": "",
      "minimized": false,
      "custom_fields": {},
      "output_types": [],
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "links",
        "display_name": "Download links",
        "method": "export",
        "value": "__UNDEFINED__",
        "cache": true,
        "allows_loop": false,
        "group_outputs": false,
        "tool_mode": true
       }
      ],
      "field_order": [
       "report",
       "review",
       "title",
       "author",
       "file_format"
      ],
      "beta": false,
      "legacy": false,
      "edited": false,
      "metadata": {
       "module": "_lfx_ext.extra.reports.export_report",
       "code_hash": "4b8079df0dbb",
       "dependencies": {
        "total_dependencies": 2,
        "dependencies": [
         {
          "name": "httpx",
          "version": "0.28.1"
         },
         {
          "name": "lfx",
          "version": "1.12.2"
         }
        ]
       }
      },
      "tool_mode": false,
      "extension": "reports",
      "bundle": "reports",
      "extension_version": "0.0.0",
      "namespaced_id": "ext:reports:ExportReport@extra",
      "name": "ExportReport",
      "legacy_module": "export_report"
     },
     "showNode": true
    }
   },
   {
    "id": "ChatOutput-3978ba",
    "type": "genericNode",
    "position": {
     "x": 6900,
     "y": 700
    },
    "data": {
     "id": "ChatOutput-3978ba",
     "type": "ChatOutput",
     "node": {
      "base_classes": [
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Display a chat message in the Playground.",
      "display_name": "Download links (revised)",
      "documentation": "https://docs.langflow.org/chat-input-and-output",
      "edited": false,
      "field_order": [
       "input_value",
       "should_store_message",
       "sender",
       "sender_name",
       "session_id",
       "context_id",
       "data_template",
       "clean_data"
      ],
      "frozen": false,
      "icon": "MessagesSquare",
      "legacy": false,
      "metadata": {
       "code_hash": "84009527d08c",
       "dependencies": {
        "dependencies": [
         {
          "name": "orjson",
          "version": "3.11.9"
         },
         {
          "name": "fastapi",
          "version": "0.139.2"
         },
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 3
       },
       "module": "lfx.components.input_output.chat_output.ChatOutput"
      },
      "minimized": true,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Output Message",
        "group_outputs": false,
        "method": "message_response",
        "name": "message",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "clean_data": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Basic Clean Data",
        "dynamic": false,
        "info": "Whether to clean data before converting to string.",
        "list": false,
        "list_add_label": "Add More",
        "name": "clean_data",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom lfx.base.io.chat import ChatComponent\nfrom lfx.helpers.data import safe_convert\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\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.template.field.base import Output\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    documentation: str = \"https://docs.langflow.org/chat-input-and-output\"\n    icon = \"MessagesSquare\"\n    name = \"ChatOutput\"\n    minimized = True\n\n    inputs = [\n        HandleInput(\n            name=\"input_value\",\n            display_name=\"Inputs\",\n            info=\"Message to be passed as output.\",\n            input_types=[\"Data\", \"JSON\", \"DataFrame\", \"Table\", \"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=\"context_id\",\n            display_name=\"Context ID\",\n            info=\"The context ID of the chat. Adds an extra layer to the local memory.\",\n            value=\"\",\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        BoolInput(\n            name=\"clean_data\",\n            display_name=\"Basic Clean Data\",\n            value=True,\n            advanced=True,\n            info=\"Whether to clean data before converting to string.\",\n        ),\n    ]\n    outputs = [\n        Output(\n            display_name=\"Output Message\",\n            name=\"message\",\n            method=\"message_response\",\n        ),\n    ]\n\n    def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n        source_dict = {}\n        if id_:\n            source_dict[\"id\"] = id_\n        if display_name:\n            source_dict[\"display_name\"] = display_name\n        if source:\n            # Handle case where source is a ChatOpenAI object\n            if hasattr(source, \"model_name\"):\n                source_dict[\"source\"] = source.model_name\n            elif hasattr(source, \"model\"):\n                source_dict[\"source\"] = str(source.model)\n            else:\n                source_dict[\"source\"] = str(source)\n        return Source(**source_dict)\n\n    async def message_response(self) -> Message:\n        # First convert the input to string if needed\n        text = self.convert_to_string()\n\n        # Get source properties\n        source, _, display_name, source_id = self.get_properties_from_source_component()\n\n        # Create or use existing Message object\n        if isinstance(self.input_value, Message) and not self.is_connected_to_chat_input():\n            message = self.input_value\n            # Update message properties\n            message.text = text\n            # Preserve existing session_id from the incoming message if it exists\n            existing_session_id = message.session_id\n        else:\n            message = Message(text=text)\n            existing_session_id = None\n\n        # Set message properties\n        message.sender = self.sender\n        message.sender_name = self.sender_name\n        # Preserve session_id from incoming message, or use component/graph session_id\n        message.session_id = (\n            self.session_id or existing_session_id or (self.graph.session_id if hasattr(self, \"graph\") else None) or \"\"\n        )\n        message.context_id = self.context_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\n        # Store message if needed\n        if message.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        # Set accumulated token usage from all upstream LLM vertices.\n        # This must happen AFTER send_message() because streaming captures\n        # usage from chunks and would overwrite accumulated totals.\n        if hasattr(self, \"_vertex\") and self._vertex is not None:\n            accumulated_usage = self._vertex._accumulate_upstream_token_usage()  # noqa: SLF001\n            if accumulated_usage:\n                message.properties.usage = accumulated_usage\n                if self.should_store_message and message.get_id():\n                    message = await self._update_stored_message(message)\n                    await self._send_message_event(message, id_=message.get_id())\n\n        self.status = message\n        return message\n\n    def _serialize_data(self, data: Data) -> str:\n        \"\"\"Serialize Data object to JSON string.\"\"\"\n        # Convert data.data to JSON-serializable format\n        serializable_data = jsonable_encoder(data.data)\n        # Serialize with orjson, enabling pretty printing with indentation\n        json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n        # Convert bytes to string and wrap in Markdown code blocks\n        return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n    def _validate_input(self) -> None:\n        \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n        if self.input_value is None:\n            msg = \"Input data cannot be None\"\n            raise ValueError(msg)\n        if isinstance(self.input_value, list) and not all(\n            isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n        ):\n            invalid_types = [\n                type(item).__name__\n                for item in self.input_value\n                if not isinstance(item, Message | Data | DataFrame | str)\n            ]\n            msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n            raise TypeError(msg)\n        if not isinstance(\n            self.input_value,\n            Message | Data | DataFrame | str | list | Generator | type(None),\n        ):\n            type_name = type(self.input_value).__name__\n            msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n            raise TypeError(msg)\n\n    def convert_to_string(self) -> str | Generator[Any, None, None]:\n        \"\"\"Convert input data to string with proper error handling.\"\"\"\n        self._validate_input()\n        if isinstance(self.input_value, list):\n            clean_data: bool = getattr(self, \"clean_data\", False)\n            return \"\\n\".join([safe_convert(item, clean_data=clean_data) for item in self.input_value])\n        if isinstance(self.input_value, Generator):\n            return self.input_value\n        return safe_convert(self.input_value)\n"
       },
       "context_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Context ID",
        "dynamic": false,
        "info": "The context ID of the chat. Adds an extra layer to the local memory.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "context_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "data_template": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "{text}"
       },
       "input_value": {
        "_input_type": "HandleInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Inputs",
        "dynamic": false,
        "info": "Message to be passed as output.",
        "input_types": [
         "Data",
         "JSON",
         "DataFrame",
         "Table",
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "other",
        "value": ""
       },
       "sender": {
        "_input_type": "DropdownInput",
        "advanced": true,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Sender Type",
        "dynamic": false,
        "external_options": {},
        "info": "Type of sender.",
        "name": "sender",
        "options": [
         "Machine",
         "User"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "Machine"
       },
       "sender_name": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "AI"
       },
       "session_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "should_store_message": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Store Messages",
        "dynamic": false,
        "info": "Store the message in the history.",
        "list": false,
        "list_add_label": "Add More",
        "name": "should_store_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "PromptComponent-cbd27c",
    "type": "genericNode",
    "position": {
     "x": 6240,
     "y": 1450
    },
    "data": {
     "id": "PromptComponent-cbd27c",
     "type": "Prompt Template",
     "node": {
      "template": {
       "_type": "Component",
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from typing import Any\n\nfrom lfx.base.prompts.api_utils import process_prompt_template\nfrom lfx.custom.custom_component.component import Component\nfrom lfx.inputs.input_mixin import FieldTypes\nfrom lfx.inputs.inputs import DefaultPromptField\nfrom lfx.io import BoolInput, MessageTextInput, Output, PromptInput\nfrom lfx.log.logger import logger\nfrom lfx.schema.dotdict import dotdict\nfrom lfx.schema.message import Message\nfrom lfx.template.utils import update_template_values\nfrom lfx.utils.mustache_security import validate_mustache_template\n\n\nclass PromptComponent(Component):\n    display_name: str = \"Prompt Template\"\n    description: str = \"Create a prompt template with dynamic variables.\"\n    documentation: str = \"https://docs.langflow.org/components-prompts\"\n    icon = \"prompts\"\n    trace_type = \"prompt\"\n    name = \"Prompt Template\"\n\n    inputs = [\n        PromptInput(name=\"template\", display_name=\"Template\"),\n        BoolInput(\n            name=\"use_double_brackets\",\n            display_name=\"Use Double Brackets\",\n            value=False,\n            advanced=True,\n            info=\"Use {{variable}} syntax instead of {variable}.\",\n            real_time_refresh=True,\n        ),\n        MessageTextInput(\n            name=\"tool_placeholder\",\n            display_name=\"Tool Placeholder\",\n            tool_mode=True,\n            advanced=True,\n            show=False,\n            info=\"A placeholder input for tool mode.\",\n        ),\n    ]\n\n    outputs = [\n        Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n    ]\n\n    def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n        \"\"\"Update the template field type based on the selected mode.\"\"\"\n        if field_name == \"use_double_brackets\":\n            # Change the template field type based on mode\n            is_mustache = field_value is True\n            if is_mustache:\n                build_config[\"template\"][\"type\"] = FieldTypes.MUSTACHE_PROMPT.value\n            else:\n                build_config[\"template\"][\"type\"] = FieldTypes.PROMPT.value\n\n            # Re-process the template to update variables when mode changes\n            template_value = build_config.get(\"template\", {}).get(\"value\", \"\")\n            if template_value:\n                # Ensure custom_fields is properly initialized\n                if \"custom_fields\" not in build_config:\n                    build_config[\"custom_fields\"] = {}\n\n                # Clean up fields from the OLD mode before processing with NEW mode\n                # This ensures we don't keep fields with wrong syntax even if validation fails\n                old_custom_fields = build_config[\"custom_fields\"].get(\"template\", [])\n                for old_field in list(old_custom_fields):\n                    # Remove the field from custom_fields and template\n                    if old_field in old_custom_fields:\n                        old_custom_fields.remove(old_field)\n                    build_config.pop(old_field, None)\n\n                # Try to process template with new mode to add new variables\n                # If validation fails, at least we cleaned up old fields\n                try:\n                    # Validate mustache templates for security\n                    if is_mustache:\n                        validate_mustache_template(template_value)\n\n                    # Re-process template with new mode to add new variables\n                    _ = process_prompt_template(\n                        template=template_value,\n                        name=\"template\",\n                        custom_fields=build_config[\"custom_fields\"],\n                        frontend_node_template=build_config,\n                        is_mustache=is_mustache,\n                    )\n                except ValueError as e:\n                    # If validation fails, we still updated the mode and cleaned old fields\n                    # User will see error when they try to save\n                    logger.debug(\"Template validation failed during mode switch\", exc_info=e)\n        return build_config\n\n    async def build_prompt(self) -> Message:\n        use_double_brackets = self.use_double_brackets if hasattr(self, \"use_double_brackets\") else False\n        template_format = \"mustache\" if use_double_brackets else \"f-string\"\n        prompt = await Message.from_template_and_variables(template_format=template_format, **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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(prompt_template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be created\n            logger.debug(\"Template validation failed in _update_template\", exc_info=e)\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        use_double_brackets = frontend_node[\"template\"].get(\"use_double_brackets\", {}).get(\"value\", False)\n        is_mustache = use_double_brackets is True\n\n        try:\n            # Validate mustache templates for security\n            if is_mustache:\n                validate_mustache_template(template)\n\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                is_mustache=is_mustache,\n            )\n        except ValueError as e:\n            # If validation fails, don't add variables but allow component to be updated\n            logger.debug(\"Template validation failed in update_frontend_node\", exc_info=e)\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,
        "api_editable": false,
        "display_name": "Template",
        "dynamic": false,
        "info": "",
        "list": false,
        "list_add_label": "Add More",
        "name": "template",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "prompt",
        "value": "Draft report to review:\n{draft}"
       },
       "tool_placeholder": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": true,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "use_double_brackets": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Use Double Brackets",
        "dynamic": false,
        "info": "Use {{variable}} syntax instead of {variable}.",
        "list": false,
        "list_add_label": "Add More",
        "name": "use_double_brackets",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "draft": {
        "field_type": "str",
        "required": false,
        "placeholder": "",
        "list": false,
        "show": true,
        "multiline": true,
        "value": "",
        "fileTypes": [],
        "file_path": "",
        "name": "draft",
        "display_name": "draft",
        "advanced": false,
        "api_editable": false,
        "input_types": [
         "Message"
        ],
        "dynamic": false,
        "info": "",
        "load_from_db": false,
        "title_case": false,
        "type": "str"
       }
      },
      "description": "Create a prompt template with dynamic variables.",
      "icon": "prompts",
      "is_input": null,
      "is_output": null,
      "is_composition": null,
      "base_classes": [
       "Message"
      ],
      "name": "",
      "display_name": "7b. Critique 2 input",
      "priority": null,
      "documentation": "https://docs.langflow.org/components-prompts",
      "minimized": false,
      "custom_fields": {
       "template": [
        "draft"
       ]
      },
      "output_types": [],
      "full_path": null,
      "pinned": false,
      "conditional_paths": [],
      "frozen": false,
      "outputs": [
       {
        "types": [
         "Message"
        ],
        "selected": "Message",
        "name": "prompt",
        "hidden": null,
        "display_name": "Prompt",
        "method": "build_prompt",
        "value": "__UNDEFINED__",
        "cache": true,
        "required_inputs": null,
        "allows_loop": false,
        "loop_types": null,
        "group_outputs": false,
        "options": null,
        "tool_mode": true
       }
      ],
      "field_order": [
       "template",
       "use_double_brackets",
       "tool_placeholder"
      ],
      "beta": false,
      "legacy": false,
      "replacement": null,
      "error": null,
      "edited": false,
      "metadata": {
       "code_hash": "e3714ffe5d15",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "module": "lfx.components.models_and_agents.prompt.PromptComponent"
      },
      "tool_mode": false,
      "add_tool_output": null
     },
     "showNode": true
    }
   },
   {
    "id": "LanguageModelComponent-b490a3",
    "type": "genericNode",
    "position": {
     "x": 6440,
     "y": 1150
    },
    "data": {
     "id": "LanguageModelComponent-b490a3",
     "type": "LanguageModelComponent",
     "node": {
      "base_classes": [
       "LanguageModel",
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Runs a language model given a specified provider.",
      "display_name": "7b. Critique 2",
      "documentation": "https://docs.langflow.org/components-models",
      "edited": false,
      "field_order": [
       "model",
       "model_name",
       "provider",
       "api_key",
       "base_url_ibm_watsonx",
       "project_id",
       "ollama_base_url",
       "input_value",
       "system_message",
       "stream",
       "temperature",
       "max_tokens"
      ],
      "frozen": false,
      "icon": "brain-circuit",
      "legacy": false,
      "metadata": {
       "code_hash": "1bfa98a19c39",
       "dependencies": {
        "dependencies": [
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 1
       },
       "keywords": [
        "model",
        "llm",
        "language model",
        "large language model"
       ],
       "model_provider_policy_mode": "delegate",
       "module": "lfx.components.models_and_agents.language_model.LanguageModelComponent"
      },
      "minimized": false,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Model Response",
        "group_outputs": false,
        "method": "text_response",
        "name": "text_output",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       },
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Language Model",
        "group_outputs": false,
        "method": "build_model",
        "name": "model_output",
        "selected": "LanguageModel",
        "tool_mode": true,
        "types": [
         "LanguageModel"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "api_key": {
        "_input_type": "SecretStrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "API Key",
        "dynamic": false,
        "info": "Overrides global provider settings. Leave blank to use your pre-configured API Key.",
        "input_types": [],
        "load_from_db": true,
        "name": "api_key",
        "override_skip": false,
        "password": true,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": true,
        "title_case": false,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "base_url_ibm_watsonx": {
        "_input_type": "DropdownInput",
        "advanced": false,
        "api_editable": false,
        "combobox": true,
        "dialog_inputs": {},
        "display_name": "watsonx API Endpoint",
        "dynamic": false,
        "external_options": {},
        "info": "The base URL of the API (IBM watsonx.ai only)",
        "name": "base_url_ibm_watsonx",
        "options": [
         "https://us-south.ml.cloud.ibm.com",
         "https://eu-de.ml.cloud.ibm.com",
         "https://eu-gb.ml.cloud.ibm.com",
         "https://au-syd.ml.cloud.ibm.com",
         "https://jp-tok.ml.cloud.ibm.com",
         "https://ca-tor.ml.cloud.ibm.com"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "https://us-south.ml.cloud.ibm.com"
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "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.base.models.model import LCModelComponent\nfrom lfx.base.models.unified_models import (\n    get_language_model_options,\n    get_llm,\n    handle_model_input_update,\n)\nfrom lfx.base.models.watsonx_constants import IBM_WATSONX_URLS\nfrom lfx.components.models_and_agents.model_selection import apply_model_overrides\nfrom lfx.field_typing.constants import LanguageModel\nfrom lfx.field_typing.range_spec import RangeSpec\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, StrInput\nfrom lfx.io import IntInput, MessageInput, ModelInput, MultilineInput, SecretStrInput, SliderInput\n\n\nclass LanguageModelComponent(LCModelComponent):\n    model_provider_policy_mode = \"delegate\"\n    display_name = \"Language Model\"\n    description = \"Runs a language model given a specified provider.\"\n    documentation: str = \"https://docs.langflow.org/components-models\"\n    icon = \"brain-circuit\"\n    category = \"models\"\n\n    inputs = [\n        ModelInput(\n            name=\"model\",\n            display_name=\"Language Model\",\n            info=\"Select your model provider\",\n            real_time_refresh=True,\n            required=True,\n        ),\n        StrInput(\n            name=\"model_name\",\n            display_name=\"Model Name Override\",\n            info=(\n                \"Optional model name to use instead of the selected model. \"\n                \"Can be set from a global variable for runtime model selection.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        StrInput(\n            name=\"provider\",\n            display_name=\"Provider Override\",\n            info=(\n                \"Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.\"\n            ),\n            advanced=True,\n            load_from_db=False,\n        ),\n        SecretStrInput(\n            name=\"api_key\",\n            display_name=\"API Key\",\n            info=\"Overrides global provider settings. Leave blank to use your pre-configured API Key.\",\n            required=False,\n            show=True,\n            real_time_refresh=True,\n            advanced=True,\n        ),\n        DropdownInput(\n            name=\"base_url_ibm_watsonx\",\n            display_name=\"watsonx API Endpoint\",\n            info=\"The base URL of the API (IBM watsonx.ai only)\",\n            options=IBM_WATSONX_URLS,\n            value=IBM_WATSONX_URLS[0],\n            combobox=True,\n            show=False,\n            real_time_refresh=True,\n        ),\n        StrInput(\n            name=\"project_id\",\n            display_name=\"watsonx Project ID\",\n            info=\"The project ID associated with the foundation model (IBM watsonx.ai only)\",\n            show=False,\n            required=False,\n        ),\n        StrInput(\n            name=\"ollama_base_url\",\n            display_name=\"Ollama API URL\",\n            info=\"Endpoint of the Ollama API (Ollama only)\",\n            show=False,\n            real_time_refresh=True,\n        ),\n        MessageInput(\n            name=\"input_value\",\n            display_name=\"Input\",\n            info=\"The input text to send to the model\",\n        ),\n        MultilineInput(\n            name=\"system_message\",\n            display_name=\"System Message\",\n            info=\"A system message that helps set the behavior of the assistant\",\n            advanced=False,\n        ),\n        BoolInput(\n            name=\"stream\",\n            display_name=\"Stream\",\n            info=\"Whether to stream the response\",\n            value=False,\n            advanced=True,\n        ),\n        SliderInput(\n            name=\"temperature\",\n            display_name=\"Temperature\",\n            value=0.1,\n            info=\"Controls randomness in responses\",\n            range_spec=RangeSpec(min=0, max=1, step=0.01),\n            advanced=True,\n        ),\n        IntInput(\n            name=\"max_tokens\",\n            display_name=\"Max Tokens\",\n            info=\"Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.\",\n            advanced=True,\n            range_spec=RangeSpec(min=0, max=128000, step=1, step_type=\"int\"),\n        ),\n    ]\n\n    def build_model(self) -> LanguageModel:\n        model = apply_model_overrides(\n            self.model,\n            model_name=getattr(self, \"model_name\", None),\n            provider=getattr(self, \"provider\", None),\n            user_id=self.user_id,\n            get_options=get_language_model_options,\n        )\n        return get_llm(\n            model=model,\n            user_id=self.user_id,\n            api_key=self.api_key,\n            temperature=self.temperature,\n            stream=self.stream,\n            max_tokens=getattr(self, \"max_tokens\", None),\n            watsonx_url=getattr(self, \"base_url_ibm_watsonx\", None),\n            watsonx_project_id=getattr(self, \"project_id\", None),\n            ollama_base_url=getattr(self, \"ollama_base_url\", None),\n        )\n\n    def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None):\n        \"\"\"Dynamically update build config with user-filtered model options.\"\"\"\n        return handle_model_input_update(self, build_config, field_value, field_name)\n"
       },
       "input_value": {
        "_input_type": "MessageInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Input",
        "dynamic": false,
        "info": "The input text to send to the model",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "max_tokens": {
        "_input_type": "IntInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Max Tokens",
        "dynamic": false,
        "info": "Maximum number of tokens to generate. Set to 0 for no explicit limit. Field name varies by provider.",
        "list": false,
        "list_add_label": "Add More",
        "name": "max_tokens",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 128000.0,
         "min": 0.0,
         "step": 1.0,
         "step_type": "int"
        },
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "int",
        "value": 16000
       },
       "model": {
        "_input_type": "ModelInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Language Model",
        "dynamic": false,
        "external_options": {
         "fields": {
          "data": {
           "node": {
            "display_name": "Connect other models",
            "icon": "CornerDownLeft",
            "name": "connect_other_models"
           }
          }
         }
        },
        "info": "Select your model provider",
        "input_types": [
         "LanguageModel"
        ],
        "list": false,
        "list_add_label": "Add More",
        "model_type": "language",
        "name": "model",
        "override_skip": false,
        "placeholder": "Setup Provider",
        "real_time_refresh": true,
        "refresh_button": true,
        "required": true,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "track_in_telemetry": false,
        "type": "model",
        "value": [
         {
          "name": "claude-sonnet-5",
          "icon": "Anthropic",
          "category": "Anthropic",
          "provider": "Anthropic",
          "metadata": {
           "context_length": 128000,
           "model_class": "ChatAnthropic",
           "model_name_param": "model",
           "api_key_param": "api_key",
           "max_tokens_field_name": "max_tokens",
           "reasoning": true,
           "reasoning_models": [
            "claude-sonnet-5"
           ]
          }
         }
        ]
       },
       "model_name": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Model Name Override",
        "dynamic": false,
        "info": "Optional model name to use instead of the selected model. Can be set from a global variable for runtime model selection.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "model_name",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "ollama_base_url": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Ollama API URL",
        "dynamic": false,
        "info": "Endpoint of the Ollama API (Ollama only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "ollama_base_url",
        "override_skip": false,
        "placeholder": "",
        "real_time_refresh": true,
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "project_id": {
        "_input_type": "StrInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "watsonx Project ID",
        "dynamic": false,
        "info": "The project ID associated with the foundation model (IBM watsonx.ai only)",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "project_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": false,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "provider": {
        "_input_type": "StrInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Provider Override",
        "dynamic": false,
        "info": "Optional provider to use with Model Name Override. Leave blank to use the selected model's provider.",
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "provider",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "stream": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Stream",
        "dynamic": false,
        "info": "Whether to stream the response",
        "list": false,
        "list_add_label": "Add More",
        "name": "stream",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": false
       },
       "system_message": {
        "_input_type": "MultilineInput",
        "advanced": false,
        "ai_enabled": false,
        "api_editable": false,
        "copy_field": false,
        "display_name": "System Message",
        "dynamic": false,
        "info": "A system message that helps set the behavior of the assistant",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "multiline": true,
        "name": "system_message",
        "override_skip": false,
        "password": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "You are a rigorous internal reviewer. You do not write or rewrite report\ncontent; you only evaluate it.\nYou will receive the draft report.\nScore the draft against this rubric:\n1. Framework correctness: is each factor correctly classified (a Five\n   Forces item isn't presented as a SWOT item and vice versa)?\n2. Traceability: is every claim traceable to a prior-stage output, or\n   marked [UNSOURCED]?\n3. No invented certainty: flag any [UNSOURCED] claim stated as fact.\n4. Answers the question: does the Recommendation actually answer the\n   strategic question, using the top-ranked TOWS options?\n5. Clarity: flag unclear, repetitive, or overlong sentences.\nOutput strictly as JSON:\n{\n\"verdict\": \"PASS\" or \"REVISE\",\n\"fixes\": [\n{\"section\": \"<section name>\", \"issue\": \"<what is wrong>\", \"fix\": \"<specific instruction to correct it>\"}\n]\n}\nReturn \"PASS\" only if there are zero fixes required."
       },
       "temperature": {
        "_input_type": "SliderInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Temperature",
        "dynamic": false,
        "info": "Controls randomness in responses",
        "max_label": "",
        "max_label_icon": "",
        "min_label": "",
        "min_label_icon": "",
        "name": "temperature",
        "override_skip": false,
        "placeholder": "",
        "range_spec": {
         "max": 1.0,
         "min": 0.0,
         "step": 0.01,
         "step_type": "float"
        },
        "required": false,
        "show": true,
        "slider_buttons": false,
        "slider_buttons_options": [],
        "slider_color": "default",
        "slider_input": false,
        "title_case": false,
        "tool_mode": false,
        "track_in_telemetry": false,
        "type": "slider",
        "value": 0.1,
        "value_inverted": false
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   },
   {
    "id": "ChatOutput-1e9e3f",
    "type": "genericNode",
    "position": {
     "x": 6900,
     "y": 1150
    },
    "data": {
     "id": "ChatOutput-1e9e3f",
     "type": "ChatOutput",
     "node": {
      "base_classes": [
       "Message"
      ],
      "beta": false,
      "conditional_paths": [],
      "custom_fields": {},
      "description": "Display a chat message in the Playground.",
      "display_name": "Critique 2 verdict",
      "documentation": "https://docs.langflow.org/chat-input-and-output",
      "edited": false,
      "field_order": [
       "input_value",
       "should_store_message",
       "sender",
       "sender_name",
       "session_id",
       "context_id",
       "data_template",
       "clean_data"
      ],
      "frozen": false,
      "icon": "MessagesSquare",
      "legacy": false,
      "metadata": {
       "code_hash": "84009527d08c",
       "dependencies": {
        "dependencies": [
         {
          "name": "orjson",
          "version": "3.11.9"
         },
         {
          "name": "fastapi",
          "version": "0.139.2"
         },
         {
          "name": "lfx",
          "version": null
         }
        ],
        "total_dependencies": 3
       },
       "module": "lfx.components.input_output.chat_output.ChatOutput"
      },
      "minimized": true,
      "output_types": [],
      "outputs": [
       {
        "allows_loop": false,
        "cache": true,
        "display_name": "Output Message",
        "group_outputs": false,
        "method": "message_response",
        "name": "message",
        "selected": "Message",
        "tool_mode": true,
        "types": [
         "Message"
        ],
        "value": "__UNDEFINED__"
       }
      ],
      "pinned": false,
      "template": {
       "_type": "Component",
       "clean_data": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Basic Clean Data",
        "dynamic": false,
        "info": "Whether to clean data before converting to string.",
        "list": false,
        "list_add_label": "Add More",
        "name": "clean_data",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       },
       "code": {
        "advanced": true,
        "api_editable": false,
        "dynamic": true,
        "fileTypes": [],
        "file_path": "",
        "info": "",
        "list": false,
        "load_from_db": false,
        "multiline": true,
        "name": "code",
        "password": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "type": "code",
        "value": "from collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom lfx.base.io.chat import ChatComponent\nfrom lfx.helpers.data import safe_convert\nfrom lfx.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\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.template.field.base import Output\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    documentation: str = \"https://docs.langflow.org/chat-input-and-output\"\n    icon = \"MessagesSquare\"\n    name = \"ChatOutput\"\n    minimized = True\n\n    inputs = [\n        HandleInput(\n            name=\"input_value\",\n            display_name=\"Inputs\",\n            info=\"Message to be passed as output.\",\n            input_types=[\"Data\", \"JSON\", \"DataFrame\", \"Table\", \"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=\"context_id\",\n            display_name=\"Context ID\",\n            info=\"The context ID of the chat. Adds an extra layer to the local memory.\",\n            value=\"\",\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        BoolInput(\n            name=\"clean_data\",\n            display_name=\"Basic Clean Data\",\n            value=True,\n            advanced=True,\n            info=\"Whether to clean data before converting to string.\",\n        ),\n    ]\n    outputs = [\n        Output(\n            display_name=\"Output Message\",\n            name=\"message\",\n            method=\"message_response\",\n        ),\n    ]\n\n    def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n        source_dict = {}\n        if id_:\n            source_dict[\"id\"] = id_\n        if display_name:\n            source_dict[\"display_name\"] = display_name\n        if source:\n            # Handle case where source is a ChatOpenAI object\n            if hasattr(source, \"model_name\"):\n                source_dict[\"source\"] = source.model_name\n            elif hasattr(source, \"model\"):\n                source_dict[\"source\"] = str(source.model)\n            else:\n                source_dict[\"source\"] = str(source)\n        return Source(**source_dict)\n\n    async def message_response(self) -> Message:\n        # First convert the input to string if needed\n        text = self.convert_to_string()\n\n        # Get source properties\n        source, _, display_name, source_id = self.get_properties_from_source_component()\n\n        # Create or use existing Message object\n        if isinstance(self.input_value, Message) and not self.is_connected_to_chat_input():\n            message = self.input_value\n            # Update message properties\n            message.text = text\n            # Preserve existing session_id from the incoming message if it exists\n            existing_session_id = message.session_id\n        else:\n            message = Message(text=text)\n            existing_session_id = None\n\n        # Set message properties\n        message.sender = self.sender\n        message.sender_name = self.sender_name\n        # Preserve session_id from incoming message, or use component/graph session_id\n        message.session_id = (\n            self.session_id or existing_session_id or (self.graph.session_id if hasattr(self, \"graph\") else None) or \"\"\n        )\n        message.context_id = self.context_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\n        # Store message if needed\n        if message.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        # Set accumulated token usage from all upstream LLM vertices.\n        # This must happen AFTER send_message() because streaming captures\n        # usage from chunks and would overwrite accumulated totals.\n        if hasattr(self, \"_vertex\") and self._vertex is not None:\n            accumulated_usage = self._vertex._accumulate_upstream_token_usage()  # noqa: SLF001\n            if accumulated_usage:\n                message.properties.usage = accumulated_usage\n                if self.should_store_message and message.get_id():\n                    message = await self._update_stored_message(message)\n                    await self._send_message_event(message, id_=message.get_id())\n\n        self.status = message\n        return message\n\n    def _serialize_data(self, data: Data) -> str:\n        \"\"\"Serialize Data object to JSON string.\"\"\"\n        # Convert data.data to JSON-serializable format\n        serializable_data = jsonable_encoder(data.data)\n        # Serialize with orjson, enabling pretty printing with indentation\n        json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n        # Convert bytes to string and wrap in Markdown code blocks\n        return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n    def _validate_input(self) -> None:\n        \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n        if self.input_value is None:\n            msg = \"Input data cannot be None\"\n            raise ValueError(msg)\n        if isinstance(self.input_value, list) and not all(\n            isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n        ):\n            invalid_types = [\n                type(item).__name__\n                for item in self.input_value\n                if not isinstance(item, Message | Data | DataFrame | str)\n            ]\n            msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n            raise TypeError(msg)\n        if not isinstance(\n            self.input_value,\n            Message | Data | DataFrame | str | list | Generator | type(None),\n        ):\n            type_name = type(self.input_value).__name__\n            msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n            raise TypeError(msg)\n\n    def convert_to_string(self) -> str | Generator[Any, None, None]:\n        \"\"\"Convert input data to string with proper error handling.\"\"\"\n        self._validate_input()\n        if isinstance(self.input_value, list):\n            clean_data: bool = getattr(self, \"clean_data\", False)\n            return \"\\n\".join([safe_convert(item, clean_data=clean_data) for item in self.input_value])\n        if isinstance(self.input_value, Generator):\n            return self.input_value\n        return safe_convert(self.input_value)\n"
       },
       "context_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Context ID",
        "dynamic": false,
        "info": "The context ID of the chat. Adds an extra layer to the local memory.",
        "input_types": [
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "load_from_db": false,
        "name": "context_id",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "data_template": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "{text}"
       },
       "input_value": {
        "_input_type": "HandleInput",
        "advanced": false,
        "api_editable": false,
        "display_name": "Inputs",
        "dynamic": false,
        "info": "Message to be passed as output.",
        "input_types": [
         "Data",
         "JSON",
         "DataFrame",
         "Table",
         "Message"
        ],
        "list": false,
        "list_add_label": "Add More",
        "name": "input_value",
        "override_skip": false,
        "placeholder": "",
        "required": true,
        "show": true,
        "title_case": false,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "other",
        "value": ""
       },
       "sender": {
        "_input_type": "DropdownInput",
        "advanced": true,
        "api_editable": false,
        "combobox": false,
        "dialog_inputs": {},
        "display_name": "Sender Type",
        "dynamic": false,
        "external_options": {},
        "info": "Type of sender.",
        "name": "sender",
        "options": [
         "Machine",
         "User"
        ],
        "options_metadata": [],
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "toggle": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "str",
        "value": "Machine"
       },
       "sender_name": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": "AI"
       },
       "session_id": {
        "_input_type": "MessageTextInput",
        "advanced": true,
        "api_editable": false,
        "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",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_input": true,
        "trace_as_metadata": true,
        "track_in_telemetry": false,
        "type": "str",
        "value": ""
       },
       "should_store_message": {
        "_input_type": "BoolInput",
        "advanced": true,
        "api_editable": false,
        "display_name": "Store Messages",
        "dynamic": false,
        "info": "Store the message in the history.",
        "list": false,
        "list_add_label": "Add More",
        "name": "should_store_message",
        "override_skip": false,
        "placeholder": "",
        "required": false,
        "show": true,
        "title_case": false,
        "tool_mode": false,
        "trace_as_metadata": true,
        "track_in_telemetry": true,
        "type": "bool",
        "value": true
       }
      },
      "tool_mode": false
     },
     "showNode": true
    }
   }
  ],
  "edges": [
   {
    "id": "xy-edge__ChatInput-a37b32{\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-a37b32\u0153,\u0153name\u0153:\u0153message\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-db48ef{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-db48ef\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "ChatInput-a37b32",
    "target": "LanguageModelComponent-db48ef",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-a37b32\u0153,\u0153name\u0153:\u0153message\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-db48ef\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ChatInput",
      "id": "ChatInput-a37b32",
      "name": "message",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-db48ef",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-db48ef{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-db48ef\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-Agent-42850e{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-db48ef",
    "target": "Agent-42850e",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-db48ef\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-db48ef",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "Agent-42850e",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__WebResearch-8ac626{\u0153dataType\u0153:\u0153ext:reports:WebResearch@extra\u0153,\u0153id\u0153:\u0153WebResearch-8ac626\u0153,\u0153name\u0153:\u0153component_as_tool\u0153,\u0153output_types\u0153:[\u0153Tool\u0153]}-Agent-42850e{\u0153fieldName\u0153:\u0153tools\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153inputTypes\u0153:[\u0153Tool\u0153],\u0153type\u0153:\u0153other\u0153}",
    "source": "WebResearch-8ac626",
    "target": "Agent-42850e",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ext:reports:WebResearch@extra\u0153,\u0153id\u0153:\u0153WebResearch-8ac626\u0153,\u0153name\u0153:\u0153component_as_tool\u0153,\u0153output_types\u0153:[\u0153Tool\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153tools\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153inputTypes\u0153:[\u0153Tool\u0153],\u0153type\u0153:\u0153other\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ext:reports:WebResearch@extra",
      "id": "WebResearch-8ac626",
      "name": "component_as_tool",
      "output_types": [
       "Tool"
      ]
     },
     "targetHandle": {
      "fieldName": "tools",
      "id": "Agent-42850e",
      "inputTypes": [
       "Tool"
      ],
      "type": "other"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__Agent-42850e{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-b2706d{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-b2706d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "Agent-42850e",
    "target": "PromptComponent-b2706d",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-b2706d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Agent",
      "id": "Agent-42850e",
      "name": "response",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "research",
      "id": "PromptComponent-b2706d",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-b2706d{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-b2706d\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-def4cd{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-b2706d",
    "target": "LanguageModelComponent-def4cd",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-b2706d\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-b2706d",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-def4cd",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__Agent-42850e{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-68f723{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "Agent-42850e",
    "target": "PromptComponent-68f723",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Agent",
      "id": "Agent-42850e",
      "name": "response",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "research",
      "id": "PromptComponent-68f723",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-def4cd{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-68f723{\u0153fieldName\u0153:\u0153five_forces\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-def4cd",
    "target": "PromptComponent-68f723",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153five_forces\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-def4cd",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "five_forces",
      "id": "PromptComponent-68f723",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-68f723{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-96d726{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-68f723",
    "target": "LanguageModelComponent-96d726",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-68f723\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-68f723",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-96d726",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__Agent-42850e{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-60e59a{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "Agent-42850e",
    "target": "PromptComponent-60e59a",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Agent",
      "id": "Agent-42850e",
      "name": "response",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "research",
      "id": "PromptComponent-60e59a",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-96d726{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-60e59a{\u0153fieldName\u0153:\u0153swot\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-96d726",
    "target": "PromptComponent-60e59a",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153swot\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-96d726",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "swot",
      "id": "PromptComponent-60e59a",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-60e59a{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-c3d9df{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-c3d9df\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-60e59a",
    "target": "LanguageModelComponent-c3d9df",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-60e59a\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-c3d9df\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-60e59a",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-c3d9df",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__Agent-42850e{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-a5d4ec{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "Agent-42850e",
    "target": "PromptComponent-a5d4ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Agent\u0153,\u0153id\u0153:\u0153Agent-42850e\u0153,\u0153name\u0153:\u0153response\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153research\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Agent",
      "id": "Agent-42850e",
      "name": "response",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "research",
      "id": "PromptComponent-a5d4ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-def4cd{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-a5d4ec{\u0153fieldName\u0153:\u0153five_forces\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-def4cd",
    "target": "PromptComponent-a5d4ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-def4cd\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153five_forces\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-def4cd",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "five_forces",
      "id": "PromptComponent-a5d4ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-96d726{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-a5d4ec{\u0153fieldName\u0153:\u0153swot\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-96d726",
    "target": "PromptComponent-a5d4ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-96d726\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153swot\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-96d726",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "swot",
      "id": "PromptComponent-a5d4ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-c3d9df{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-c3d9df\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-a5d4ec{\u0153fieldName\u0153:\u0153tows\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-c3d9df",
    "target": "PromptComponent-a5d4ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-c3d9df\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153tows\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-c3d9df",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "tows",
      "id": "PromptComponent-a5d4ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-a5d4ec{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-390257{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-a5d4ec",
    "target": "LanguageModelComponent-390257",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-a5d4ec\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-a5d4ec",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-390257",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-390257{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-b31840{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-b31840\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-390257",
    "target": "PromptComponent-b31840",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-b31840\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-390257",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "draft",
      "id": "PromptComponent-b31840",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-b31840{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-b31840\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-dbf74f{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-b31840",
    "target": "LanguageModelComponent-dbf74f",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-b31840\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-b31840",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-dbf74f",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-dbf74f{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ConditionalRouter-ae00e4{\u0153fieldName\u0153:\u0153input_text\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-dbf74f",
    "target": "ConditionalRouter-ae00e4",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_text\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-dbf74f",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_text",
      "id": "ConditionalRouter-ae00e4",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-390257{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ConditionalRouter-ae00e4{\u0153fieldName\u0153:\u0153true_case_message\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-390257",
    "target": "ConditionalRouter-ae00e4",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153true_case_message\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-390257",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "true_case_message",
      "id": "ConditionalRouter-ae00e4",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-390257{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ConditionalRouter-ae00e4{\u0153fieldName\u0153:\u0153false_case_message\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-390257",
    "target": "ConditionalRouter-ae00e4",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-390257\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153false_case_message\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-390257",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "false_case_message",
      "id": "ConditionalRouter-ae00e4",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__ConditionalRouter-ae00e4{\u0153dataType\u0153:\u0153ConditionalRouter\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153name\u0153:\u0153true_result\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ExportReport-377b2d{\u0153fieldName\u0153:\u0153report\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "ConditionalRouter-ae00e4",
    "target": "ExportReport-377b2d",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ConditionalRouter\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153name\u0153:\u0153true_result\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153report\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ConditionalRouter",
      "id": "ConditionalRouter-ae00e4",
      "name": "true_result",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "report",
      "id": "ExportReport-377b2d",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-dbf74f{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ExportReport-377b2d{\u0153fieldName\u0153:\u0153review\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-dbf74f",
    "target": "ExportReport-377b2d",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153review\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-dbf74f",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "review",
      "id": "ExportReport-377b2d",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__ExportReport-377b2d{\u0153dataType\u0153:\u0153ext:reports:ExportReport@extra\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153name\u0153:\u0153links\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ChatOutput-f0c919{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-f0c919\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "source": "ExportReport-377b2d",
    "target": "ChatOutput-f0c919",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ext:reports:ExportReport@extra\u0153,\u0153id\u0153:\u0153ExportReport-377b2d\u0153,\u0153name\u0153:\u0153links\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-f0c919\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ext:reports:ExportReport@extra",
      "id": "ExportReport-377b2d",
      "name": "links",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "ChatOutput-f0c919",
      "inputTypes": [
       "Data",
       "JSON",
       "DataFrame",
       "Table",
       "Message"
      ],
      "type": "other"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__ConditionalRouter-ae00e4{\u0153dataType\u0153:\u0153ConditionalRouter\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153name\u0153:\u0153false_result\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-2d545f{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "ConditionalRouter-ae00e4",
    "target": "PromptComponent-2d545f",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ConditionalRouter\u0153,\u0153id\u0153:\u0153ConditionalRouter-ae00e4\u0153,\u0153name\u0153:\u0153false_result\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ConditionalRouter",
      "id": "ConditionalRouter-ae00e4",
      "name": "false_result",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "draft",
      "id": "PromptComponent-2d545f",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-dbf74f{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-2d545f{\u0153fieldName\u0153:\u0153critique\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-dbf74f",
    "target": "PromptComponent-2d545f",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-dbf74f\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153critique\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-dbf74f",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "critique",
      "id": "PromptComponent-2d545f",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-2d545f{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-5ceb31{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-2d545f",
    "target": "LanguageModelComponent-5ceb31",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-2d545f\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-2d545f",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-5ceb31",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-5ceb31{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ExportReport-7392ec{\u0153fieldName\u0153:\u0153report\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-5ceb31",
    "target": "ExportReport-7392ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153report\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-5ceb31",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "report",
      "id": "ExportReport-7392ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-b490a3{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ExportReport-7392ec{\u0153fieldName\u0153:\u0153review\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-b490a3",
    "target": "ExportReport-7392ec",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153review\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-b490a3",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "review",
      "id": "ExportReport-7392ec",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__ExportReport-7392ec{\u0153dataType\u0153:\u0153ext:reports:ExportReport@extra\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153name\u0153:\u0153links\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ChatOutput-3978ba{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-3978ba\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "source": "ExportReport-7392ec",
    "target": "ChatOutput-3978ba",
    "sourceHandle": "{\u0153dataType\u0153:\u0153ext:reports:ExportReport@extra\u0153,\u0153id\u0153:\u0153ExportReport-7392ec\u0153,\u0153name\u0153:\u0153links\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-3978ba\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "ext:reports:ExportReport@extra",
      "id": "ExportReport-7392ec",
      "name": "links",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "ChatOutput-3978ba",
      "inputTypes": [
       "Data",
       "JSON",
       "DataFrame",
       "Table",
       "Message"
      ],
      "type": "other"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-5ceb31{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-PromptComponent-cbd27c{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-cbd27c\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "LanguageModelComponent-5ceb31",
    "target": "PromptComponent-cbd27c",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-5ceb31\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153draft\u0153,\u0153id\u0153:\u0153PromptComponent-cbd27c\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-5ceb31",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "draft",
      "id": "PromptComponent-cbd27c",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__PromptComponent-cbd27c{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-cbd27c\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-LanguageModelComponent-b490a3{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "source": "PromptComponent-cbd27c",
    "target": "LanguageModelComponent-b490a3",
    "sourceHandle": "{\u0153dataType\u0153:\u0153Prompt Template\u0153,\u0153id\u0153:\u0153PromptComponent-cbd27c\u0153,\u0153name\u0153:\u0153prompt\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153inputTypes\u0153:[\u0153Message\u0153],\u0153type\u0153:\u0153str\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "Prompt Template",
      "id": "PromptComponent-cbd27c",
      "name": "prompt",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "LanguageModelComponent-b490a3",
      "inputTypes": [
       "Message"
      ],
      "type": "str"
     }
    },
    "animated": false,
    "className": ""
   },
   {
    "id": "xy-edge__LanguageModelComponent-b490a3{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}-ChatOutput-1e9e3f{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-1e9e3f\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "source": "LanguageModelComponent-b490a3",
    "target": "ChatOutput-1e9e3f",
    "sourceHandle": "{\u0153dataType\u0153:\u0153LanguageModelComponent\u0153,\u0153id\u0153:\u0153LanguageModelComponent-b490a3\u0153,\u0153name\u0153:\u0153text_output\u0153,\u0153output_types\u0153:[\u0153Message\u0153]}",
    "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-1e9e3f\u0153,\u0153inputTypes\u0153:[\u0153Data\u0153,\u0153JSON\u0153,\u0153DataFrame\u0153,\u0153Table\u0153,\u0153Message\u0153],\u0153type\u0153:\u0153other\u0153}",
    "data": {
     "sourceHandle": {
      "dataType": "LanguageModelComponent",
      "id": "LanguageModelComponent-b490a3",
      "name": "text_output",
      "output_types": [
       "Message"
      ]
     },
     "targetHandle": {
      "fieldName": "input_value",
      "id": "ChatOutput-1e9e3f",
      "inputTypes": [
       "Data",
       "JSON",
       "DataFrame",
       "Table",
       "Message"
      ],
      "type": "other"
     }
    },
    "animated": false,
    "className": ""
   }
  ],
  "viewport": {
   "x": 0,
   "y": 0,
   "zoom": 0.5
  }
 }
}