{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "dd511e76-27a0-47a1-b475-d00fffa22eb8",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "position": [
        -1280,
        120
      ],
      "parameters": {
        "updates": [
          "message"
        ],
        "additionalFields": {}
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "fef3c7b6-ac57-4b83-8f21-2babd81d3b50",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1280,
        320
      ],
      "parameters": {
        "text": "={{\n$('Session Ref').first().json.data.messages?.parseJson()\n  .map(msg => msg.value)\n  .join(' ')\n}}",
        "options": {
          "systemMessage": "Do not format your response in markdown, use plain text instead."
        },
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "084bfb67-c59b-44c4-a310-4c25743cd8f9",
      "name": "Google Gemini Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        1260,
        520
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-preview-05-20"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "4733b8d3-427d-4724-a5a3-7eb787941494",
      "name": "Redis Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryRedisChat",
      "position": [
        1420,
        520
      ],
      "parameters": {
        "sessionKey": "=session/{{ $('Session Ref').first().json.data.from }}/agent",
        "sessionIdType": "customKey",
        "contextWindowLength": 10
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.5
    },
    {
      "id": "8f7e6148-b1a5-46b5-934a-0c75f20898d8",
      "name": "Get Values",
      "type": "n8n-nodes-base.set",
      "position": [
        -860,
        120
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "7cee3033-0615-43ac-93fd-5397c759e6a3",
              "name": "from",
              "type": "string",
              "value": "={{ $json.message.chat.id }}"
            },
            {
              "id": "c2bb1875-7074-411f-a14a-42c699e5bdc1",
              "name": "message",
              "type": "string",
              "value": "={{ $json.message.text }}"
            },
            {
              "id": "34d5087e-79be-4181-b839-0a55b95133e8",
              "name": "createdAt",
              "type": "string",
              "value": "={{ $now.toISO() }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "52cc0bd2-bb0a-4c2c-a1a5-2a9104d891da",
      "name": "Update Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -920,
        700
      ],
      "parameters": {
        "key": "=session/{{ $('Prediction Subworkflow').item.json.from }}",
        "value": "={{\n{\n  ...($('Get Session').item.json.data ?? {}),\n  messages: [\n    ...($('Get Session').item.json.data?.messages\n      ? $('Get Session').item.json.data?.messages.parseJson()\n      : []\n    ),\n    {\n      value: $('Prediction Subworkflow').item.json.message,\n      createdAt: $('Prediction Subworkflow').item.json.createdAt\n    }\n  ].toJsonString(),\n  from: $('Prediction Subworkflow').item.json.from\n}\n}}",
        "expire": true,
        "keyType": "hash",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "7bdee33c-a2e7-49a3-b782-d045f3f46dd3",
      "name": "Get Session",
      "type": "n8n-nodes-base.redis",
      "position": [
        -1100,
        700
      ],
      "parameters": {
        "key": "=session/{{ $json.from }}",
        "keyType": "hash",
        "options": {},
        "operation": "get",
        "propertyName": "data"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "0fc0b9eb-3553-4fdb-befc-7d7891305405",
      "name": "Is Waiting?",
      "type": "n8n-nodes-base.if",
      "position": [
        -80,
        1020
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "0e37d8ff-3d04-4224-84cc-defa80dc4a8b",
              "operator": {
                "name": "filter.operator.equals",
                "type": "string",
                "operation": "equals"
              },
              "leftValue": "={{ $('Get Session').item.json.data?.state }}",
              "rightValue": "waiting"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "de3eb7bb-81e0-4169-a6f4-286a1ac9a2f7",
      "name": "Update Session1",
      "type": "n8n-nodes-base.redis",
      "position": [
        40,
        300
      ],
      "parameters": {
        "key": "=session/{{ $('Session Ref').first().json.data.from }}",
        "value": "={{\n{\n  state: 'waiting',\n  resumeUrl: $execution.resumeUrl\n}\n}}",
        "expire": true,
        "keyType": "hash",
        "operation": "set"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "c7fb3fba-9d6a-4c68-a995-5539ab6bcf63",
      "name": "is New Session?",
      "type": "n8n-nodes-base.if",
      "position": [
        -560,
        700
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "71db97f1-e5ba-49d1-8a9a-b9df3a989c77",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ !$json.data?.state }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "8bbb222c-d3d1-4c03-931c-37f1d2d37df2",
      "name": "Google Gemini Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "position": [
        300,
        460
      ],
      "parameters": {
        "options": {},
        "modelName": "models/gemini-2.5-flash-lite-preview-06-17"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3a285fef-8c80-48dc-8e79-2c30ff9d351c",
      "name": "Get Session2",
      "type": "n8n-nodes-base.redis",
      "position": [
        720,
        460
      ],
      "parameters": {
        "key": "=session/{{ $('Session Ref').first().json.data.from }}",
        "keyType": "hash",
        "options": {},
        "operation": "get",
        "propertyName": "data"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "e8254612-ff46-4165-ab93-a6c9d7a2c583",
      "name": "Session Ref",
      "type": "n8n-nodes-base.noOp",
      "position": [
        -120,
        300
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "bdd227f1-65fd-46d0-8e0c-23e812f83967",
      "name": "Session Ended?",
      "type": "n8n-nodes-base.if",
      "position": [
        880,
        460
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "398277f7-9386-4f99-ab9c-f99fe59c872e",
              "operator": {
                "type": "object",
                "operation": "empty",
                "singleValue": true
              },
              "leftValue": "={{ Object.assign({}, $json.data ?? {}) }}",
              "rightValue": ""
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a9611069-9cf9-4090-aa6e-cc6a3f4a0fa8",
      "name": "Wait For Webhook",
      "type": "n8n-nodes-base.wait",
      "position": [
        560,
        460
      ],
      "parameters": {
        "resume": "webhook",
        "options": {},
        "resumeUnit": "seconds",
        "resumeAmount": 20,
        "limitWaitTime": true
      },
      "typeVersion": 1.1
    },
    {
      "id": "456d5b81-7eee-49fd-9b27-93e9e8fbe7b8",
      "name": "Update Session2",
      "type": "n8n-nodes-base.redis",
      "position": [
        1620,
        320
      ],
      "parameters": {
        "key": "=session/{{ $('Session Ref').first().json.data.from }}",
        "operation": "delete"
      },
      "credentials": {
        "redis": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "d778edf7-4152-4bd9-a720-a0a28db45369",
      "name": "Is Bot command?",
      "type": "n8n-nodes-base.if",
      "position": [
        -1080,
        120
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "bff0d1a5-1d78-4af6-a643-b2fd28f10fba",
              "operator": {
                "type": "string",
                "operation": "startsWith"
              },
              "leftValue": "={{ $json.message.text }}",
              "rightValue": "/"
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "a465cb15-8faf-42fb-b00e-51af15c596da",
      "name": "Predict End of Utterance",
      "type": "@n8n/n8n-nodes-langchain.textClassifier",
      "position": [
        200,
        300
      ],
      "parameters": {
        "options": {
          "systemPromptTemplate": "=You are an NLP expert who predicts whether a message is the end of an turn/utterance i.e., whether the user's message is complete or if more parts of the message are likely to follow. Help predict the given message or partial message to determine whether it is now the agent's turn to respond to the user.\n\n## Techniques to use\n* Temporal Threshold (Inactivity Delay) - Use a decay model\u2014each consecutive message shortens the threshold unless punctuation suggests completeness.\n* Punctuation Patterns - Strong indicators of message completeness: Ends with period ., question mark ?, or exclamation mark !. Ends with an emoji or newline after a complete sentence. Weak or uncertain indicators: Ellipsis ... \u2192 often implies continuation. Dash - or incomplete sentence \u2192 wait for more.\n* Linguistic Features - Lexical cues (last word, punctuation). Part-of-speech tags (is the sentence syntactically complete?). Message length and position in sequence. Semantic cues like discourse markers: \"anyway\", \"so\", \"that's all\"\n* Conversational Cues (Pragmatic Signals) - \"That's it\", \"Done\", \"Hope that helps\", \"Thanks\" \u2192 End markers. \"Let me continue\", \"Hold on\", \"Part 2\" \u2192 Continuation markers.\n\n### Sample Dataset\nLabel: 1 = end of utterance, 0 = continuation\n* \"Here's the plan for tomorrow.\" = 1,\n* \"First, we meet at 9am.\" = 0,\n* \"Then we head to the venue\" = 0,\n* \"...and set everything up.\" = 0,\n* \"Let me know if you're free!\" = 1,\n* \"Part 1 of the idea is this\" = 0,\n* \"More to come\" = 0,\n* \"That's all!\" = 1,\n* \"Thanks!\" = 1\n* \"Next, we should discuss logistics\" = 0"
        },
        "inputText": "={{\n$('Session Ref').item.json.data.messages.parseJson()\n  .map(msg => `[${msg.createdAt}] ${msg.value}`)\n  .join(' ')\n}}",
        "categories": {
          "categories": [
            {
              "category": "end_of_utterance",
              "description": "The user has reached their end of turn or end of utterance"
            },
            {
              "category": "has_more_to_follow",
              "description": "The user has not finished their message or turn or reached their end of utterance"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "14dd091f-636b-4944-81b4-35da5f0f64b2",
      "name": "Respond to User",
      "type": "n8n-nodes-base.telegram",
      "position": [
        1800,
        320
      ],
      "parameters": {
        "text": "={{ $json.output }}",
        "chatId": "={{ $('Session Ref').first().json.data.from }}",
        "additionalFields": {
          "parse_mode": "HTML",
          "appendAttribution": false
        }
      },
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "b6ae259c-9fc4-481a-bd6d-f1b3a0dc2d28",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1360,
        -80
      ],
      "parameters": {
        "color": 7,
        "width": 1020,
        "height": 440,
        "content": "## 1. Accept Telegram messages\n[Read more about the Telegram Trigger](https://docs.n8n.io/integrations/builtin/trigger-nodes/n8n-nodes-base.telegramtrigger)\n\nThis template will only work with chat systems where not every incoming message needs a reply - this unfortunately rules out n8n's built in chat trigger. For demonstration purposes, we'll use Telegram and in this set up, we have designed it to capture as many messages from a single user as possible without blocking the execution."
      },
      "typeVersion": 1
    },
    {
      "id": "33c5c000-5d3e-459a-814e-f2e5669e9592",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1360,
        400
      ],
      "parameters": {
        "color": 7,
        "width": 1020,
        "height": 560,
        "content": "## 2. Determine If New Message\n[Learn more about the subworkflow trigger](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.executeworkflowtrigger)\n\nOur goal is ultimately to not \"interrupt\" the user. We do so by buffering presumed partial messages until we can predict that the user has reached the end of what they want to say. Before we can do that however, we need to first determine if the message received is a new message or \"utterance\". For this task, we'll use simple session/state management via Redis to create temporary data to collect each incoming utterance and track our progress."
      },
      "typeVersion": 1
    },
    {
      "id": "939f8f46-c7c1-468f-af83-2c4f15fca09b",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        780
      ],
      "parameters": {
        "color": 7,
        "width": 660,
        "height": 460,
        "content": "## 3. Prediction Loop Already Running? Let's Resume!\n[Read more about the HTTP node](https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.httprequest/)\n\nUsing the session data, we can check if a prediction loop is already running and if so, resume it rather than starting a new one. The HTTP request is used to trigger the \"resume_url\" of the prediction loop wait node."
      },
      "typeVersion": 1
    },
    {
      "id": "d36770f7-1992-4335-8e9d-01a73de44c0b",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -240,
        -60
      ],
      "parameters": {
        "color": 7,
        "width": 1340,
        "height": 800,
        "content": "## 2. Start of a New Utterance? Start the Prediction Wait Loop.\n[Learn more about the Text Classifier node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.text-classifier)\n\nPredicting when a user has finished their thought during a casual text or call is surprisingly difficult. Although some heuristics and natural language processing (NLP) tools exist, I wanted to explore whether large language models (LLMs) could effectively handle this task.\n\nI implemented a basic text classifier and fine-tuned its prompt to predict message completion. If the model determines a message is incomplete, the workflow pauses using a wait node. This node doesn't operate on a timer; instead, it needs to be triggered by a HTTP request to resume. The trick is when we get new messages, we use those executions to resume the paused wait node if it exists. Once the wait node resumes, it combined the new message with the previous messages and sends them to the classifier for a new prediction.\n\nThe loop continues until the model identifies the message as complete, at which point the full conversation is sent to the agent. As a fallback, if the session is somehow lost - maybe due to expiry, the loop is abandoned and the new chat message is sent to the agent instead."
      },
      "typeVersion": 1
    },
    {
      "id": "d9dcc03f-8549-495e-969b-82c0f96c3057",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1160,
        80
      ],
      "parameters": {
        "color": 7,
        "width": 840,
        "height": 660,
        "content": "## 4. Send Reply Only When End of Utterance is Detected\n[Read more about the AI Agent node](https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.agent/)\n\nFinally! With the buffered messages combined and sent to the agent, we're now able to generate a reply to the user. The end experience is seemingly much closer to a natural conversation with a human as there is no artificial delays needed.\n\nThe last thing to do then is to clear out the temporary session for the utterance. This allows our workflow to recognise the next chat message as a new utterance."
      },
      "typeVersion": 1
    },
    {
      "id": "c242729b-0d95-4201-adee-b3a537d28eb1",
      "name": "Resume Wait Node",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        140,
        1020
      ],
      "parameters": {
        "url": "={{ $json.data.resumeUrl }}",
        "options": {}
      },
      "typeVersion": 4.2
    },
    {
      "id": "321e48ff-9389-408f-af97-e1590ce13e0c",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1860,
        -300
      ],
      "parameters": {
        "width": 460,
        "height": 1260,
        "content": "## Try It Out!\n### This n8n template demonstrates one approach to achieve a more natural and less frustration conversations with AI agents by reducing interrupts by predicting the end of user utterances.\n\nWhen we text or chat casually, it's not uncommon to break our sentences over multiple messages or when it comes to voice, break our speech with the odd pause or umms and ahhs. If an agent replies to every message, it's likely to interrupt us before we finish our thoughts and it can get very annoying!\n\nPreviously, I demonstrated a [simple technique for buffering each incoming message by 5 seconds](https://n8n.io/workflows/2346-enhance-customer-chat-by-buffering-messages-with-twilio-and-redis/) but that approach still suffers in some scenarios when more time is needed. This technique has no arbitrary time limit and instead uses AI to figure out when its the agent's turn based on the user's message, allowing for the user to take all the time they need.\n\n### How it works\n* Telegram messages are received but no reply is generated for them by default. Instead they are sent to the prediction subworkflow to determine if a reply should be generated.\n* The prediction subworkflow begins by checking Redis for the current user's prediction session state. If this is a new \"utterance\", it kicks off the \"predict end of utterance\" loop - the purpose of which is to buffer messages in a smart way!\n* New users message can continue to be accepted by the workflow until enough is collected to allow our prediction classifier to determine the end of the utterance has been reached.\n* The loop is then broken and the buffered chat messages are combined and sent to the AI agent to generate a response and sent to the user via the telegram node.\n* The prediction session state is then deleted to signal the workflow is ready to start again with a new message.\n\n### How to use\n* This system sits between your preferred chat platform and the AI agent so all you need to do is replace the telegram nodes as required.\n* Where LLM-only prediction isn't working well enough, consider more traditional code-based checking of heuristics to improve the detection.\n* Ideally you'll want a fast but accurate LLM so your user isn't waiting longer than they have to - at time of writing Gemini-2.5-flash-lite was the fastest in testing but keep a look out for smaller and more powerful LLMs in the future.\n\n### Requirements\n* Gemini for LLM\n* Redis for session management\n* Telegram for chat platform\n\n### Need Help?\nJoin the [Discord](https://discord.com/invite/XPKeKXeB7d) or ask in the [Forum](https://community.n8n.io/)!\n\nHappy Hacking!"
      },
      "typeVersion": 1
    },
    {
      "id": "f56fe585-7ece-49aa-813a-2d72b11d5d0f",
      "name": "Call Prediction Subworkflow",
      "type": "n8n-nodes-base.executeWorkflow",
      "position": [
        -640,
        120
      ],
      "parameters": {
        "options": {
          "waitForSubWorkflow": false
        },
        "workflowId": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $workflow.id }}"
        },
        "workflowInputs": {
          "value": {
            "from": "={{ $json.from }}",
            "message": "={{ $json.message }}",
            "createdAt": "={{ $json.createdAt }}"
          },
          "schema": [
            {
              "id": "from",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "from",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "message",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "message",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            },
            {
              "id": "createdAt",
              "type": "string",
              "display": true,
              "removed": false,
              "required": false,
              "displayName": "createdAt",
              "defaultMatch": false,
              "canBeUsedToMatch": true
            }
          ],
          "mappingMode": "defineBelow",
          "matchingColumns": [],
          "attemptToConvertTypes": false,
          "convertFieldsToString": true
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "6d24bc9e-223e-48f8-b32d-84cabf1febfc",
      "name": "Prediction Subworkflow",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [
        -1280,
        700
      ],
      "parameters": {
        "workflowInputs": {
          "values": [
            {
              "name": "from"
            },
            {
              "name": "message"
            },
            {
              "name": "createdAt"
            }
          ]
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "f711ff8e-4b68-4379-8abe-33bc673bab1f",
      "name": "Get Updated Session Values",
      "type": "n8n-nodes-base.set",
      "position": [
        -740,
        700
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ed66f412-aa70-4838-9295-bed652d62bfd",
              "name": "data",
              "type": "object",
              "value": "={{\n{\n  ...($('Get Session').item.json.data ?? {}),\n  messages: [\n    ...($('Get Session').item.json.data?.messages\n      ? $('Get Session').item.json.data?.messages.parseJson()\n      : []\n    ),\n    {\n      value: $('Prediction Subworkflow').item.json.message,\n      createdAt: $('Prediction Subworkflow').item.json.createdAt\n    }\n  ].toJsonString(),\n  from: $('Prediction Subworkflow').item.json.from\n}\n}}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    }
  ],
  "connections": {
    "AI Agent": {
      "main": [
        [
          {
            "node": "Update Session2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Values": {
      "main": [
        [
          {
            "node": "Call Prediction Subworkflow",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session": {
      "main": [
        [
          {
            "node": "Update Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Waiting?": {
      "main": [
        [
          {
            "node": "Resume Wait Node",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "Session Ref": {
      "main": [
        [
          {
            "node": "Update Session1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Session2": {
      "main": [
        [
          {
            "node": "Session Ended?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Session Ended?": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Session Ref",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Session": {
      "main": [
        [
          {
            "node": "Get Updated Session Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Bot command?": {
      "main": [
        [],
        [
          {
            "node": "Get Values",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Respond to User": {
      "main": [
        []
      ]
    },
    "Update Session1": {
      "main": [
        [
          {
            "node": "Predict End of Utterance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Session2": {
      "main": [
        [
          {
            "node": "Respond to User",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "is New Session?": {
      "main": [
        [
          {
            "node": "Session Ref",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Is Waiting?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Is Bot command?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait For Webhook": {
      "main": [
        [
          {
            "node": "Get Session2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Redis Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Prediction Subworkflow": {
      "main": [
        [
          {
            "node": "Get Session",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Predict End of Utterance": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Wait For Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Google Gemini Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Predict End of Utterance",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Get Updated Session Values": {
      "main": [
        [
          {
            "node": "is New Session?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Call Prediction Subworkflow": {
      "main": [
        []
      ]
    }
  }
}