{
  "id": "hgimqy2YWeNz6tMZ",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Chatbot Yash Portfolio",
  "tags": [],
  "nodes": [
    {
      "id": "e04239b1-cb9b-46b8-a32e-77d07534250f",
      "name": "Chat Trigger",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        1008,
        -1024
      ],
      "parameters": {
        "mode": "=webhook",
        "public": true,
        "options": {}
      },
      "typeVersion": 1.3
    },
    {
      "id": "9269677d-0f39-420b-8f98-9736518b1ac1",
      "name": "Guard Input",
      "type": "n8n-nodes-base.if",
      "position": [
        1328,
        -1024
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "not-empty",
              "operator": {
                "type": "number",
                "operation": "gt"
              },
              "leftValue": "={{ ($json.chatInput || '').trim().length }}",
              "rightValue": 0
            },
            {
              "id": "max-length",
              "operator": {
                "type": "number",
                "operation": "lte"
              },
              "leftValue": "={{ ($json.chatInput || '').length }}",
              "rightValue": 1000
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "0b7b6bb6-2e4b-466e-9ca9-0739673475e3",
      "name": "Invalid Input Reply",
      "type": "n8n-nodes-base.set",
      "position": [
        1312,
        -784
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "output",
              "name": "output",
              "type": "string",
              "value": "Please enter a message (up to 1000 characters) and I'll be happy to help!"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8935a198-1637-4f50-958f-e3fbf918bec9",
      "name": "Profile Unavailable Reply",
      "type": "n8n-nodes-base.set",
      "position": [
        1536,
        -800
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "output",
              "name": "output",
              "type": "string",
              "value": "Sorry, I'm having trouble accessing Yash's profile right now. Please try again in a moment, or reach out to Yash directly through the contact links on this site."
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7b6d4bcf-7d6b-4e58-9577-daa5391cba51",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        -1232
      ],
      "parameters": {
        "width": 480,
        "height": 640,
        "content": "## Chatbot\n\n### How it works\n\n1. The workflow is triggered by a chat message (public widget).\n2. Input is validated (non-empty, max 1000 characters).\n3. Profile data is fetched with a 1-hour cache (falls back to stale cache if the site is down).\n4. The AI agent answers using the Groq Llama 3.3 70B model.\n\n### Setup steps\n\n- [x] Set up the chat trigger (allowed origins restricted)\n- [x] Configure the cached profile fetch\n- [x] Set up the AI agent\n- [x] Configure the Groq chat model\n\n### Customization\n\nCustomize the system prompt and model as needed"
      },
      "typeVersion": 1
    },
    {
      "id": "7a583858-d53c-45a5-8af8-a491e78537db",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        944,
        -1232
      ],
      "parameters": {
        "color": 7,
        "height": 352,
        "content": "## Chat Trigger\n\nTrigger for chat messages.\nOrigins restricted to iamyashkabra.com"
      },
      "typeVersion": 1
    },
    {
      "id": "0f9c682b-3836-44cc-bcac-b01e88d0ece8",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1248,
        -1248
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 608,
        "content": "## Validate, Fetch and Prepare Data\n\nGuards the input, fetches profile data (cached 1 hour, stale fallback on error) and prepares it for the AI agent"
      },
      "typeVersion": 1
    },
    {
      "id": "60a7859e-a4e5-4c35-9f8b-f52c530d4be5",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1824,
        -1248
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 624,
        "content": "## AI Agent Processing\n\nProcesses chat input using AI agents"
      },
      "typeVersion": 1
    },
    {
      "id": "f5c4fbb0-9f7c-440a-8270-336c011124c5",
      "name": "Get Profile",
      "type": "n8n-nodes-base.code",
      "onError": "continueErrorOutput",
      "position": [
        1504,
        -1024
      ],
      "parameters": {
        "jsCode": "// Fetch the profile with a 1-hour cache. Falls back to a stale cache\n// if the site is unreachable; only errors when no cache exists at all.\nconst staticData = $getWorkflowStaticData('global');\nconst ONE_HOUR = 60 * 60 * 1000;\nconst now = Date.now();\n\nif (staticData.profileCache && staticData.profileFetchedAt && now - staticData.profileFetchedAt < ONE_HOUR) {\n  return [{ json: staticData.profileCache }];\n}\n\ntry {\n  const profile = await this.helpers.httpRequest({\n    url: 'https://www.iamyashkabra.com/yash-profile.json',\n    json: true,\n    timeout: 5000,\n  });\n  staticData.profileCache = profile;\n  staticData.profileFetchedAt = now;\n  return [{ json: profile }];\n} catch (error) {\n  if (staticData.profileCache) {\n    return [{ json: staticData.profileCache }];\n  }\n  throw error;\n}"
      },
      "typeVersion": 2
    },
    {
      "id": "114a3c29-fd05-4faf-b143-63659c7764af",
      "name": "Set Chat Data",
      "type": "n8n-nodes-base.set",
      "position": [
        1680,
        -1024
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "chatInput",
              "name": "chatInput",
              "type": "string",
              "value": "={{ $('Chat Trigger').item.json.chatInput }}"
            },
            {
              "id": "profile_json",
              "name": "profile_json",
              "type": "string",
              "value": "={{ JSON.stringify($('Get Profile').item.json) }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "0514aabe-00a4-4f0c-a06b-1620185b7205",
      "name": "Chat Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        1920,
        -1024
      ],
      "parameters": {
        "text": "={{ $json.chatInput }}",
        "options": {
          "systemMessage": "=You are Yash Kabra's portfolio assistant.\n\nHere is Yash's complete professional profile data:\n\n{{ $('Set Chat Data').item.json.profile_json }}\n\nInstructions:\n- Use ONLY the information from this profile to answer questions\n- Answer questions about Yash's work experience and career history, technical skills, projects and achievements, education background, contact information and social links, and professional summary\n- Be conversational and professional\n- If specific information isn't available in the profile, say \"I don't have that information in Yash's profile\"\n- For contact inquiries, mention preferred contact methods from the profile\n- Highlight relevant achievements and quantifiable results when discussing experience\n- Never invent or assume information not in the profile\n\nFormatting rules:\n- Always format your responses using Markdown: use **bold** for key terms, bullet points for lists, and code blocks for technical examples. Keep paragraphs short and readable.\n- ALWAYS write links as Markdown hyperlinks. NEVER show a raw URL.\n  - CORRECT: [LinkedIn](https://www.linkedin.com/in/yashkabra143/)\n  - INCORRECT: LinkedIn: https://www.linkedin.com/in/yashkabra143/"
        },
        "promptType": "define"
      },
      "typeVersion": 2.2
    },
    {
      "id": "258c9a2b-32f2-4944-9fdf-b2092d71e1b5",
      "name": "Chat Memory",
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "position": [
        2016,
        -832
      ],
      "parameters": {
        "contextWindowLength": 10
      },
      "typeVersion": 1.3
    },
    {
      "id": "8684cc2a-5728-4fa4-bf56-8dc56eba34ed",
      "name": "Groq Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatGroq",
      "position": [
        1872,
        -816
      ],
      "parameters": {
        "model": "llama-3.3-70b-versatile",
        "options": {}
      },
      "credentials": {
        "groqApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    }
  ],
  "active": true,
  "settings": {
    "timezone": "Asia/Kolkata",
    "binaryMode": "separate",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "H3xXQcCOAplaHb93",
    "timeSavedMode": "fixed",
    "availableInMCP": true,
    "executionOrder": "v1"
  },
  "versionId": "e37c9861-2088-42f6-8ccc-d4c8e62a11c3",
  "nodeGroups": [],
  "connections": {
    "Groq Model": {
      "ai_languageModel": [
        [
          {
            "node": "Chat Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Chat Memory": {
      "ai_memory": [
        [
          {
            "node": "Chat Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Get Profile": {
      "main": [
        [
          {
            "node": "Set Chat Data",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Profile Unavailable Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Guard Input": {
      "main": [
        [
          {
            "node": "Get Profile",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Invalid Input Reply",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Chat Trigger": {
      "main": [
        [
          {
            "node": "Guard Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Chat Data": {
      "main": [
        [
          {
            "node": "Chat Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}