{
  "id": "Q1QXmZae19of5sGy",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "ChatAssistant",
  "tags": [],
  "nodes": [
    {
      "id": "48af8dab-64b3-46e7-9963-23692c64a1de",
      "name": "Parse AI Response",
      "type": "n8n-nodes-base.code",
      "position": [
        -752,
        432
      ],
      "parameters": {
        "jsCode": "// Parse LLM classification response, validate category, fallback to \"other\"\n\nconst VALID_CATEGORIES = [\n  'modify_infrastructure',\n  'incident',\n  'question',\n  'ci_cd_error',\n  'limits',\n  'new_system',\n  'announcement',\n  'other'\n];\n\nconst aiOutput = $input.first().json.text || $input.first().json.output || '';\n\nlet parsed;\ntry {\n  const clean = aiOutput.replace(/```json\\s*|```\\s*/g, '').trim();\n  // Extract the first {...} block to survive any stray prose from tool turns\n  const match = clean.match(/\\{[\\s\\S]*\\}/);\n  parsed = match ? JSON.parse(match[0]) : JSON.parse(clean);\n} catch (e) {\n  parsed = { category: 'other', confidence: 0, summary: 'Failed to parse AI response', acknowledge: '' };\n}\n\nif (!VALID_CATEGORIES.includes(parsed.category)) {\n  parsed.category = 'other';\n  parsed.confidence = 0;\n}\n\n// Merge with upstream data, exclude base64 attachments to save memory\nconst upstream = $('Router').first().json;\nconst { attachments, ...upstreamClean } = upstream;\n\nreturn [{\n  json: {\n    ...upstreamClean,\n    category: parsed.category,\n    confidence: parsed.confidence,\n    summary: parsed.summary || '',\n    acknowledge: parsed.acknowledge || ''\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "00ada946-85b0-4279-ac4d-5204dd581e7b",
      "name": "Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        -176,
        352
      ],
      "parameters": {
        "rules": {
          "values": [
            {
              "outputKey": "modify_infrastructure",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "43563850-a0dc-48e7-bc8f-4feba2ebed70",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "modify_infrastructure"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "incident",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "d29bcd68-7dea-4882-ad95-d1a216ff659f",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "incident"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "question",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "13abbfd6-4dc9-4fcb-8128-a3d3332a99ed",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "question"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "ci_cd_error",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "d26d3d6b-0d2e-4ba5-be7a-3cebc0e0ef16",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "ci_cd_error"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "new_system",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "18b50cea-3cb8-4517-9193-1f04ffdcd5d2",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "new_system"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "announcement",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "f243a2ef-a148-4ec5-8aef-8bf3e13efd6e",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "announcement"
                  }
                ]
              },
              "renameOutput": true
            },
            {
              "outputKey": "other",
              "conditions": {
                "options": {
                  "version": 3,
                  "leftValue": "",
                  "caseSensitive": true,
                  "typeValidation": "strict"
                },
                "combinator": "and",
                "conditions": [
                  {
                    "id": "08b1fef7-6642-4b4c-bfb5-0748312bdd2b",
                    "operator": {
                      "name": "filter.operator.equals",
                      "type": "string",
                      "operation": "equals"
                    },
                    "leftValue": "={{ $json.category }}",
                    "rightValue": "other"
                  }
                ]
              },
              "renameOutput": true
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3.4
    },
    {
      "id": "6efbb8b7-a389-4ba5-ae6d-35abeab4efef",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2128,
        192
      ],
      "parameters": {
        "color": 2,
        "width": 948,
        "height": 528,
        "content": "## Input chain\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nReceiving a message and validating that it came from a trusted source"
      },
      "typeVersion": 1
    },
    {
      "id": "40e323b2-0d04-4ac8-a9aa-22afd81f6549",
      "name": "OpenAI Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        -1088,
        608
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-5.5",
          "cachedResultName": "gpt-5.5"
        },
        "options": {},
        "builtInTools": {}
      },
      "credentials": {
        "openAiApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "7a28b4cd-7855-4e6a-8dc7-3f2ea571b5cc",
      "name": "GetSlackMessage",
      "type": "n8n-nodes-base.webhook",
      "position": [
        -2080,
        416
      ],
      "parameters": {
        "path": "+1234567890+1234567890",
        "options": {
          "rawBody": true
        },
        "httpMethod": "POST",
        "responseMode": "responseNode"
      },
      "typeVersion": 2.1
    },
    {
      "id": "bee8440d-89bc-42d4-8e2c-80ba06a4fa3c",
      "name": "Router",
      "type": "n8n-nodes-base.code",
      "position": [
        -1712,
        416
      ],
      "parameters": {
        "jsCode": "// Parse Slack app_mention event and extract clean message\nconst headers = $input.first().json.headers;\nconst body = $input.first().json.body;\n\n// Validate Slack verification token\nconst SLACK_TOKEN = $input.first().json.CHAT_TOKEN;\nif (body.token !== SLACK_TOKEN) {\n  throw new Error('Invalid Slack verification token');\n}\n\n// Skip Slack retries (Slack retries if no 200 within 3s)\nif (headers['x-slack-retry-num']) {\n  return [];\n}\n\n// URL verification (should not happen after initial setup, but just in case)\nif (body.type === 'url_verification') {\n  return [{\n    json: {\n      isChallenge: true,\n      challenge: body.challenge\n    }\n  }];\n}\n\nconst event = body.event || {};\n\n// Strip <@UXXXXX> bot mention and clean whitespace\nconst cleanText = (event.text || '')\n  .replace(/<@[A-Z0-9]+>\\s*/g, '')\n  .trim();\n\n// Reject empty messages\nif (!cleanText) {\n  return [];\n}\nconst isInThread = Boolean(event.thread_ts) && event.thread_ts !== event.ts;\n// Slack puts uploaded files into event.files (present for message and app_mention events when files are attached).\nconst files = Array.isArray(event.files) ? event.files : [];\nconst fileIds = files.map(f => f && f.id).filter(Boolean);\nreturn [{\n  json: {\n    isChallenge: false,\n    message: cleanText,\n    user_id: event.user,\n    channel_id: event.channel,\n    ts: event.ts,\n    thread_ts: event.thread_ts || event.ts,\n    is_in_thread: isInThread,\n    team_id: body.team_id,\n    event_type: event.type,\n    file_ids: fileIds\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "b1661edc-fa87-4e66-86a2-5c0489fae854",
      "name": "If",
      "type": "n8n-nodes-base.if",
      "position": [
        -1552,
        416
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 3,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "2943f7f0-79d2-4b6f-97b2-c236e86d046f",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              },
              "leftValue": "={{ $json.isChallenge }}",
              "rightValue": "true"
            }
          ]
        }
      },
      "typeVersion": 2.3
    },
    {
      "id": "5a7fd626-a800-4a77-87b3-9f4028de825d",
      "name": "Respond to Webhook",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1360,
        256
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ challenge: $json.challenge }) }}"
      },
      "typeVersion": 1.5
    },
    {
      "id": "7ceffc2c-afa5-4c19-9081-d03522d9dedc",
      "name": "Respond to Webhook1",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        -1360,
        432
      ],
      "parameters": {
        "options": {
          "responseCode": 200
        },
        "respondWith": "noData"
      },
      "typeVersion": 1.5
    },
    {
      "id": "f4a37a6b-5bcc-4f1a-9b23-beca3f1a1a83",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1088,
        432
      ],
      "parameters": {
        "text": "=CONTEXT\nChannel ID: {{ $json.channel_id }}\nMessage ts: {{ $json.ts }}\nThread root ts: {{ $json.thread_ts }}\nIs in thread: {{ $json.is_in_thread }}\nUser ID: {{ $json.user_id }}\n\nUSER MESSAGE:\n{{ $json.message }}",
        "options": {
          "systemMessage": "=You are a request classifier for a DevOps/SRE team. You receive messages from a Slack channel where team members mention your configured on-call bot (@your-oncall-bot).\n\nAlways resolve thread context via Slack MCP\n\nClassify each message into exactly ONE category. Respond with ONLY a valid JSON object, no markdown, no explanation.\n\n## Categories\n\n### modify_infrastructure\nA group of requests related to infrastructure modifications. Typically, this involves changes to the architectural characteristics of the system:\nhigh availability, scalability, security, cost management, perfromance, operations, etc\nMain types of requests\n1. Changing characteristics of existing resources, changing integrations between resources, scaling, icnresing limits and deleting resources.\nExamples:\n- Increase max memory in stateful Valkey\n- Add the X-Region header to the ingress of our main API\n- Scale the Redpanda cluster to 5 instances in production\n- Remove the Meilisearch instance, we no longer use it\n- Increase the memory limit for bottle-api\n\n2. A request to create, allocate, or configure new standard infrastructure resources, i.e., those that we have already organized in our infrastructure. Creation databases (PostgreSQL, Redpanda, Valkey, ScyllaDB), secrets in Vault, ntopics in Kafka/Redpanda, DNS records, create new git repository, write new github actions workflow, add new alert into Prometheus etc.\nExamples:\n- Can you create a Postgres for the new service?\n- Please add a Vault secret\n- Can we add battlepass to the stateless Valkey?\n- We need Redis and Postgres for the new achievement service\n- Please set up a small Postgres for the roulette service\n- Create an alert for low disk space on the server\n\n3. This could be an explicit request to create a task\nExamples:\n - Create a task to expand load balancer monitoring\n\n### incident\nSomething is broken, down, not working, returning errors. Active production or staging issues. Keywords: broken, down, 500, 502, errors flooding, not working, crashed, fell over, investigate alert.\nExamples:\n- Two hours ago we broke the Kafka connection in Vault\n- Are you changing something? The admin panel is returning 500s\n- Postgres is down on staging\n- fetch had inner topic errors from broker\n- The network on staging is acting up\n- 502 errors are flooding in\n- Sentry is down\n- Sockets have been broken for about an hour\n- All jobs are stuck on staging\n- We got a disk space alert\n- Need to investigate the 500 error issue\n\n### question\nAsking for information, clarification, checking status. No urgent breakage, just wants to understand or verify something. This must be related to our infrastructure. Keywords: tell me, how does it work, is there docs, can you check, it seems like, I have a feeling, could it be.\nExamples:\n- How do pod crash alerts work?\n- Is there any documentation on domains?\n- There was an OOM on user-api but no alert came through\n- Please check the memory on stateful Valkey\n- It seems like battlepass-api metrics are not being collected\n- Could it be that the CDN is blocking MP3 files?\n- Are the ingress settings different between staging and production?\n\n### ci_cd_error\nCI/CD pipeline failures: builds broken, deployments failing, GitHub Actions issues, builds stuck in queue, timeouts.\nExamples:\n- Builds seem to be broken (link to GitHub Actions)\n- The build is taking very long\n- Builds are queuing but not running\n- Staging deployment is failing\n- CI/CD in Flutter is broken\n- Builds in pull requests are failing en masse\n- GitHub CI/CD is not working\n\n### new_system\n1. Implementing a fundamentally new system or integration. Use the knowledge base to determine whether such a system already exists in our infrastructure. This could be a request to create a vector database that we don't yet have. This could be a request to implement tracing using Grafana Tempo.\nExamples:\n- Set up a Qdrant vector database for us\n- Deploy OpenClaw on a DigitalOcean droplet\n\n2. This could be replacing the current solution with some new one that we haven't had yet.\nExamples:\n- We need to replace Nginx Ingress Controller with Envoy API Gateway\n- Let's migrate from Redis to Valkey\n\n### announcement\nNotifications about changes related to our infrastructure. For example, work in our cloud provider's Kubernetes cluster on a specific date. Changes in the cost of resources consumed by our infrastructure.\n\n### other\nAnything that does not clearly fit the above categories.\n\n## Tools (Slack MCP)\nYou have a Slack MCP tool with these capabilities:\n- conversations_replies(channel, ts) \u2014 fetch all replies in a thread\n- conversations_history(channel, limit) \u2014 fetch recent channel messages\n- users_search(query) \u2014 resolve user IDs to display names\n- channels_list \u2014 list channels\n## Decision procedure\n1. Inspect the input field \"Is in thread\".\n2. If \"Is in thread\" is true:\n   a. Call conversations_replies with channel = \"Channel ID\" and ts = \"Thread root ts\" to fetch the full thread.\n   b. Read every reply in chronological order. Treat the user's latest message as the request, and the earlier replies as context (what was already discussed, what error/log/link was shared, what was already tried).\n   c. If the thread reveals additional context (e.g. error logs pasted earlier, previous DevOps actions, the actual broken service), incorporate it into \"summary\" and use it to refine \"category\".\n3. If \"Is in thread\" is false:\n   a. You MAY call conversations_history(channel = \"Channel ID\", limit = 10) only if the message is ambiguous (very short, only a link, only an error fragment) and the surrounding channel context could disambiguate it.\n   b. Otherwise classify directly from the message.\n4. Resolve user mentions in the message via users_search ONLY if the user ID is referenced in the request and resolving it changes the classification or summary.\n\n## Important Rules\n1. If a message fits multiple categories, pick the PRIMARY intent.\n2. \"incident\" takes priority over \"question\" when something is clearly broken.\n3. \"change_request\" vs \"create_resource\": if the resource already exists and needs modification \u2192 change_request. If it's new \u2192 create_resource.\n4. Answer in the same language in which the question was asked. Respond with JSON only.\n5. Don't ask the user anything. You may call tools, but the final assistant message must be JSON only.\n\n## Output\nYour final answer MUST be a single JSON object and NOTHING else:\n- No prose before the JSON.\n- No prose after the JSON.\n- No markdown, no ```json fences, no explanations.\n- No comments inside the JSON.\n\nIf you violate this contract, the downstream parser fails and the user gets no reply.\n{\"category\": \"<category_key>\", \"confidence\": <0.0-1.0>, \"summary\": \"<one sentence summary in  the same language in which the question was asked>\",\"acknowledge\":\"<a short response that you accepted the request and started working on it>\"}"
        },
        "promptType": "define"
      },
      "typeVersion": 3.1
    },
    {
      "id": "6f12f767-d18e-4d3c-8c7a-688acc972e70",
      "name": "Slack",
      "type": "@n8n/n8n-nodes-langchain.mcpClientTool",
      "position": [
        -944,
        800
      ],
      "parameters": {
        "include": "selected",
        "options": {},
        "endpointUrl": "https://<your-mcp-host>/slack/mcp",
        "includeTools": [
          "channels_list",
          "conversations_history",
          "users_search",
          "conversations_replies"
        ],
        "authentication": "bearerAuth"
      },
      "credentials": {
        "httpBearerAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "bf74b477-2771-4d36-8925-6977a7184fb0",
      "name": "GetUserName",
      "type": "n8n-nodes-base.slack",
      "position": [
        -544,
        432
      ],
      "parameters": {
        "user": {
          "__rl": true,
          "mode": "id",
          "value": "={{ $json.user_id }}"
        },
        "resource": "user",
        "operation": "getProfile",
        "authentication": "oAuth2"
      },
      "credentials": {
        "slackOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.4
    },
    {
      "id": "084d0909-99cb-4854-8a92-e295f832a0fa",
      "name": "AddUserName",
      "type": "n8n-nodes-base.code",
      "position": [
        -336,
        432
      ],
      "parameters": {
        "jsCode": "const upstream = $('Parse AI Response').first().json;\nconst profile = $input.first().json;\n\nconst userName =\n  profile.real_name ||\n  profile.display_name ||\n  profile.real_name_normalized ||\n  profile.display_name_normalized ||\n  '';\n\nreturn [{\n  json: {\n    ...upstream,\n    user_name: userName,\n  },\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "b522c75c-87ba-4486-a3a7-f543461db6ee",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1120,
        192
      ],
      "parameters": {
        "color": 6,
        "width": 304,
        "height": 528,
        "content": "## Message classificator"
      },
      "typeVersion": 1
    },
    {
      "id": "273772cf-aa41-4fa0-9e28-c1bb3c5d1319",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1120,
        752
      ],
      "parameters": {
        "color": 4,
        "width": 560,
        "height": 352,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n## MCP servers\nAdd correct URLs for remote MCPs\nUse following mcp:\n* [slack-mcp](https://github.com/korotovsky/slack-mcp-server)"
      },
      "typeVersion": 1
    },
    {
      "id": "6eeeed75-30a0-4341-987a-a9181de392b1",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -208,
        176
      ],
      "parameters": {
        "color": 2,
        "width": 304,
        "height": 928,
        "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n## Output chain\n```\n[\n  {\n    \"message\": ,\n    \"post_id\": ,\n    \"channel_id\": ,\n    \"channel_name\": ,\n    \"user_name\": ,\n    \"user_id\": ,\n    \"file_ids\":,\n    \"category\": ,\n    \"confidence\": ,\n    \"summary\": ,\n    \"acknowledge\": ,\n    \"is_in_thread\":,\n    \"parent_post\": ,\n    \"thread_root_id\": \n  }\n]\n```"
      },
      "typeVersion": 1
    },
    {
      "id": "642559b2-8c63-4111-9383-f03402f00ac5",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2128,
        752
      ],
      "parameters": {
        "color": 5,
        "width": 944,
        "height": 832,
        "content": "## Overview\nReceives messages to technical support and classifies them.\n\n## Requirements\n1. OpenRouter/OpenAI/Anthropic API key\n1. Google Gemini API key \u2014 for embeddings (models/gemini-embedding-2-preview) used with Qdrant\n1. Slack app \u2014 configured on the Slack side; Events API sends POST to the GetSlackMessage webhook; the token from the payload is compared against CHAT_TOKEN\n1. Slack MCP\n1. Qdrant\n1. Webhook token \u2014 a static CHAT_TOKEN value in the SetVars node, must match the verification token configured in your Slack app\n\n## How it works\n1. Message reception. Slack sends a POST with an app_mention event to the GetSlackMessage webhook when @your-oncall-bot is mentioned in a subscribed channel. The message is validated using the token and required fields are extracted.\n1. Classification via AI Agent. The agent analyzes the request and determines which category to assign it to.\n1. Parsing the agent's response.\n1. Resolving user name by id.\n1. Routing by category. Switch dispatches the payload across 7 branches (modify_infrastructure, incident, question, ci_cd_error, new_system, announcement, other) \u2014 all branches are intentionally empty in the template, so users can plug in their own sub-workflows.\n\n## How to use\n1. Import the workflow into n8n, verify compatibility with v2.18.2.\n1. Connect credentials:\n  * OpenRouter/OpenAI/Anthropic (Chat Model)\n  * Google Gemini (Embeddings)\n  * Qdrant API (Vector Store) \u2014 set your own URL and API key\n  * Slack OAuth (GetUserName)\n  * Slack MCP bearer token (Slack node)\n4. Replace the Slack MCP URL in the Slack node with your endpoint\n5. Create a Qdrant collection and set its name in the Qdrant Vector Store node\n6. Set CHAT_TOKEN in SetVars to match your Slack app verification token\n7. Configure your Slack app to post events to the webhook URL\n8. Optionally configure an Error Workflow"
      },
      "typeVersion": 1
    },
    {
      "id": "0eb905c4-3024-4801-bbec-6689d6a4bc38",
      "name": "Qdrant Vector Store",
      "type": "@n8n/n8n-nodes-langchain.vectorStoreQdrant",
      "position": [
        -832,
        800
      ],
      "parameters": {
        "mode": "retrieve-as-tool",
        "options": {},
        "toolDescription": "Use this tool to fetch knowledge our IT infrastructure. ",
        "qdrantCollection": {
          "__rl": true,
          "mode": "list",
          "value": "<your-infrastructure-knowledge-collection>",
          "cachedResultName": "<your-infrastructure-knowledge-collection>"
        }
      },
      "credentials": {
        "qdrantApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "1b44b0f4-5913-47cc-adce-9be8b49a38f9",
      "name": "Embeddings Google Gemini",
      "type": "@n8n/n8n-nodes-langchain.embeddingsGoogleGemini",
      "position": [
        -832,
        960
      ],
      "parameters": {
        "modelName": "models/gemini-embedding-2-preview"
      },
      "credentials": {
        "googlePalmApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "fb5784da-44a1-496e-b301-fd59474bbd67",
      "name": "SetVars",
      "type": "n8n-nodes-base.set",
      "position": [
        -1904,
        416
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "14b3ecf7-3456-4eb1-96d0-d49da7b174ea",
              "name": "CHAT_TOKEN",
              "type": "string",
              "value": ""
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "",
    "timeSavedMode": "fixed",
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "2749fc5f-e1b8-420e-9100-3fcfa110a3e7",
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond to Webhook1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Slack": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Router": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch": {
      "main": [
        [],
        [],
        [],
        [],
        [],
        [],
        []
      ]
    },
    "SetVars": {
      "main": [
        [
          {
            "node": "Router",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AddUserName": {
      "main": [
        [
          {
            "node": "Switch",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GetUserName": {
      "main": [
        [
          {
            "node": "AddUserName",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GetSlackMessage": {
      "main": [
        [
          {
            "node": "SetVars",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "GetUserName",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Qdrant Vector Store": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    },
    "Respond to Webhook1": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Embeddings Google Gemini": {
      "ai_embedding": [
        [
          {
            "node": "Qdrant Vector Store",
            "type": "ai_embedding",
            "index": 0
          }
        ]
      ]
    }
  }
}