{
  "name": "legal_rag_telegram_api_current_github_ready",
  "nodes": [
    {
      "parameters": {
        "updates": [
          "message",
          "callback_query"
        ],
        "additionalFields": {}
      },
      "id": "343b97d5-02b2-4853-9dab-692872f64994",
      "name": "Telegram Trigger",
      "type": "n8n-nodes-base.telegramTrigger",
      "typeVersion": 1.2,
      "position": [
        46928,
        12496
      ],
      "credentials": {
        "telegramApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const msg = $json.message || {};\nconst cb = $json.callback_query || {};\nconst doc = msg?.document || null;\n\nreturn [{\n  json: {\n    chat_id: msg?.chat?.id || cb?.message?.chat?.id || null,\n    input_text: (msg?.text || '').trim(),\n    callback_data: cb?.data || '',\n    callback_id: cb?.id || '',\n    telegram_file_id: doc?.file_id || '',\n    telegram_document_name: doc?.file_name || '',\n    telegram_mime_type: doc?.mime_type || '',\n    has_document: !!doc\n  },\n  binary: $binary\n}];"
      },
      "id": "3902c56f-19e6-4a53-ab07-25bedc9d9400",
      "name": "Normalize Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        47312,
        12384
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($json.chat_id || 'unknown');\nconst state = states[chatId] || {};\n\nreturn [{\n  json: {\n    ...$json,\n    api_base_url: $env.N8N_API_BASE_URL || 'http://host.docker.internal:8000/api',\n    rag_base_url: $env.N8N_RAG_BASE_URL || 'http://rag:8001',\n    llm_base_url: $env.N8N_LLM_BASE_URL || 'http://llm:8002',\n    rag_top_k: Number($env.N8N_RAG_TOP_K || 5),\n    active_case_id: state.active_case_id || '',\n    pending_action: state.pending_action || ''\n  },\n  binary: $binary\n}];"
      },
      "id": "9cbc8406-2fe9-4d57-9d37-68ed0af5518b",
      "name": "Load State",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        47552,
        12384
      ]
    },
    {
      "parameters": {
        "jsCode": "const text = ($json.input_text || '').trim();\nconst cb = $json.callback_data || '';\nconst pending = $json.pending_action || '';\nconst hasActive = !!$json.active_case_id;\nconst hasDocument = !!$json.has_document;\n\nlet route = 'UNKNOWN';\nlet selected_case_id = '';\nlet selected_doc_id = '';\n\nif (text === '/start' || text === '/menu' || cb === 'BACK_MAIN') {\n  route = 'START';\n} else if (cb === 'BACK_LIST_CASES') {\n  route = 'LIST_CASES';\n} else if (cb === 'BACK_CASE_MENU') {\n  route = hasActive ? 'STATUS' : 'NO_ACTIVE_CASE';\n} else if (text === '/new_case' || text === '/newcase' || cb === 'MENU_NEW_CASE') {\n  route = 'NEW_CASE';\n} else if (pending === 'waiting_case_title' && text && !text.startsWith('/')) {\n  route = 'SUBMIT_NEW_CASE';\n} else if (text === '/list_cases' || text === '/listcases' || cb === 'MENU_LIST_CASES' || cb === 'CASE_LIST') {\n  route = 'LIST_CASES';\n} else if (cb.startsWith('CASE_SELECT:')) {\n  route = 'SELECT_CASE';\n  selected_case_id = cb.split(':')[1] || '';\n} else if (text.startsWith('/select ')) {\n  route = 'SELECT_CASE';\n  selected_case_id = text.replace('/select', '').trim();\n} else if (text === '/status' || cb === 'CASE_STATUS') {\n  route = hasActive ? 'STATUS' : 'NO_ACTIVE_CASE';\n} else if (cb === 'CASE_LIST_DOCS') {\n  route = hasActive ? 'LIST_DOCS' : 'NO_ACTIVE_CASE';\n} else if (cb === 'CASE_DELETE_DOC') {\n  route = hasActive ? 'DELETE_DOC' : 'NO_ACTIVE_CASE';\n} else if (cb.startsWith('DOC_DELETE:')) {\n  route = hasActive ? 'CONFIRM_DELETE_DOC' : 'NO_ACTIVE_CASE';\n  selected_doc_id = cb.split(':')[1] || '';\n} else if (text === '/add_doc' || text === '/adddoc' || cb === 'CASE_ADD_DOC') {\n  route = hasActive ? 'ADD_DOC' : 'NO_ACTIVE_CASE';\n} else if (pending === 'waiting_document' && hasDocument) {\n  route = hasActive ? 'SUBMIT_ADD_DOC' : 'NO_ACTIVE_CASE';\n} else if (text === '/ask' || cb === 'CASE_ASK') {\n  route = hasActive ? 'ASK' : 'NO_ACTIVE_CASE';\n} else if (pending === 'waiting_question' && text && !text.startsWith('/')) {\n  route = hasActive ? 'SUBMIT_ASK' : 'NO_ACTIVE_CASE';\n}\n\nreturn [{\n  json: {\n    ...$json,\n    route,\n    selected_case_id,\n    selected_doc_id\n  },\n  binary: $binary\n}];"
      },
      "id": "e61f36ff-4e44-463e-824c-c638d1f508f7",
      "name": "Determine Route",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        47792,
        12384
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "START",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "NEW_CASE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "SUBMIT_NEW_CASE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "LIST_CASES",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "SELECT_CASE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "STATUS",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "LIST_DOCS",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "DELETE_DOC",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "CONFIRM_DELETE_DOC",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "ADD_DOC",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "SUBMIT_ADD_DOC",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "ASK",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "SUBMIT_ASK",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "NO_ACTIVE_CASE",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{$json.route}}",
                    "rightValue": "UNKNOWN",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "b0560bf0-46dd-4c0a-81e3-d75085036788",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        48032,
        12384
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($json.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = '';\ndata.states = states;\n\nreturn [{ json: { chat_id: $json.chat_id, message_text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u043b\u043e', callback_data: 'MENU_NEW_CASE' }, { text: '\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b', callback_data: 'MENU_LIST_CASES' }]] }) } }];"
      },
      "id": "393d3616-e51e-4452-b7bf-1739648b6291",
      "name": "Prepare Start Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        11936
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "6fd0d545-6882-4110-84ed-33dcc9829e6c",
      "name": "Send Start Menu",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        11936
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($json.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].active_case_id = states[chatId].active_case_id || '';\nstates[chatId].pending_action = 'waiting_case_title';\ndata.states = states;\nreturn [{ json: { chat_id: $json.chat_id, message_text: '\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u0434\u0435\u043b\u0430 \u043e\u0434\u043d\u0438\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_MAIN' }]] }) } }];"
      },
      "id": "ec15aa73-c16c-4e69-aae1-28163d951958",
      "name": "Set Waiting Case Title",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        12064
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "7d462a8c-d631-46a6-a38c-9b1b87cab449",
      "name": "Ask Case Title",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        12064
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.api_base_url}}/cases",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Accept",
              "value": "application/json"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"chat_id\": {{$json.chat_id}},\n  \"title\": \"{{$json.input_text}}\"\n}",
        "options": {
          "redirect": {},
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "2930347d-49a1-4328-bcba-07cbc56b5715",
      "name": "API Create Case",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12176
      ]
    },
    {
      "parameters": {
        "jsCode": "const resp = $json || {};\nconst caseId = String(resp.id || resp.case_id || '');\nconst title = $item(0).$node['Determine Route'].json.input_text || '\u0411\u0435\u0437 \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u044f';\n\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($item(0).$node['Load State'].json.chat_id || 'unknown');\n\nstates[chatId] = states[chatId] || {};\nstates[chatId].active_case_id = caseId;\nstates[chatId].pending_action = '';\nstates[chatId].cases = Array.isArray(states[chatId].cases) ? states[chatId].cases : [];\nstates[chatId].case_titles = states[chatId].case_titles || {};\n\nconst existing = states[chatId].cases.find(c => String(c.case_id || c.id || '') === caseId);\nconst localCase = {\n  case_id: caseId,\n  title,\n  chat_id: resp.chat_id || $item(0).$node['Load State'].json.chat_id\n};\n\nstates[chatId].case_titles[caseId] = title;\n\nif (existing) {\n  existing.title = localCase.title;\n  existing.chat_id = localCase.chat_id;\n} else {\n  states[chatId].cases.unshift(localCase);\n}\n\ndata.states = states;\n\nreturn [{\n  json: {\n    chat_id: $item(0).$node['Load State'].json.chat_id,\n    case_id: caseId,\n    title,\n    message_text: `\u0414\u0435\u043b\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u043e\\n\\n<b>${title}</b>\\nID: <code>${caseId}</code>`,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_LIST_CASES' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "3d26909f-1397-4764-929e-35c228d92a75",
      "name": "Save Created Case",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12176
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "a5e43f0a-d37f-415b-ba2c-ca56ee1f309f",
      "name": "Created Case Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12176
      ]
    },
    {
      "parameters": {
        "url": "={{$json.api_base_url}}/cases?chat_id={{$json.chat_id}}",
        "options": {
          "redirect": {},
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "b66c4be0-f6da-4d46-81dd-f52d8d696411",
      "name": "API List Cases",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12304
      ]
    },
    {
      "parameters": {
        "jsCode": "const payload = $json || {};\n\nlet apiCases = [];\nif (Array.isArray(payload.cases)) {\n  apiCases = payload.cases;\n} else if (Array.isArray(payload)) {\n  apiCases = payload;\n} else if (payload.case_id || payload.id) {\n  apiCases = [payload];\n}\n\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($item(0).$node['Load State'].json.chat_id || 'unknown');\n\nstates[chatId] = states[chatId] || {};\nstates[chatId].cases = Array.isArray(states[chatId].cases) ? states[chatId].cases : [];\nstates[chatId].case_titles = states[chatId].case_titles || {};\n\nconst localCases = states[chatId].cases;\nconst caseTitles = states[chatId].case_titles || {};\n\nconst isGenericTitle = (value, id) => {\n  const title = String(value || '').trim();\n  if (!title) return true;\n  if (/^\u0414\u0435\u043b\u043e\\s+\\d+$/i.test(title)) return true;\n  if (/^Case\\s+\\d+$/i.test(title)) return true;\n  if (id && title === `\u0414\u0435\u043b\u043e ${String(id).replace(/^case_/, '')}`) return true;\n  return false;\n};\n\nconst mergedCases = apiCases.map(c => {\n  const id = String(c.case_id || c.id || '');\n  const local = localCases.find(x => String(x.case_id || x.id || '') === id) || {};\n  const apiTitle = String(c.title || '');\n  const localTitle = String(local.title || caseTitles[id] || '');\n\n  const chosenTitle = isGenericTitle(apiTitle, id)\n    ? (localTitle || apiTitle || `\u0414\u0435\u043b\u043e ${String(id).replace(/^case_/, '')}`)\n    : apiTitle;\n\n  if (chosenTitle) {\n    caseTitles[id] = chosenTitle;\n  }\n\n  return {\n    case_id: id,\n    title: chosenTitle || `\u0414\u0435\u043b\u043e ${String(id).replace(/^case_/, '')}`,\n    documents_count: Number(c.documents_count ?? local.documents_count ?? 0)\n  };\n}).filter(c => c.case_id);\n\nstates[chatId].case_titles = caseTitles;\nstates[chatId].cases = mergedCases;\nstates[chatId].pending_action = '';\ndata.states = states;\n\nif (!mergedCases.length) {\n  return [{\n    json: {\n      chat_id: $item(0).$node['Load State'].json.chat_id,\n      message_text: '\u0423 \u0432\u0430\u0441 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442 \u0434\u0435\u043b\\n\\n\u041d\u0430\u0436\u043c\u0438\u0442\u0435 \u043a\u043d\u043e\u043f\u043a\u0443 \u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u043b\u043e',\n      reply_markup_json: JSON.stringify({\n        inline_keyboard: [\n          [{ text: '\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u043b\u043e', callback_data: 'MENU_NEW_CASE' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n        ]\n      })\n    }\n  }];\n}\n\nconst activeCaseId = String(states[chatId].active_case_id || '');\nconst rows = mergedCases.map(c => {\n  const marker = c.case_id === activeCaseId ? '\u2022 ' : '';\n  const suffix = c.documents_count ? ` \u00b7 \u0434\u043e\u043a.: ${c.documents_count}` : '';\n  return [{\n    text: `${marker}${c.title}${suffix}`.slice(0, 60),\n    callback_data: `CASE_SELECT:${c.case_id}`\n  }];\n});\n\nrows.push([{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_MAIN' }]);\n\nreturn [{\n  json: {\n    chat_id: $item(0).$node['Load State'].json.chat_id,\n    message_text: '\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u0435\u043b\u043e',\n    reply_markup_json: JSON.stringify({ inline_keyboard: rows })\n  }\n}];"
      },
      "id": "fed34fbb-c73b-4cd0-8197-0888d5e5a64b",
      "name": "Prepare Cases Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12304
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "2e1eb8cd-4161-4b92-90fd-aee6f9d6f93e",
      "name": "Send Cases Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12304
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { ...$json } }];"
      },
      "id": "42102d53-6281-47a5-83da-00e73c2d996f",
      "name": "API Activate Case",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        12416
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($item(0).$node['Load State'].json.chat_id || 'unknown');\nconst caseId = $item(0).$node['Determine Route'].json.selected_case_id || '';\n\nstates[chatId] = states[chatId] || {};\nstates[chatId].active_case_id = caseId;\nstates[chatId].pending_action = '';\nstates[chatId].case_titles = states[chatId].case_titles || {};\n\nconst cases = Array.isArray(states[chatId].cases) ? states[chatId].cases : [];\nconst selected = cases.find(c => String(c.case_id || c.id || '') === String(caseId)) || {};\ndata.states = states;\n\nconst title = selected.title || states[chatId].case_titles[caseId] || `\u0414\u0435\u043b\u043e ${String(caseId).replace(/^case_/, '')}`;\n\nreturn [{\n  json: {\n    chat_id: $item(0).$node['Load State'].json.chat_id,\n    message_text: `\u0410\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0434\u0435\u043b\u043e \u0432\u044b\u0431\u0440\u0430\u043d\u043e\\n\\n<b>${title}</b>\\nID: <code>${caseId}</code>`,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_LIST_CASES' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "fbcc3786-23c5-4204-bde4-3259475f50b7",
      "name": "Save Active Case",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12416
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "55395765-5578-44df-9bb1-fba103cde01f",
      "name": "Selected Case Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12416
      ]
    },
    {
      "parameters": {
        "url": "={{$json.api_base_url}}/cases/{{$json.active_case_id}}/status?chat_id={{$json.chat_id}}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "10ca272b-1333-4995-aad2-3eea0a25b109",
      "name": "API Case Status",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12544
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($item(0).$node['Load State'].json.chat_id || 'unknown');\nconst activeCaseId = String($item(0).$node['Load State'].json.active_case_id || '');\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = '';\nstates[chatId].case_titles = states[chatId].case_titles || {};\ndata.states = states;\nconst selected = (states[chatId]?.cases || []).find(c => String(c.case_id || c.id || '') === activeCaseId) || {};\nconst title = selected.title || states[chatId].case_titles[activeCaseId] || `\u0414\u0435\u043b\u043e ${String(activeCaseId).replace(/^case_/, '')}`;\n\nreturn [{\n  json: {\n    chat_id: $item(0).$node['Load State'].json.chat_id,\n    message_text: `\u0414\u0435\u043b\u043e\\n\\n<b>${title}</b>\\nID: <code>${activeCaseId}</code>`,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_LIST_CASES' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "039677e3-16e3-4f27-b176-b1fb9653d721",
      "name": "Prepare Status Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12544
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "a503bc0a-bf42-4bce-b8e7-b7b0dc4ad4ed",
      "name": "Send Status Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12544
      ]
    },
    {
      "parameters": {
        "url": "={{$json.api_base_url}}/cases/{{$json.active_case_id}}?chat_id={{$json.chat_id}}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "a415273f-8624-48f1-abe4-311574665014",
      "name": "API Get Case Details",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12656
      ]
    },
    {
      "parameters": {
        "jsCode": "const details = $json || {};\nconst load = $item(0).$node['Load State'].json;\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String(load.chat_id || 'unknown');\nconst activeCaseId = String(load.active_case_id || details.case_id || '');\nstates[chatId] = states[chatId] || {};\nstates[chatId].case_titles = states[chatId].case_titles || {};\nstates[chatId].pending_action = '';\n\nconst localCase = (states[chatId].cases || []).find(c => String(c.case_id || c.id || '') === activeCaseId) || {};\nconst apiTitle = String(details.title || '');\nconst localTitle = String(localCase.title || states[chatId].case_titles[activeCaseId] || '');\n\nconst isGenericTitle = (value, id) => {\n  const title = String(value || '').trim();\n  if (!title) return true;\n  if (/^\u0414\u0435\u043b\u043e\\s+\\d+$/i.test(title)) return true;\n  if (/^Case\\s+\\d+$/i.test(title)) return true;\n  if (id && title === `\u0414\u0435\u043b\u043e ${String(id).replace(/^case_/, '')}`) return true;\n  return false;\n};\n\nconst resolvedTitle = isGenericTitle(apiTitle, activeCaseId)\n  ? (localTitle || `\u0414\u0435\u043b\u043e ${String(activeCaseId).replace(/^case_/, '')}`)\n  : apiTitle;\n\nif (resolvedTitle) {\n  states[chatId].case_titles[activeCaseId] = resolvedTitle;\n  if (localCase && typeof localCase === 'object') {\n    localCase.title = resolvedTitle;\n  }\n  data.states = states;\n}\n\nconst title = String(resolvedTitle || `\u0414\u0435\u043b\u043e ${String(activeCaseId).replace(/^case_/, '')}`);\nconst docs = Array.isArray(details.documents) ? details.documents : [];\n\nconst escapeHtml = (value) => String(value || '')\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;');\n\nif (Number(details.status) === 404 || details.error === 'Not Found') {\n  return [{\n    json: {\n      chat_id: load.chat_id,\n      message_text: `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u0435\u043b\u0430\\n\\nID: ${escapeHtml(activeCaseId)}\\n\u041e\u0448\u0438\u0431\u043a\u0430 API: 404 Not Found`,\n      reply_markup_json: JSON.stringify({\n        inline_keyboard: [\n          [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n          [{ text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }, { text: '\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b', callback_data: 'CASE_LIST' }],\n          [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n        ]\n      })\n    }\n  }];\n}\n\nlet docsBlock = '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442.';\nif (docs.length) {\n  docsBlock = docs.map((doc, index) => {\n    const name = escapeHtml(doc.name || doc.filename || `\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 ${index + 1}`);\n    const docId = escapeHtml(doc.doc_id || doc.id || `doc_${index + 1}`);\n    const pages = doc.pages !== undefined && doc.pages !== null ? `, \u0441\u0442\u0440.: ${escapeHtml(doc.pages)}` : '';\n    return `${index + 1}. ${name}\\nID: ${docId}${pages}`;\n  }).join('\\n\\n');\n}\n\nreturn [{\n  json: {\n    chat_id: load.chat_id,\n    message_text: `\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u0435\u043b\u0430\\n\\n<b>${escapeHtml(title)}</b>\\nID: ${escapeHtml(activeCaseId)}\\n\\n${docsBlock}`,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n        [{ text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }, { text: '\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b', callback_data: 'CASE_LIST' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "410ddf57-6736-4760-aa45-aee37ce497a6",
      "name": "Prepare Docs Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12656
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "329e23a1-4c89-4779-ad30-fb2f6c0f1ab1",
      "name": "Send Docs Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12656
      ]
    },
    {
      "parameters": {
        "url": "={{$json.api_base_url}}/cases/{{$json.active_case_id}}?chat_id={{$json.chat_id}}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "6dd5541e-262e-4a52-b4e9-870e621c4ad2",
      "name": "API Get Case Details For Delete",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12784
      ]
    },
    {
      "parameters": {
        "jsCode": "const details = $json || {};\nconst load = $item(0).$node['Load State'].json;\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String(load.chat_id || 'unknown');\nconst activeCaseId = String(load.active_case_id || details.case_id || '');\nstates[chatId] = states[chatId] || {};\nstates[chatId].case_titles = states[chatId].case_titles || {};\nstates[chatId].pending_action = '';\n\nconst localCase = (states[chatId].cases || []).find(c => String(c.case_id || c.id || '') === activeCaseId) || {};\nconst apiTitle = String(details.title || '');\nconst localTitle = String(localCase.title || states[chatId].case_titles[activeCaseId] || '');\n\nconst isGenericTitle = (value, id) => {\n  const title = String(value || '').trim();\n  if (!title) return true;\n  if (/^\u0414\u0435\u043b\u043e\\s+\\d+$/i.test(title)) return true;\n  if (/^Case\\s+\\d+$/i.test(title)) return true;\n  if (id && title === `\u0414\u0435\u043b\u043e ${String(id).replace(/^case_/, '')}`) return true;\n  return false;\n};\n\nconst resolvedTitle = isGenericTitle(apiTitle, activeCaseId)\n  ? (localTitle || `\u0414\u0435\u043b\u043e ${String(activeCaseId).replace(/^case_/, '')}`)\n  : apiTitle;\n\nif (resolvedTitle) {\n  states[chatId].case_titles[activeCaseId] = resolvedTitle;\n  if (localCase && typeof localCase === 'object') {\n    localCase.title = resolvedTitle;\n  }\n  data.states = states;\n}\n\nconst title = String(resolvedTitle || `\u0414\u0435\u043b\u043e ${String(activeCaseId).replace(/^case_/, '')}`);\nconst docs = Array.isArray(details.documents) ? details.documents : [];\n\nconst escapeHtml = (value) => String(value || '')\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;');\n\nif (Number(details.status) === 404 || details.error === 'Not Found') {\n  return [{\n    json: {\n      chat_id: load.chat_id,\n      message_text: `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\\n\\nID: ${escapeHtml(activeCaseId)}\\n\u041e\u0448\u0438\u0431\u043a\u0430 API: 404 Not Found`,\n      reply_markup_json: JSON.stringify({\n        inline_keyboard: [\n          [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n          [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n        ]\n      })\n    }\n  }];\n}\n\nif (!docs.length) {\n  return [{\n    json: {\n      chat_id: load.chat_id,\n      message_text: `\u0412 \u0434\u0435\u043b\u0435 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\\n\\n<b>${escapeHtml(title)}</b>\\nID: ${escapeHtml(activeCaseId)}`,\n      reply_markup_json: JSON.stringify({\n        inline_keyboard: [\n          [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }],\n          [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n        ]\n      })\n    }\n  }];\n}\n\nconst buttons = docs.slice(0, 50).map((doc, index) => {\n  const docId = String(doc.doc_id || doc.id || '');\n  const name = String(doc.name || doc.filename || `\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 ${index + 1}`);\n  return [{\n    text: `\u0423\u0434\u0430\u043b\u0438\u0442\u044c: ${name}`.slice(0, 60),\n    callback_data: `DOC_DELETE:${docId}`\n  }];\n});\n\nbuttons.push([{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }]);\nbuttons.push([{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]);\n\nreturn [{\n  json: {\n    chat_id: load.chat_id,\n    message_text: `\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u044f\\n\\n<b>${escapeHtml(title)}</b>\\nID: ${escapeHtml(activeCaseId)}`,\n    reply_markup_json: JSON.stringify({ inline_keyboard: buttons })\n  }\n}];"
      },
      "id": "d0d01518-af93-4ce0-ba36-a581894b6815",
      "name": "Prepare Delete Buttons",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12784
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "e5f7e141-d5c3-4564-8d40-bd1b63d27e4d",
      "name": "Send Delete Options",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12784
      ]
    },
    {
      "parameters": {
        "method": "DELETE",
        "url": "={{$json.api_base_url}}/cases/{{$json.active_case_id}}/documents/{{ String($json.selected_doc_id).replace('doc_', '') }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "34e6c035-eb91-4e95-90a0-f46a33a7b754",
      "name": "API Delete Document",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        12896
      ]
    },
    {
      "parameters": {
        "jsCode": "const buildRaw = $json.data ?? (typeof $json === 'string' ? $json : JSON.stringify($json));\nlet buildParsed = {};\ntry { buildParsed = typeof buildRaw === 'string' ? JSON.parse(buildRaw) : buildRaw; } catch (e) { buildParsed = {}; }\n\nconst prep = $item(0).$node['Prepare Delete Rebuild Request'].json;\nconst selectedDocId = String(prep.selected_doc_id || '');\n\nconst escapeHtml = (value) => String(value || '')\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;');\n\nconst ok = !!prep.delete_ok;\n\nconst buildLooksBad = (() => {\n  const text = String(buildRaw || '').toLowerCase();\n  if (!text) return false;\n  return text.includes('\\\"detail\\\"') || text.includes('\\\"error\\\"') || text.includes('traceback') || text.includes('exception');\n})();\n\nlet message_text;\n\nif (ok) {\n  const buildNote = buildLooksBad\n    ? '\\n\\n\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0443\u0434\u0430\u043b\u0451\u043d, \u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0432\u0435\u0440\u043d\u0443\u043b\u0430 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 RAG \u0441\u0435\u0440\u0432\u0438\u0441.'\n    : '\\n\\n\u041f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u0430.';\n  message_text = `\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0443\u0434\u0430\u043b\u0451\u043d\\n\\nID \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430: ${escapeHtml(selectedDocId)}${buildNote}`;\n} else {\n  message_text = `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\\n\\nID \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430: ${escapeHtml(selectedDocId)}\\n\\n\u041e\u0442\u0432\u0435\u0442 API:\\n<code>${escapeHtml(String(prep.delete_raw || '').slice(0, 1000))}</code>`;\n}\n\nreturn [{\n  json: {\n    chat_id: prep.chat_id,\n    message_text,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u0414\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_ADD_DOC' }, { text: '\u0423\u0434\u0430\u043b\u0438\u0442\u044c \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442', callback_data: 'CASE_DELETE_DOC' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "14022e35-ed94-4a36-ac79-9b146e4b2d85",
      "name": "Prepare Delete Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        12896
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "66ef9ee8-b26e-4933-935a-9dd29d5f1313",
      "name": "Send Delete Result",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        12896
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($json.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].active_case_id = states[chatId].active_case_id || $json.active_case_id || '';\nstates[chatId].pending_action = 'waiting_document';\ndata.states = states;\nreturn [{ json: { chat_id: $json.chat_id, message_text: '\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0442\u043f\u0440\u0430\u0432\u044c\u0442\u0435 PDF, DOCX \u0438\u043b\u0438 TXT \u043e\u0434\u043d\u0438\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }]] }) } }];"
      },
      "id": "550a94c4-559c-479f-b677-67a3e4166515",
      "name": "Set Waiting Document",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        13024
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "04ba4b19-1e0c-43a5-8ae8-fd6b8839d4e0",
      "name": "Prompt Document Upload",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        13024
      ]
    },
    {
      "parameters": {
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/getFile?file_id={{$json.telegram_file_id}}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "24e2d495-7c3d-4dbb-9102-e9040b1aa49a",
      "name": "Telegram Get File",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        13136
      ]
    },
    {
      "parameters": {
        "jsCode": "const result = $json.result || {};\nreturn [{ json: { ...$item(0).$node['Load State'].json, telegram_document_name: $item(0).$node['Determine Route'].json.telegram_document_name, telegram_mime_type: $item(0).$node['Determine Route'].json.telegram_mime_type, telegram_file_path: result.file_path || '', message_text: '' } }];"
      },
      "id": "89d4c8ec-f82b-4363-9a7c-4e82c2ddaead",
      "name": "Prepare Telegram File Path",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        13136
      ]
    },
    {
      "parameters": {
        "url": "=https://api.telegram.org/file/bot{{$env.TELEGRAM_BOT_TOKEN}}/{{$json.telegram_file_path}}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file",
              "outputPropertyName": "telegram_file"
            }
          }
        }
      },
      "id": "a5912de2-52a7-48dd-80de-222459c63327",
      "name": "Download Telegram File",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        13136
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\nreturn items.map(item => {\n  item.binary = item.binary || {};\n  if (item.binary.telegram_file) {\n    item.binary.telegram_file.fileName = item.json.telegram_document_name || 'document.bin';\n    item.binary.telegram_file.mimeType = item.json.telegram_mime_type || item.binary.telegram_file.mimeType || 'application/octet-stream';\n  }\n  return item;\n});"
      },
      "id": "60c9ae31-51db-495c-ae57-f7e07c212778",
      "name": "Attach Binary Metadata",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48992,
        13136
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.api_base_url}}/cases/{{$json.active_case_id}}/documents",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "file",
              "inputDataFieldName": "telegram_file"
            }
          ]
        },
        "options": {
          "redirect": {},
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true,
              "responseFormat": "text",
              "outputPropertyName": "data"
            }
          }
        }
      },
      "id": "ad76670b-ecb8-479c-98b6-490c49220cb6",
      "name": "HTTP Upload Document",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        49232,
        13136
      ]
    },
    {
      "parameters": {
        "jsCode": "const buildRaw = $json.data ?? (typeof $json === 'string' ? $json : JSON.stringify($json));\nconst upload = $item(0).$node['Prepare Build Request'].json;\nconst uploadOk = !!upload.upload_ok;\n\nconst escapeHtml = (value) => String(value || '')\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;');\n\nconst buildLooksBad = (() => {\n  const text = String(buildRaw || '').toLowerCase();\n  if (!text) return false;\n  return text.includes('\"detail\"') || text.includes('\"error\"') || text.includes('traceback') || text.includes('exception');\n})();\n\nlet text;\nif (!uploadOk) {\n  text = `\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0443 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\\n\\n\u041e\u0442\u0432\u0435\u0442 API:\\n<code>${escapeHtml(String(upload.upload_raw || '').slice(0, 700))}</code>`;\n} else {\n  const buildNote = buildLooksBad\n    ? '\\n\\n\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0441\u043e\u0445\u0440\u0430\u043d\u0451\u043d, \u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0432\u0435\u0440\u043d\u0443\u043b\u0430 \u043d\u0435\u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u0439 \u043e\u0442\u0432\u0435\u0442. \u041f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 RAG \u0441\u0435\u0440\u0432\u0438\u0441.'\n    : '\\n\\n\u041f\u0435\u0440\u0435\u0441\u0431\u043e\u0440\u043a\u0430 \u0438\u043d\u0434\u0435\u043a\u0441\u0430 \u0437\u0430\u043f\u0443\u0449\u0435\u043d\u0430.';\n  text = `\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\\n\\n\u0424\u0430\u0439\u043b: ${escapeHtml(upload.file_name)}\\n\u0414\u0435\u043b\u043e: ${escapeHtml(upload.active_case_id)}\\nID \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430: ${escapeHtml(upload.upload_doc_id || '\u043d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e')}${buildNote}`;\n}\n\nreturn [{\n  json: {\n    chat_id: upload.chat_id,\n    message_text: text,\n    reply_markup_json: JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    })\n  }\n}];"
      },
      "id": "ff3c5000-822b-4d99-8bb6-7412f7b688ce",
      "name": "Finalize Document Upload",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        49472,
        13136
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "chat_id",
              "value": "={{$json.chat_id}}"
            },
            {
              "name": "text",
              "value": "={{$json.message_text}}"
            },
            {
              "name": "reply_markup",
              "value": "={{$json.reply_markup_json}}"
            }
          ]
        },
        "options": {}
      },
      "id": "4607adcd-bcb6-4848-bcef-c60eb3d3380b",
      "name": "Send Upload Result",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        49712,
        13136
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String($json.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = 'waiting_question';\ndata.states = states;\nreturn [{ json: { chat_id: $json.chat_id, message_text: '\u041d\u0430\u043f\u0438\u0448\u0438\u0442\u0435 \u0432\u043e\u043f\u0440\u043e\u0441 \u043f\u043e \u0434\u0435\u043b\u0443 \u043e\u0434\u043d\u0438\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u043c', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }]] }) } }];"
      },
      "id": "694ac548-55b2-49dd-a0c9-4fc9ee548ffb",
      "name": "Set Waiting Question",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        13264
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "8e988ef7-c12f-4b04-abd6-2bcae238ef75",
      "name": "Prompt Ask Question",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        13264
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.rag_base_url}}/retrieve",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": String($json.chat_id), \"case_id\": (($json.active_case_id || '').startsWith('case_') ? $json.active_case_id : `case_${$json.active_case_id}`), \"query\": $json.input_text, \"top_k\": Number($json.rag_top_k || 5)} }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "b3cfac33-f46c-4db6-9354-622d7807b13b",
      "name": "RAG Retrieve",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48272,
        13376
      ]
    },
    {
      "parameters": {
        "jsCode": "const raw = $json.data ?? (typeof $json === 'string' ? $json : JSON.stringify($json));\nlet parsed = {};\ntry { parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch (e) { parsed = {}; }\n\nconst retrieve = $item(0).$node['Prepare Generate Request'].json;\nconst chunksCount = Number(retrieve.chunks_count || 0);\nconst retrieveRaw = String(retrieve.retrieve_raw || '');\n\nconst escapeHtml = (value) => String(value || '')\n  .replace(/&/g, '&amp;')\n  .replace(/</g, '&lt;')\n  .replace(/>/g, '&gt;');\n\nconst answer = String(parsed.answer || '').trim();\nif (!answer) {\n  const reason = chunksCount === 0\n    ? '\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u0439\u0442\u0438 \u0440\u0435\u043b\u0435\u0432\u0430\u043d\u0442\u043d\u044b\u0435 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u044b \u043f\u043e \u0434\u0435\u043b\u0443'\n    : '\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0442\u0432\u0435\u0442 \u043f\u043e \u0434\u0435\u043b\u0443';\n\n  const debugSource = chunksCount === 0 ? retrieveRaw : String(raw);\n  return [{\n    json: {\n      chat_id: $item(0).$node['Load State'].json.chat_id,\n      message_text: `${reason}\\n\\n\u041e\u0442\u0432\u0435\u0442 \u0441\u0435\u0440\u0432\u0438\u0441\u0430:\\n<code>${escapeHtml(debugSource.slice(0, 1000))}</code>`,\n      reply_markup_json: JSON.stringify({\n        inline_keyboard: [\n          [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n          [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n        ]\n      })\n    }\n  }];\n}\n\nconst sources = Array.isArray(parsed.sources) ? parsed.sources : [];\nconst lines = [];\nlines.push('<b>\u041e\u0442\u0432\u0435\u0442 \u043f\u043e \u0434\u0435\u043b\u0443</b>');\nlines.push('');\nfor (const part of answer.split(/\\n+/)) {\n  lines.push(escapeHtml(part));\n}\nif (sources.length) {\n  lines.push('');\n  lines.push('<b>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438</b>');\n  for (const [i, src] of sources.slice(0, 10).entries()) {\n    const name = escapeHtml(src.name || src.source || src.doc_name || src.doc_id || `\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a ${i + 1}`);\n    const page = src.page !== undefined && src.page !== null ? `, \u0441\u0442\u0440. ${escapeHtml(src.page)}` : '';\n    lines.push(`\u2022 ${name}${page}`);\n  }\n}\nif (parsed.meta && Object.keys(parsed.meta).length) {\n  lines.push('');\n  lines.push(`<code>meta: ${escapeHtml(JSON.stringify(parsed.meta).slice(0, 500))}</code>`);\n}\n\nconst blocks = [];\nlet current = '';\nconst limit = 3500;\n\nconst pushCurrent = () => {\n  const trimmed = current.trim();\n  if (trimmed) blocks.push(trimmed);\n  current = '';\n};\n\nfor (const line of lines) {\n  const candidate = current ? `${current}\\n${line}` : line;\n  if (candidate.length <= limit) {\n    current = candidate;\n    continue;\n  }\n  if (current) pushCurrent();\n\n  if (line.length <= limit) {\n    current = line;\n    continue;\n  }\n\n  let remaining = line;\n  while (remaining.length > limit) {\n    let cut = remaining.lastIndexOf(' ', limit);\n    if (cut < Math.floor(limit * 0.6)) cut = limit;\n    blocks.push(remaining.slice(0, cut).trim());\n    remaining = remaining.slice(cut).trim();\n  }\n  current = remaining;\n}\npushCurrent();\n\nreturn blocks.map((chunk, index) => ({\n  json: {\n    chat_id: $item(0).$node['Load State'].json.chat_id,\n    message_text: chunk,\n    reply_markup_json: index === blocks.length - 1 ? JSON.stringify({\n      inline_keyboard: [\n        [{ text: '\u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u044b', callback_data: 'CASE_LIST_DOCS' }, { text: '\u0421\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u043f\u043e \u0434\u0435\u043b\u0443', callback_data: 'CASE_ASK' }],\n        [{ text: '\u041d\u0430\u0437\u0430\u0434', callback_data: 'BACK_CASE_MENU' }, { text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]\n      ]\n    }) : ''\n  }\n}));"
      },
      "id": "0b8f2a71-5cb0-474e-b719-4b3307ae0fa0",
      "name": "Prepare Ask Chunks",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48512,
        13376
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ Object.assign({\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\"}, $json.reply_markup_json ? {\"reply_markup\": JSON.parse($json.reply_markup_json)} : {}) }}",
        "options": {}
      },
      "id": "1ba09edf-d8bf-45b7-af4e-b97f305a1c8d",
      "name": "Send Ask Chunks",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48752,
        13376
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { chat_id: $json.chat_id, message_text: '\u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0432\u044b\u0431\u0435\u0440\u0438 \u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0439 \u0434\u0435\u043b\u043e', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u0434\u0435\u043b\u043e', callback_data: 'MENU_NEW_CASE' }, { text: '\u0421\u043f\u0438\u0441\u043e\u043a \u0434\u0435\u043b', callback_data: 'MENU_LIST_CASES' }],[{ text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]] }) } }];"
      },
      "id": "1756c4ef-efcb-4e38-aa49-a2c52aa34b42",
      "name": "Prepare No Active Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        13504
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "fd9cfaea-d9ef-4256-9546-2ad86ad247a1",
      "name": "Send No Active Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        13504
      ]
    },
    {
      "parameters": {
        "jsCode": "return [{ json: { chat_id: $json.chat_id, message_text: '\u041d\u0435 \u043f\u043e\u043d\u044f\u043b \u043a\u043e\u043c\u0430\u043d\u0434\u0443', reply_markup_json: JSON.stringify({ inline_keyboard: [[{ text: '\u0413\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e', callback_data: 'BACK_MAIN' }]] }) } }];"
      },
      "id": "e232b64e-39fa-43d8-b82e-94b24c01c226",
      "name": "Prepare Unknown Message",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48272,
        13616
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://api.telegram.org/bot{{$env.TELEGRAM_BOT_TOKEN}}/sendMessage",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": $json.chat_id, \"text\": $json.message_text, \"parse_mode\": \"HTML\", \"reply_markup\": JSON.parse($json.reply_markup_json)} }}",
        "options": {}
      },
      "id": "abd25560-508d-46ee-82f0-0ff976fc637b",
      "name": "Send Unknown Message",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        13616
      ]
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\ndata.states = {};\nreturn items;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        47184,
        12800
      ],
      "id": "dcea9948-31e3-43c0-8ef1-215b4fa98144",
      "name": "Code in JavaScript"
    },
    {
      "parameters": {
        "jsCode": "const data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst base = $item(0).$node['Load State'].json;\nconst chatId = String(base.chat_id || 'unknown');\nconst activeCaseId = String(base.active_case_id || '');\nconst externalCaseId = activeCaseId.startsWith('case_') ? activeCaseId : `case_${activeCaseId}`;\n\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = '';\n\nconst raw = $json.data ?? (typeof $json === 'string' ? $json : JSON.stringify($json));\nlet parsed = {};\ntry { parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch (e) { parsed = {}; }\n\nconst ok = !!parsed.ok;\nconst fileName = $item(0).$node['Prepare Telegram File Path'].json.telegram_document_name || 'document';\n\nconst docs = Array.isArray(states[chatId].documents) ? states[chatId].documents : [];\nif (ok) {\n  docs.push({\n    doc_id: String(parsed.doc_id || '').replace(/^doc_/, ''),\n    display_doc_id: String(parsed.doc_id || ''),\n    name: fileName,\n    case_id: activeCaseId\n  });\n}\nstates[chatId].documents = docs;\n\nconst caseDocs = docs.filter(doc => String(doc.case_id || '') === activeCaseId);\nconst cases = Array.isArray(states[chatId].cases) ? states[chatId].cases : [];\nconst selectedCase = cases.find(c => String(c.case_id || c.id || '').replace(/^case_/, '') === activeCaseId);\nif (selectedCase && ok) {\n  selectedCase.status = parsed.case_status || 'DIRTY';\n  selectedCase.documents_count = caseDocs.length;\n}\ndata.states = states;\n\nconst buildDocs = caseDocs\n  .map(doc => String(doc.display_doc_id || doc.doc_id || doc.name || '').trim())\n  .filter(Boolean);\n\nreturn [{\n  json: {\n    chat_id: base.chat_id,\n    active_case_id: activeCaseId,\n    external_case_id: externalCaseId,\n    rag_base_url: base.rag_base_url,\n    upload_ok: ok,\n    upload_raw: raw,\n    upload_doc_id: parsed.doc_id || '',\n    file_name: fileName,\n    build_docs: buildDocs\n  }\n}];"
      },
      "id": "38277a46-9f49-4d02-8c97-a4bce6541276",
      "name": "Prepare Build Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        54240,
        14720
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.rag_base_url}}/build",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": String($json.chat_id), \"case_id\": $json.external_case_id, \"docs\": $json.build_docs} }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "9866b936-43a1-48c5-8ba8-cf17bde030fb",
      "name": "RAG Build",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        54480,
        14720
      ]
    },
    {
      "parameters": {
        "jsCode": "const raw = $json.data ?? (typeof $json === 'string' ? $json : JSON.stringify($json));\nlet parsed = {};\ntry { parsed = typeof raw === 'string' ? JSON.parse(raw) : raw; } catch (e) { parsed = {}; }\n\nlet chunks = [];\nif (Array.isArray(parsed)) {\n  chunks = parsed;\n} else if (Array.isArray(parsed.chunks)) {\n  chunks = parsed.chunks;\n} else if (Array.isArray(parsed.results)) {\n  chunks = parsed.results;\n} else if (Array.isArray(parsed.data?.chunks)) {\n  chunks = parsed.data.chunks;\n}\n\nchunks = chunks.map((chunk, index) => {\n  if (typeof chunk === 'string') {\n    return {\n      text: chunk,\n      source: '',\n      page: null,\n      chunk_id: `chunk_${index + 1}`,\n      score: null\n    };\n  }\n  return {\n    text: String(chunk.text || chunk.content || ''),\n    source: String(chunk.source || chunk.doc_name || chunk.doc_id || ''),\n    page: chunk.page ?? null,\n    chunk_id: String(chunk.chunk_id || chunk.id || `chunk_${index + 1}`),\n    score: chunk.score ?? null\n  };\n}).filter(chunk => chunk.text);\n\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst base = $item(0).$node['Load State'].json;\nconst chatId = String(base.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = '';\ndata.states = states;\n\nconst activeCaseId = String(base.active_case_id || '');\nconst externalCaseId = activeCaseId.startsWith('case_') ? activeCaseId : `case_${activeCaseId}`;\n\nreturn [{\n  json: {\n    chat_id: base.chat_id,\n    active_case_id: activeCaseId,\n    external_case_id: externalCaseId,\n    llm_base_url: base.llm_base_url,\n    query: $item(0).$node['Determine Route'].json.input_text || '',\n    chunks,\n    retrieve_raw: raw,\n    chunks_count: chunks.length\n  }\n}];"
      },
      "id": "99cf726f-4fca-4be6-9b37-b2273756ed76",
      "name": "Prepare Generate Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        53344,
        15024
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.llm_base_url}}/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"case_id\": $json.external_case_id, \"query\": $json.query, \"chunks\": $json.chunks} }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "46321ff3-38ae-480b-8691-870e381e90e3",
      "name": "LLM Generate",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        53584,
        15024
      ]
    },
    {
      "parameters": {
        "url": "={{$item(0).$node[\"Load State\"].json.api_base_url}}/cases/{{$item(0).$node[\"Load State\"].json.active_case_id}}?chat_id={{$item(0).$node[\"Load State\"].json.chat_id}}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "json"
            }
          }
        }
      },
      "id": "6a0006c5-f96a-4cfc-a398-dd479ab1a144",
      "name": "API Get Case Details After Delete",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48512,
        12896
      ]
    },
    {
      "parameters": {
        "jsCode": "const details = $json || {};\nconst base = $item(0).$node['Load State'].json;\nconst deleteNode = $item(0).$node['API Delete Document'].json;\n\nconst deleteRaw = deleteNode.data ?? (typeof deleteNode === 'string' ? deleteNode : JSON.stringify(deleteNode));\nlet deleteParsed = {};\ntry { deleteParsed = typeof deleteRaw === 'string' ? JSON.parse(deleteRaw) : deleteRaw; } catch (e) { deleteParsed = {}; }\n\nconst selectedDocId = String($item(0).$node['Determine Route'].json.selected_doc_id || '');\nconst activeCaseId = String(base.active_case_id || details.case_id || '');\nconst externalCaseId = activeCaseId.startsWith('case_') ? activeCaseId : `case_${activeCaseId}`;\n\nconst deleteOk = deleteParsed.ok === true || deleteParsed.success === true || deleteParsed.deleted === true;\n\nconst docsFromApi = Array.isArray(details.documents) ? details.documents : [];\nconst buildDocs = docsFromApi\n  .map(doc => String(doc.doc_id || doc.id || doc.name || doc.filename || '').trim())\n  .filter(Boolean);\n\nconst data = $getWorkflowStaticData('global');\nconst states = data.states || {};\nconst chatId = String(base.chat_id || 'unknown');\nstates[chatId] = states[chatId] || {};\nstates[chatId].pending_action = '';\n\nif (Array.isArray(states[chatId].documents)) {\n  states[chatId].documents = states[chatId].documents.filter(doc => {\n    const docId = String(doc.display_doc_id || doc.doc_id || '');\n    return docId !== selectedDocId && docId !== String(selectedDocId).replace(/^doc_/, '');\n  });\n}\ndata.states = states;\n\nreturn [{\n  json: {\n    chat_id: base.chat_id,\n    active_case_id: activeCaseId,\n    external_case_id: externalCaseId,\n    rag_base_url: base.rag_base_url,\n    selected_doc_id: selectedDocId,\n    delete_ok: deleteOk,\n    delete_raw: deleteRaw,\n    build_docs: buildDocs\n  }\n}];"
      },
      "id": "bfabcff7-72a2-4e98-9d2a-71f8ec55cc75",
      "name": "Prepare Delete Rebuild Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        48752,
        12896
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{$json.rag_base_url}}/build",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ {\"chat_id\": String($json.chat_id), \"case_id\": $json.external_case_id, \"docs\": $json.build_docs} }}",
        "options": {
          "response": {
            "response": {
              "neverError": true,
              "responseFormat": "text"
            }
          }
        }
      },
      "id": "c8b6641c-5f76-43b4-8562-7c0c62b024e3",
      "name": "RAG Build After Delete",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.3,
      "position": [
        48992,
        12896
      ]
    }
  ],
  "connections": {
    "Telegram Trigger": {
      "main": [
        [
          {
            "node": "Normalize Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize Input": {
      "main": [
        [
          {
            "node": "Load State",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load State": {
      "main": [
        [
          {
            "node": "Determine Route",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Determine Route": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [
          {
            "node": "Prepare Start Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Waiting Case Title",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API Create Case",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API List Cases",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API Activate Case",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Status Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API Get Case Details",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API Get Case Details For Delete",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "API Delete Document",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Waiting Document",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Telegram Get File",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set Waiting Question",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "RAG Retrieve",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare No Active Message",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Prepare Unknown Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Start Message": {
      "main": [
        [
          {
            "node": "Send Start Menu",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Waiting Case Title": {
      "main": [
        [
          {
            "node": "Ask Case Title",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Create Case": {
      "main": [
        [
          {
            "node": "Save Created Case",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Created Case": {
      "main": [
        [
          {
            "node": "Created Case Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API List Cases": {
      "main": [
        [
          {
            "node": "Prepare Cases Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Cases Message": {
      "main": [
        [
          {
            "node": "Send Cases Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Activate Case": {
      "main": [
        [
          {
            "node": "Save Active Case",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Active Case": {
      "main": [
        [
          {
            "node": "Selected Case Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Case Status": {
      "main": [
        [
          {
            "node": "Prepare Status Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Status Message": {
      "main": [
        [
          {
            "node": "Send Status Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Get Case Details": {
      "main": [
        [
          {
            "node": "Prepare Docs Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Docs Message": {
      "main": [
        [
          {
            "node": "Send Docs Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Get Case Details For Delete": {
      "main": [
        [
          {
            "node": "Prepare Delete Buttons",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Delete Buttons": {
      "main": [
        [
          {
            "node": "Send Delete Options",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Delete Document": {
      "main": [
        [
          {
            "node": "API Get Case Details After Delete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Delete Result": {
      "main": [
        [
          {
            "node": "Send Delete Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Waiting Document": {
      "main": [
        [
          {
            "node": "Prompt Document Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Telegram Get File": {
      "main": [
        [
          {
            "node": "Prepare Telegram File Path",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Telegram File Path": {
      "main": [
        [
          {
            "node": "Download Telegram File",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Download Telegram File": {
      "main": [
        [
          {
            "node": "Attach Binary Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Attach Binary Metadata": {
      "main": [
        [
          {
            "node": "HTTP Upload Document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Upload Document": {
      "main": [
        [
          {
            "node": "Prepare Build Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Finalize Document Upload": {
      "main": [
        [
          {
            "node": "Send Upload Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Waiting Question": {
      "main": [
        [
          {
            "node": "Prompt Ask Question",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Ask Chunks": {
      "main": [
        [
          {
            "node": "Send Ask Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare No Active Message": {
      "main": [
        [
          {
            "node": "Send No Active Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Unknown Message": {
      "main": [
        [
          {
            "node": "Send Unknown Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RAG Retrieve": {
      "main": [
        [
          {
            "node": "Prepare Generate Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Generate Request": {
      "main": [
        [
          {
            "node": "LLM Generate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "LLM Generate": {
      "main": [
        [
          {
            "node": "Prepare Ask Chunks",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Build Request": {
      "main": [
        [
          {
            "node": "RAG Build",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RAG Build": {
      "main": [
        [
          {
            "node": "Finalize Document Upload",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Get Case Details After Delete": {
      "main": [
        [
          {
            "node": "Prepare Delete Rebuild Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Delete Rebuild Request": {
      "main": [
        [
          {
            "node": "RAG Build After Delete",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "RAG Build After Delete": {
      "main": [
        [
          {
            "node": "Prepare Delete Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "3346dbf9-49e0-48c5-9da3-06cb5876d184",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "aL9rZhOK2b6jArIF",
  "tags": []
}