{
  "name": "AI Sentiment Analyzer (Ollama)",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "analyze-sentiment",
        "responseMode": "responseNode",
        "options": {}
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000001",
      "name": "Webhook - Receive Text",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "const input = $input.first().json;\nlet texts = [];\n\n// Support single text or array of texts\nif (input.body && input.body.texts && Array.isArray(input.body.texts)) {\n  texts = input.body.texts.slice(0, 10); // Max 10 texts per batch\n} else if (input.body && input.body.text) {\n  texts = [input.body.text];\n} else {\n  return [{ json: { error: 'Provide { \"text\": \"...\" } or { \"texts\": [\"...\", \"...\"] } in the request body', valid: false } }];\n}\n\nif (texts.length === 0 || texts.some(t => !t || t.length < 2)) {\n  return [{ json: { error: 'Each text must be at least 2 characters long', valid: false } }];\n}\n\nreturn [{ json: { texts, count: texts.length, valid: true } }];"
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000002",
      "name": "Validate Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        480,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": false
          },
          "combinator": "and",
          "conditions": [
            {
              "leftValue": "={{ $json.valid }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        }
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000003",
      "name": "Is Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        700,
        300
      ]
    },
    {
      "parameters": {
        "url": "http://localhost:11434/api/generate",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'llama3.1:8b', prompt: 'You are a sentiment analysis engine. Analyze the sentiment of each text below. For each text, provide:\\n- sentiment: POSITIVE, NEGATIVE, NEUTRAL, or MIXED\\n- confidence: a score from 0.0 to 1.0\\n- emotions: list of detected emotions (joy, anger, sadness, fear, surprise, disgust, trust, anticipation)\\n- reasoning: one brief sentence explaining your analysis\\n\\nRespond ONLY in valid JSON format like this:\\n{\"results\": [{\"text_index\": 0, \"sentiment\": \"POSITIVE\", \"confidence\": 0.92, \"emotions\": [\"joy\", \"trust\"], \"reasoning\": \"Expresses satisfaction and gratitude.\"}]}\\n\\nTexts to analyze:\\n' + $json.texts.map((t, i) => '[' + i + '] ' + t.substring(0, 500)).join('\\n'), stream: false, options: { temperature: 0.1, num_predict: 2000 } }) }}",
        "options": {
          "timeout": 120000
        }
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000004",
      "name": "Analyze Sentiment (Ollama)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        960,
        200
      ]
    },
    {
      "parameters": {
        "jsCode": "const ollamaResponse = $input.first().json;\nlet responseText;\n\ntry {\n  const parsed = typeof ollamaResponse.data === 'string' ? JSON.parse(ollamaResponse.data) : ollamaResponse;\n  responseText = parsed.response || parsed.data?.response || '';\n} catch (e) {\n  responseText = JSON.stringify(ollamaResponse);\n}\n\n// Try to extract JSON from the response\nlet results;\ntry {\n  // Find JSON in the response\n  const jsonMatch = responseText.match(/\\{[\\s\\S]*\\}/);\n  if (jsonMatch) {\n    results = JSON.parse(jsonMatch[0]);\n  } else {\n    results = { raw_analysis: responseText };\n  }\n} catch (e) {\n  results = { raw_analysis: responseText };\n}\n\nconst inputData = $('Validate Input').first().json;\n\nreturn [{\n  json: {\n    ...results,\n    metadata: {\n      texts_analyzed: inputData.count,\n      model: 'llama3.1:8b',\n      processed_at: new Date().toISOString()\n    }\n  }\n}];"
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000005",
      "name": "Parse & Format Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1200,
        200
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify($json) }}"
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000006",
      "name": "Send Analysis Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1440,
        200
      ]
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ JSON.stringify({ error: $('Validate Input').first().json.error }) }}",
        "options": {
          "responseCode": 400
        }
      },
      "id": "b2c3d4e5-0002-4000-8000-000000000007",
      "name": "Send Error Response",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        960,
        440
      ]
    }
  ],
  "connections": {
    "Webhook - Receive Text": {
      "main": [
        [
          {
            "node": "Validate Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Input": {
      "main": [
        [
          {
            "node": "Is Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Valid?": {
      "main": [
        [
          {
            "node": "Analyze Sentiment (Ollama)",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Send Error Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analyze Sentiment (Ollama)": {
      "main": [
        [
          {
            "node": "Parse & Format Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse & Format Results": {
      "main": [
        [
          {
            "node": "Send Analysis Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "AI"
    },
    {
      "name": "Ollama"
    },
    {
      "name": "Sentiment Analysis"
    },
    {
      "name": "Free Sample"
    }
  ]
}