{ "data": { "nodes": [ { "data": { "node": { "template": { "_type": "Component", "files": { "tool_mode": false, "trace_as_metadata": true, "file_path": "", "fileTypes": [ "csv", "json", "pdf", "txt", "md", "mdx", "yaml", "yml", "xml", "html", "htm", "docx", "py", "sh", "sql", "js", "ts", "tsx", "jpg", "jpeg", "png", "bmp", "image" ], "temp_file": true, "list": true, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "files", "value": "", "display_name": "Files", "advanced": true, "api_editable": false, "dynamic": false, "info": "Files to be sent with the message.", "title_case": false, "track_in_telemetry": false, "type": "file", "_input_type": "FileInput" }, "code": { "type": "code", "required": true, "placeholder": "", "list": false, "show": true, "multiline": true, "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", "fileTypes": [], "file_path": "", "password": false, "name": "code", "advanced": true, "api_editable": false, "dynamic": true, "info": "", "load_from_db": false, "title_case": false }, "context_id": { "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": "context_id", "value": "", "display_name": "Context ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The context ID of the chat. Adds an extra layer to the local memory.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "input_value": { "tool_mode": false, "trace_as_input": true, "multiline": true, "ai_enabled": false, "trace_as_metadata": true, "load_from_db": false, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "input_value", "value": "", "display_name": "Input Text", "advanced": false, "api_editable": false, "input_types": [], "dynamic": false, "info": "Message to be passed as input.", "title_case": false, "track_in_telemetry": false, "copy_field": false, "password": false, "type": "str", "_input_type": "MultilineInput" }, "sender": { "tool_mode": false, "trace_as_metadata": true, "options": [ "Machine", "User" ], "options_metadata": [], "combobox": false, "dialog_inputs": {}, "toggle": false, "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "sender", "value": "User", "display_name": "Sender Type", "advanced": true, "api_editable": false, "dynamic": false, "info": "Type of sender.", "title_case": false, "track_in_telemetry": true, "external_options": {}, "type": "str", "_input_type": "DropdownInput" }, "sender_name": { "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": "sender_name", "value": "User", "display_name": "Sender Name", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "Name of the sender.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "session_id": { "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": "session_id", "value": "", "display_name": "Session ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "should_store_message": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "should_store_message", "value": true, "display_name": "Store Messages", "advanced": true, "api_editable": false, "dynamic": false, "info": "Store the message in the history.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" } }, "description": "Get chat inputs from the Playground.", "icon": "MessagesSquare", "base_classes": [ "Message" ], "display_name": "Chat Input", "documentation": "https://docs.langflow.org/chat-input-and-output", "minimized": true, "custom_fields": {}, "output_types": [], "pinned": false, "conditional_paths": [], "frozen": false, "outputs": [ { "types": [ "Message" ], "selected": "Message", "name": "message", "display_name": "Chat Message", "method": "message_response", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": false, "tool_mode": true } ], "field_order": [], "beta": false, "legacy": false, "edited": false, "metadata": {}, "tool_mode": false }, "type": "ChatInput", "id": "ChatInput-entry", "showNode": true }, "id": "ChatInput-entry", "measured": { "width": 320, "height": 200 }, "position": { "x": 0, "y": 200 }, "type": "genericNode" }, { "data": { "node": { "template": { "_type": "Component", "code": { "type": "code", "required": true, "placeholder": "", "list": false, "show": true, "multiline": true, "value": "from lfx.custom import Component\nfrom lfx.io import MessageTextInput, Output\nfrom lfx.schema.message import Message\nimport json\n\n\nclass RuleCheckComponent(Component):\n display_name = \"Rule Check\"\n description = \"Bewertet Betrag gegen Schwelle aus JSON-Eingabe.\"\n\n inputs = [\n MessageTextInput(name=\"payload\", display_name=\"Payload\"),\n ]\n outputs = [\n Output(display_name=\"Verdict\", name=\"verdict\", method=\"check\"),\n ]\n\n def _text(self, value) -> str:\n if value is None:\n return \"\"\n if hasattr(value, \"text\"):\n return value.text or \"\"\n return str(value)\n\n def check(self) -> Message:\n try:\n data = json.loads(self._text(self.payload) or \"{}\")\n except Exception:\n data = {}\n if not isinstance(data, dict):\n data = {}\n # Die API ruft Flows immer mit Huelle auf\n # {\"payload\": {...}, \"run_id\": ..., ...}; rohe Payloads gehen auch.\n if isinstance(data.get(\"payload\"), dict):\n data = data[\"payload\"]\n try:\n betrag = float(data.get(\"betrag\", 0) or 0)\n except (TypeError, ValueError):\n betrag = 0.0\n try:\n schwelle = float(data.get(\"schwelle\", 200000) or 200000)\n except (TypeError, ValueError):\n schwelle = 200000.0\n return Message(text=\"PRUEFUNG\" if betrag > schwelle else \"OK\")\n", "fileTypes": [], "file_path": "", "password": false, "name": "code", "advanced": true, "api_editable": false, "dynamic": true, "info": "", "load_from_db": false, "title_case": false }, "payload": { "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": "payload", "value": "", "display_name": "Payload", "advanced": false, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" } }, "description": "Bewertet Betrag gegen Schwelle aus JSON-Eingabe.", "base_classes": [ "Message" ], "display_name": "Rule Check", "documentation": "", "minimized": false, "custom_fields": {}, "output_types": [], "pinned": false, "conditional_paths": [], "frozen": false, "outputs": [ { "types": [ "Message" ], "selected": "Message", "name": "verdict", "display_name": "Verdict", "method": "check", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": false, "tool_mode": true } ], "field_order": [ "payload" ], "beta": false, "legacy": false, "edited": false, "metadata": { "module": "custom_components.rule_check", "code_hash": "0b9f8a009fcf", "dependencies": { "total_dependencies": 1, "dependencies": [ { "name": "lfx", "version": "1.12.0" } ] } }, "tool_mode": false }, "type": "CustomComponent", "id": "CustomComponent-rule", "showNode": true }, "id": "CustomComponent-rule", "measured": { "width": 320, "height": 200 }, "position": { "x": 400, "y": 200 }, "type": "genericNode" }, { "data": { "node": { "template": { "_type": "Component", "case_sensitive": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "case_sensitive", "value": true, "display_name": "Case Sensitive", "advanced": true, "api_editable": false, "dynamic": false, "info": "If true, the comparison will be case sensitive.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" }, "code": { "type": "code", "required": true, "placeholder": "", "list": false, "show": true, "multiline": true, "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", "fileTypes": [], "file_path": "", "password": false, "name": "code", "advanced": true, "api_editable": false, "dynamic": true, "info": "", "load_from_db": false, "title_case": false }, "default_route": { "tool_mode": false, "trace_as_metadata": true, "options": [ "true_result", "false_result" ], "options_metadata": [], "combobox": false, "dialog_inputs": {}, "toggle": false, "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "default_route", "value": "false_result", "display_name": "Default Route", "advanced": true, "api_editable": false, "dynamic": false, "info": "The default route to take when max iterations are reached.", "title_case": false, "track_in_telemetry": true, "external_options": {}, "type": "str", "_input_type": "DropdownInput" }, "false_case_message": { "trace_as_input": true, "tool_mode": false, "trace_as_metadata": true, "load_from_db": false, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "false_case_message", "value": "Standardfall: automatisch freigegeben", "display_name": "Case False", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The message to pass if the condition is False.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageInput" }, "input_text": { "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": "input_text", "value": "", "display_name": "Text Input", "advanced": false, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The primary text input for the operation.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "match_text": { "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": "match_text", "value": "PRUEFUNG", "display_name": "Match Text", "advanced": false, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The text input to compare against.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "max_iterations": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "max_iterations", "value": 10, "display_name": "Max Iterations", "advanced": true, "api_editable": false, "dynamic": false, "info": "The maximum number of iterations for the conditional router.", "title_case": false, "track_in_telemetry": true, "type": "int", "_input_type": "IntInput" }, "operator": { "tool_mode": false, "trace_as_metadata": true, "options": [ "equals", "not equals", "contains", "starts with", "ends with", "regex", "less than", "less than or equal", "greater than", "greater than or equal" ], "options_metadata": [], "combobox": false, "dialog_inputs": {}, "toggle": false, "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "operator", "value": "equals", "display_name": "Operator", "advanced": false, "api_editable": false, "dynamic": false, "info": "The operator to apply for comparing the texts.", "real_time_refresh": true, "title_case": false, "track_in_telemetry": true, "external_options": {}, "type": "str", "_input_type": "DropdownInput" }, "true_case_message": { "trace_as_input": true, "tool_mode": false, "trace_as_metadata": true, "load_from_db": false, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "true_case_message", "value": "Sonderfall: menschliche Pruefung erforderlich", "display_name": "Case True", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The message to pass if the condition is True.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageInput" } }, "description": "Routes an input message to a corresponding output based on text comparison.", "icon": "split", "base_classes": [ "Message" ], "display_name": "If-Else", "documentation": "https://docs.langflow.org/if-else", "minimized": false, "custom_fields": {}, "output_types": [], "pinned": false, "conditional_paths": [], "frozen": false, "outputs": [ { "types": [ "Message" ], "selected": "Message", "name": "true_result", "display_name": "True", "method": "true_response", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": true, "tool_mode": true }, { "types": [ "Message" ], "selected": "Message", "name": "false_result", "display_name": "False", "method": "false_response", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": true, "tool_mode": true } ], "field_order": [], "beta": false, "legacy": false, "edited": false, "metadata": {}, "tool_mode": false }, "type": "ConditionalRouter", "id": "ConditionalRouter-router", "showNode": true }, "id": "ConditionalRouter-router", "measured": { "width": 320, "height": 200 }, "position": { "x": 800, "y": 200 }, "type": "genericNode" }, { "data": { "node": { "template": { "_type": "Component", "input_value": { "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": true, "placeholder": "", "show": true, "name": "input_value", "value": "", "display_name": "Inputs", "advanced": false, "api_editable": false, "input_types": [ "Data", "JSON", "DataFrame", "Table", "Message" ], "dynamic": false, "info": "Message to be passed as output.", "title_case": false, "track_in_telemetry": false, "type": "other", "_input_type": "HandleInput" }, "clean_data": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "clean_data", "value": true, "display_name": "Basic Clean Data", "advanced": true, "api_editable": false, "dynamic": false, "info": "Whether to clean data before converting to string.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" }, "code": { "type": "code", "required": true, "placeholder": "", "list": false, "show": true, "multiline": true, "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", "fileTypes": [], "file_path": "", "password": false, "name": "code", "advanced": true, "api_editable": false, "dynamic": true, "info": "", "load_from_db": false, "title_case": false }, "context_id": { "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": "context_id", "value": "", "display_name": "Context ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The context ID of the chat. Adds an extra layer to the local memory.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "data_template": { "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": "data_template", "value": "{text}", "display_name": "Data Template", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "sender": { "tool_mode": false, "trace_as_metadata": true, "options": [ "Machine", "User" ], "options_metadata": [], "combobox": false, "dialog_inputs": {}, "toggle": false, "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "sender", "value": "Machine", "display_name": "Sender Type", "advanced": true, "api_editable": false, "dynamic": false, "info": "Type of sender.", "title_case": false, "track_in_telemetry": true, "external_options": {}, "type": "str", "_input_type": "DropdownInput" }, "sender_name": { "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": "sender_name", "value": "AI", "display_name": "Sender Name", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "Name of the sender.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "session_id": { "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": "session_id", "value": "", "display_name": "Session ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "should_store_message": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "should_store_message", "value": true, "display_name": "Store Messages", "advanced": true, "api_editable": false, "dynamic": false, "info": "Store the message in the history.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" } }, "description": "Display a chat message in the Playground.", "icon": "MessagesSquare", "base_classes": [ "Message" ], "display_name": "Chat Output", "documentation": "https://docs.langflow.org/chat-input-and-output", "minimized": true, "custom_fields": {}, "output_types": [], "pinned": false, "conditional_paths": [], "frozen": false, "outputs": [ { "types": [ "Message" ], "selected": "Message", "name": "message", "display_name": "Output Message", "method": "message_response", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": false, "tool_mode": true } ], "field_order": [], "beta": false, "legacy": false, "edited": false, "metadata": {}, "tool_mode": false }, "type": "ChatOutput", "id": "ChatOutput-ok", "showNode": true }, "id": "ChatOutput-ok", "measured": { "width": 320, "height": 200 }, "position": { "x": 1200, "y": 60 }, "type": "genericNode" }, { "data": { "node": { "template": { "_type": "Component", "input_value": { "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": true, "placeholder": "", "show": true, "name": "input_value", "value": "", "display_name": "Inputs", "advanced": false, "api_editable": false, "input_types": [ "Data", "JSON", "DataFrame", "Table", "Message" ], "dynamic": false, "info": "Message to be passed as output.", "title_case": false, "track_in_telemetry": false, "type": "other", "_input_type": "HandleInput" }, "clean_data": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "clean_data", "value": true, "display_name": "Basic Clean Data", "advanced": true, "api_editable": false, "dynamic": false, "info": "Whether to clean data before converting to string.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" }, "code": { "type": "code", "required": true, "placeholder": "", "list": false, "show": true, "multiline": true, "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", "fileTypes": [], "file_path": "", "password": false, "name": "code", "advanced": true, "api_editable": false, "dynamic": true, "info": "", "load_from_db": false, "title_case": false }, "context_id": { "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": "context_id", "value": "", "display_name": "Context ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The context ID of the chat. Adds an extra layer to the local memory.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "data_template": { "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": "data_template", "value": "{text}", "display_name": "Data Template", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "sender": { "tool_mode": false, "trace_as_metadata": true, "options": [ "Machine", "User" ], "options_metadata": [], "combobox": false, "dialog_inputs": {}, "toggle": false, "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "sender", "value": "Machine", "display_name": "Sender Type", "advanced": true, "api_editable": false, "dynamic": false, "info": "Type of sender.", "title_case": false, "track_in_telemetry": true, "external_options": {}, "type": "str", "_input_type": "DropdownInput" }, "sender_name": { "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": "sender_name", "value": "AI", "display_name": "Sender Name", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "Name of the sender.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "session_id": { "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": "session_id", "value": "", "display_name": "Session ID", "advanced": true, "api_editable": false, "input_types": [ "Message" ], "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", "title_case": false, "track_in_telemetry": false, "type": "str", "_input_type": "MessageTextInput" }, "should_store_message": { "tool_mode": false, "trace_as_metadata": true, "list": false, "list_add_label": "Add More", "override_skip": false, "required": false, "placeholder": "", "show": true, "name": "should_store_message", "value": true, "display_name": "Store Messages", "advanced": true, "api_editable": false, "dynamic": false, "info": "Store the message in the history.", "title_case": false, "track_in_telemetry": true, "type": "bool", "_input_type": "BoolInput" } }, "description": "Display a chat message in the Playground.", "icon": "MessagesSquare", "base_classes": [ "Message" ], "display_name": "Chat Output", "documentation": "https://docs.langflow.org/chat-input-and-output", "minimized": true, "custom_fields": {}, "output_types": [], "pinned": false, "conditional_paths": [], "frozen": false, "outputs": [ { "types": [ "Message" ], "selected": "Message", "name": "message", "display_name": "Output Message", "method": "message_response", "value": "__UNDEFINED__", "cache": true, "allows_loop": false, "group_outputs": false, "tool_mode": true } ], "field_order": [], "beta": false, "legacy": false, "edited": false, "metadata": {}, "tool_mode": false }, "type": "ChatOutput", "id": "ChatOutput-sonder", "showNode": true }, "id": "ChatOutput-sonder", "measured": { "width": 320, "height": 200 }, "position": { "x": 1200, "y": 340 }, "type": "genericNode" } ], "edges": [ { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", "id": "ChatInput-entry", "name": "message", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "payload", "id": "CustomComponent-rule", "inputTypes": [ "Message" ], "type": "str" } }, "id": "xy-edge__{œdataTypeœ: œChatInputœ, œidœ: œChatInput-entryœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}-{œfieldNameœ: œpayloadœ, œidœ: œCustomComponent-ruleœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}", "selected": false, "source": "ChatInput-entry", "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-entryœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", "target": "CustomComponent-rule", "targetHandle": "{œfieldNameœ: œpayloadœ, œidœ: œCustomComponent-ruleœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "CustomComponent", "id": "CustomComponent-rule", "name": "verdict", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_text", "id": "ConditionalRouter-router", "inputTypes": [ "Message" ], "type": "str" } }, "id": "xy-edge__{œdataTypeœ: œCustomComponentœ, œidœ: œCustomComponent-ruleœ, œnameœ: œverdictœ, œoutput_typesœ: [œMessageœ]}-{œfieldNameœ: œinput_textœ, œidœ: œConditionalRouter-routerœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}", "selected": false, "source": "CustomComponent-rule", "sourceHandle": "{œdataTypeœ: œCustomComponentœ, œidœ: œCustomComponent-ruleœ, œnameœ: œverdictœ, œoutput_typesœ: [œMessageœ]}", "target": "ConditionalRouter-router", "targetHandle": "{œfieldNameœ: œinput_textœ, œidœ: œConditionalRouter-routerœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ConditionalRouter", "id": "ConditionalRouter-router", "name": "false_result", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-ok", "inputTypes": [ "Data", "JSON", "DataFrame", "Table", "Message" ], "type": "other" } }, "id": "xy-edge__{œdataTypeœ: œConditionalRouterœ, œidœ: œConditionalRouter-routerœ, œnameœ: œfalse_resultœ, œoutput_typesœ: [œMessageœ]}-{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-okœ, œinputTypesœ: [œDataœ, œJSONœ, œDataFrameœ, œTableœ, œMessageœ], œtypeœ: œotherœ}", "selected": false, "source": "ConditionalRouter-router", "sourceHandle": "{œdataTypeœ: œConditionalRouterœ, œidœ: œConditionalRouter-routerœ, œnameœ: œfalse_resultœ, œoutput_typesœ: [œMessageœ]}", "target": "ChatOutput-ok", "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-okœ, œinputTypesœ: [œDataœ, œJSONœ, œDataFrameœ, œTableœ, œMessageœ], œtypeœ: œotherœ}" }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ConditionalRouter", "id": "ConditionalRouter-router", "name": "true_result", "output_types": [ "Message" ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-sonder", "inputTypes": [ "Data", "JSON", "DataFrame", "Table", "Message" ], "type": "other" } }, "id": "xy-edge__{œdataTypeœ: œConditionalRouterœ, œidœ: œConditionalRouter-routerœ, œnameœ: œtrue_resultœ, œoutput_typesœ: [œMessageœ]}-{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-sonderœ, œinputTypesœ: [œDataœ, œJSONœ, œDataFrameœ, œTableœ, œMessageœ], œtypeœ: œotherœ}", "selected": false, "source": "ConditionalRouter-router", "sourceHandle": "{œdataTypeœ: œConditionalRouterœ, œidœ: œConditionalRouter-routerœ, œnameœ: œtrue_resultœ, œoutput_typesœ: [œMessageœ]}", "target": "ChatOutput-sonder", "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-sonderœ, œinputTypesœ: [œDataœ, œJSONœ, œDataFrameœ, œTableœ, œMessageœ], œtypeœ: œotherœ}" } ], "viewport": { "x": 0, "y": 0, "zoom": 1 } }, "description": "Regelpruefung ohne Seiteneffekt (Spike-Fixture, kein LLM).", "endpoint_name": null, "icon": null, "icon_bg_color": null, "id": "5adedb2b-d50b-5c3f-9951-2e76b8b94869", "is_component": false, "locked": false, "mcp_enabled": false, "name": "simple_rule", "tags": [], "webhook": false }