{
  "name": "AI Pulse \u2014 Scoring (Pass 1)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 4
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        0,
        0
      ],
      "id": "121cd46b-5e19-4844-94f7-68a6edfd214c",
      "name": "Schedule Trigger"
    },
    {
      "parameters": {
        "language": "pythonNative",
        "pythonCode": "import sqlite3\n\ndb_path = \"/data/ai-pulse/ai-pulse.db\"\nconn = sqlite3.connect(db_path)\nconn.row_factory = sqlite3.Row\ncursor = conn.cursor()\n\ncursor.execute(\"\"\"\n    SELECT id, title, url, source_name, source_type, raw_content\n    FROM articles\n    WHERE relevance_score = 0\n    ORDER BY ingested_at DESC\n    LIMIT 200\n\"\"\")\nrows = cursor.fetchall()\nconn.close()\n\nreturn [{\"json\": dict(row)} for row in rows]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        224,
        96
      ],
      "id": "44ed2bff-e695-45f9-a4d3-3780f38e1216",
      "name": "Fetch Unscored Articles"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        448,
        96
      ],
      "id": "5704552c-3d07-4152-b5ed-cdd1395cd6fe",
      "name": "Loop Articles"
    },
    {
      "parameters": {
        "language": "pythonNative",
        "pythonCode": "import json as json_lib\n\nitems = _items\nresult = []\n\nfor item in items:\n    data = item['json'] if 'json' in item else item\n    \n    title = str(data.get('title', 'Untitled'))[:300]\n    source_name = str(data.get('source_name', 'Unknown'))\n    raw = str(data.get('raw_content', ''))[:3000]\n    \n    prompt = (\n        \"You are an AI relevance scorer for a Software Engineer interested in:\\n\"\n        \"1. AI-powered dev tools (Copilot, Cursor, Gemini CLI, Claude Code, Windsurf, etc.)\\n\"\n        \"2. AI agents & automation (MCP, tool use, agentic workflows, autonomous coding agents)\\n\"\n        \"3. New model releases & benchmarks (GPT, Claude, Gemini, open-source LLMs)\\n\"\n        \"4. SDLC excellence with AI (testing, CI/CD, code review, QA automation, deployment)\\n\"\n        \"5. Context engineering & prompt engineering (system prompts, RAG, context windows, prompt patterns)\\n\"\n        \"6. AI-assisted automation (workflow automation, n8n, scripting, infrastructure as code)\\n\\n\"\n        \"Scoring guidelines:\\n\"\n        \"- 80-100: Directly actionable for a hands-on engineer \u2014 new tools, releases, techniques they can use today\\n\"\n        \"- 60-79: Relevant industry development \u2014 worth knowing but not immediately actionable\\n\"\n        \"- 40-59: Tangentially related \u2014 general AI news with some engineering relevance\\n\"\n        \"- 0-39: Not relevant \u2014 business/policy news, hype, speculation, or unrelated topics\\n\\n\"\n        f\"Given this article:\\nTitle: {title}\\nSource: {source_name}\\n\"\n        f\"Content: {raw}\\n\\n\"\n        \"Return ONLY valid JSON, no other text:\\n\"\n        \"{\\\"relevance_score\\\": <0-100>, \\\"category\\\": \\\"<dev-tools|agents-automation|model-releases|sdlc-ai|context-engineering|general>\\\", \\\"reasoning\\\": \\\"<one sentence>\\\"}\"\n    )\n\n    body = json_lib.dumps({\n        \"model\": \"mistral\",\n        \"prompt\": prompt,\n        \"stream\": False,\n        \"keep_alive\": \"10m\"\n    })\n\n    result.append({\"json\": {\n        \"id\": data.get('id', 0),\n        \"title\": title,\n        \"source_name\": source_name,\n        \"request_body\": body\n    }})\n\nreturn result"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        32
      ],
      "id": "ea0ff242-499a-4c79-8260-2142aa982c20",
      "name": "Prepare Prompt"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "5340ab1b-e7c8-4436-8c05-63283057a95a",
              "name": "article_id",
              "value": "={{ $('Prepare Prompt').item.json.id }}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1120,
        32
      ],
      "id": "7d93aed2-8b38-4bdc-93a8-1b5f0b5e5c3b",
      "name": "Add Article ID"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://host.docker.internal:11434/api/generate",
        "sendBody": true,
        "contentType": "raw",
        "rawContentType": "application/json",
        "body": "={{ $json.request_body }}",
        "options": {
          "timeout": 120000
        }
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.4,
      "position": [
        896,
        32
      ],
      "id": "b412e266-fd42-45c8-88fb-f04a24e5729e",
      "name": "Ollama Score Article",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {
        "language": "pythonNative",
        "pythonCode": "import sqlite3\nimport json as json_lib\n\ndb_path = \"/data/ai-pulse/ai-pulse.db\"\n\nitems = _items\narticle_id = 0\nrelevance_score = 0\ncategory = \"general\"\nreasoning = \"\"\nstatus = \"failed\"\n\nfor item in items:\n    data = item['json'] if 'json' in item else item\n    article_id = int(data.get('article_id', 0))\n    response_text = str(data.get('response', ''))\n\n    try:\n        clean = response_text.strip()\n        start = clean.find('{')\n        end = clean.rfind('}') + 1\n        if start >= 0 and end > start:\n            parsed = json_lib.loads(clean[start:end])\n            relevance_score = int(parsed.get('relevance_score', 0))\n            category = str(parsed.get('category', 'general'))\n            reasoning = str(parsed.get('reasoning', ''))\n            status = \"success\"\n    except:\n        relevance_score = 1\n        category = \"general\"\n        status = \"parse_error\"\n\n    if article_id > 0:\n        conn = sqlite3.connect(db_path)\n        cursor = conn.cursor()\n        cursor.execute(\"\"\"\n            UPDATE articles SET relevance_score = ?, category = ?\n            WHERE id = ?\n        \"\"\", (relevance_score, category, article_id))\n        conn.commit()\n        conn.close()\n\nreturn [{\"json\": {\"article_id\": article_id, \"score\": relevance_score, \"category\": category, \"reasoning\": reasoning, \"status\": status}}]"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1344,
        96
      ],
      "id": "6861ffc6-471e-4d22-becc-5fc3f0ad3029",
      "name": "Update Article Score",
      "onError": "continueRegularOutput"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        0,
        192
      ],
      "id": "eafd3614-4919-4cc8-8e41-7b4af3573dc9",
      "name": "When clicking \u2018Execute workflow\u2019"
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Unscored Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Unscored Articles": {
      "main": [
        [
          {
            "node": "Loop Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Loop Articles": {
      "main": [
        [],
        [
          {
            "node": "Prepare Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Prompt": {
      "main": [
        [
          {
            "node": "Ollama Score Article",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Add Article ID": {
      "main": [
        [
          {
            "node": "Update Article Score",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Ollama Score Article": {
      "main": [
        [
          {
            "node": "Add Article ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Article Score": {
      "main": [
        [
          {
            "node": "Loop Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Fetch Unscored Articles",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate",
    "availableInMCP": false
  },
  "versionId": "b43914e0-7e47-415c-b428-24463b7f581f",
  "id": "AF67hJBmcplOWDzq",
  "tags": []
}