{
  "name": "KP Case Agent Native n8n",
  "nodes": [
    {
      "parameters": {},
      "id": "72de32b5-fd49-4e2d-a283-b2908f611631",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        240,
        360
      ]
    },
    {
      "parameters": {
        "mode": "manual",
        "jsonOutput": "{\n  \"pythonBin\": \"C:/Users/civ/Desktop/n8n/kp_sender_python/.venv/Scripts/python.exe\",\n  \"prepareScriptPath\": \"C:/Users/civ/Desktop/n8n/kp_sender_python/src/n8n_prepare_rows.py\",\n  \"renderScriptPath\": \"C:/Users/civ/Desktop/n8n/kp_sender_python/src/n8n_render_row.py\",\n  \"startRow\": 1,\n  \"endRow\": 5,\n  \"outgoingStart\": 101,\n  \"runId\": \"n8n-native-demo\",\n  \"caseAgentMode\": \"auto_fix\",\n  \"caseAgentModel\": \"gpt-4o-mini\",\n  \"caseAgentAutoFixMinConfidence\": 0.9\n}"
      },
      "id": "a6d66782-7de4-4212-abca-bf31f45fb3fd",
      "name": "Workflow Config",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        500,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const cfg = $input.first().json;\nconst quote = (value) => `\"${String(value).replaceAll('\"', '\\\\\"')}\"`;\nconst parts = [\n  quote(cfg.pythonBin),\n  quote(cfg.prepareScriptPath),\n  '--start', String(cfg.startRow ?? 1),\n  '--end', String(cfg.endRow ?? cfg.startRow ?? 1),\n  '--outgoing-start', String(cfg.outgoingStart ?? 101),\n];\nreturn [{ json: { ...cfg, commandLine: parts.join(' ') } }];"
      },
      "id": "24144397-0269-4ebe-ba91-f65ec03e1dc4",
      "name": "Build Prepare Command",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        760,
        360
      ]
    },
    {
      "parameters": {
        "command": "={{ $json.commandLine }}"
      },
      "id": "7020cf25-7750-46a4-90f0-8161ddecc6f9",
      "name": "Prepare Rows",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        1020,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const raw = $json.stdout || '';\nconst parsed = JSON.parse(raw);\nif (!parsed.ok) {\n  throw new Error(parsed.error || 'Prepare step failed');\n}\nreturn (parsed.items || []).map((item) => ({\n  json: {\n    ...item,\n    workflowMeta: {\n      xlsxPath: parsed.xlsx_path,\n      selectedRange: parsed.selected_range,\n    },\n  },\n}));"
      },
      "id": "d0f6c9ca-4166-47ab-90f7-b9ff13e9e5c4",
      "name": "Expand Rows",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1280,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const cfg = $('Workflow Config').first().json;\nconst payload = ($json.reviews || []).map((review) => ({\n  field: review.field,\n  source_value: review.source_value,\n  generated_value: review.generated_value,\n  target_case: review.target_case,\n  context_sentence: review.context_sentence,\n  slot_instruction: review.slot_instruction,\n  slot_label: review.slot_label,\n}));\nconst prompt = [\n  '\u0422\u044b \u0430\u0433\u0435\u043d\u0442 \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0440\u0443\u0441\u0441\u043a\u0438\u0445 \u0444\u043e\u0440\u043c\u0443\u043b\u0438\u0440\u043e\u0432\u043e\u043a \u0432 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0445.',\n  '\u0422\u0432\u043e\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u043d\u0435 \u0443\u0433\u0430\u0434\u044b\u0432\u0430\u0442\u044c \u043f\u0430\u0434\u0435\u0436 \u043f\u043e \u0438\u043c\u0435\u043d\u0438 \u043f\u043e\u043b\u044f, \u0430 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c, \u043a\u0430\u043a\u043e\u0439 \u0438\u043c\u0435\u043d\u043d\u043e \u0442\u0435\u043a\u0441\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u0442\u043e\u044f\u0442\u044c \u0432 \u043f\u043e\u0437\u0438\u0446\u0438\u0438 [SLOT] \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0444\u0440\u0430\u0437\u044b \u0448\u0430\u0431\u043b\u043e\u043d\u0430.',\n  '\u0421\u043c\u043e\u0442\u0440\u0438 \u043d\u0430 \u0432\u0441\u044e \u0444\u0440\u0430\u0437\u0443 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0439 \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u0434\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0434\u043b\u044f [SLOT].',\n  '\u041d\u0435 \u043e\u0440\u0438\u0435\u043d\u0442\u0438\u0440\u0443\u0439\u0441\u044f \u043d\u0430 \u0441\u0442\u0430\u0440\u044b\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u044b \u0438\u0437 \u0448\u0430\u0431\u043b\u043e\u043d\u0430 \u043a\u0430\u043a \u043d\u0430 \u044d\u0442\u0430\u043b\u043e\u043d.',\n  '\u0415\u0441\u043b\u0438 \u0432\u043d\u0443\u0442\u0440\u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u0438 \u0435\u0441\u0442\u044c \u0443\u0441\u0442\u043e\u0439\u0447\u0438\u0432\u043e\u0435 \u043e\u0444\u0438\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u043d\u0430\u0438\u043c\u0435\u043d\u043e\u0432\u0430\u043d\u0438\u0435, \u043d\u0435 \u0441\u043a\u043b\u043e\u043d\u044f\u0439 \u0435\u0433\u043e \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u0431\u0435\u0437 \u044f\u0432\u043d\u043e\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438.',\n  '\u0415\u0441\u043b\u0438 generated_value \u0443\u0436\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0434\u043b\u044f \u043f\u043e\u0437\u0438\u0446\u0438\u0438 [SLOT], \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0435\u0440\u043d\u0438 status=\"ok\".',\n  '\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0439 status=\"fix\" \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0441\u043b\u0438 corrected_value \u0440\u0435\u0430\u043b\u044c\u043d\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0442 generated_value.',\n  '\u0415\u0441\u043b\u0438 \u0441\u043b\u0443\u0447\u0430\u0439 \u0441\u043f\u043e\u0440\u043d\u044b\u0439, \u0432\u0435\u0440\u043d\u0438 status=\"needs_review\".',\n  '\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043f\u043e\u043b\u044f \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u0435\u0440\u043d\u0438 confidence \u043e\u0442 0.0 \u0434\u043e 1.0.',\n  '\u0412\u0435\u0440\u043d\u0438 \u0442\u043e\u043b\u044c\u043a\u043e JSON-\u043c\u0430\u0441\u0441\u0438\u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432\u0438\u0434\u0430 {\"field\":\"...\", \"status\":\"ok|fix|needs_review\", \"corrected_value\":\"...\", \"confidence\":0.0, \"comment\":\"...\"}.',\n  '',\n  '\u0414\u0430\u043d\u043d\u044b\u0435 \u0434\u043b\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438:',\n  JSON.stringify(payload, null, 2),\n].join('\\n');\nreturn [{\n  json: {\n    ...$json,\n    openaiRequestBody: {\n      model: cfg.caseAgentModel || 'gpt-4o-mini',\n      response_format: { type: 'json_object' },\n      messages: [\n        {\n          role: 'user',\n          content: prompt,\n        },\n      ],\n    },\n  },\n}];"
      },
      "id": "5716d5ef-ca3d-4643-8100-59f1b09f8a07",
      "name": "Build AI Request",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1540,
        360
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.openai.com/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{$env.OPENAI_API_KEY}}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify($json.openaiRequestBody) }}",
        "options": {}
      },
      "id": "74bc50d0-b68a-4d7e-8c7e-852e7a8ccdc2",
      "name": "OpenAI Case Review",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1800,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const cfg = $('Workflow Config').first().json;\nconst reviews = $json.reviews || [];\nlet parsedItems = [];\nlet error = null;\ntry {\n  const raw = $json.choices?.[0]?.message?.content || '{}';\n  let parsed = JSON.parse(raw);\n  if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n    parsed = parsed.items || parsed.result || parsed.results || parsed;\n  }\n  parsedItems = Array.isArray(parsed) ? parsed : [];\n} catch (e) {\n  error = e.message;\n}\nconst reviewMap = new Map(reviews.map((review) => [review.field, review]));\nconst normalized = [];\nconst seen = new Set();\nfor (const item of parsedItems) {\n  const field = item?.field;\n  if (!field || !reviewMap.has(field)) continue;\n  const review = reviewMap.get(field);\n  const generated = String(review.generated_value || '').trim();\n  const corrected = String(item.corrected_value ?? generated).trim() || generated;\n  let status = String(item.status || 'needs_review').toLowerCase();\n  let confidence = Number(item.confidence ?? 0);\n  if (!Number.isFinite(confidence)) confidence = 0;\n  let comment = String(item.comment || '').trim();\n  if (!['ok', 'fix', 'needs_review'].includes(status)) status = 'needs_review';\n  if (corrected !== generated && status === 'ok') status = 'fix';\n  if (corrected === generated && status === 'fix') {\n    status = 'ok';\n    if (!comment) comment = '\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u0430\u044f \u0444\u043e\u0440\u043c\u0430 \u0443\u0436\u0435 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u0430.';\n  }\n  if ((status === 'ok' || status === 'fix') && confidence <= 0) confidence = 0.8;\n  normalized.push({\n    field,\n    source_value: review.source_value,\n    generated_value: generated,\n    status,\n    corrected_value: corrected,\n    confidence,\n    comment,\n  });\n  seen.add(field);\n}\nfor (const review of reviews) {\n  if (seen.has(review.field)) continue;\n  normalized.push({\n    field: review.field,\n    source_value: review.source_value,\n    generated_value: review.generated_value,\n    status: 'needs_review',\n    corrected_value: review.generated_value,\n    confidence: 0,\n    comment: error || '\u041f\u043e\u043b\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 \u0432 \u043e\u0442\u0432\u0435\u0442\u0435 AI-\u0430\u0433\u0435\u043d\u0442\u0430.',\n  });\n}\nconst summary = {\n  reviewed_fields_count: reviews.length,\n  ok_count: normalized.filter((item) => item.status === 'ok').length,\n  fix_count: normalized.filter((item) => item.status === 'fix').length,\n  needs_review_count: normalized.filter((item) => item.status === 'needs_review').length,\n};\nreturn [{\n  json: {\n    ...$json,\n    agent_result: {\n      enabled: true,\n      mode: cfg.caseAgentMode || 'auto_fix',\n      error,\n      items: normalized,\n      summary,\n    },\n  },\n}];"
      },
      "id": "ec252dcb-46d8-4711-8d3c-7b82203a8817",
      "name": "Normalize AI Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2060,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const cfg = $('Workflow Config').first().json;\nconst quote = (value) => `\"${String(value).replaceAll('\"', '\\\\\"')}\"`;\nconst payload = {\n  row: $json.row,\n  context: $json.context,\n  agent_result: $json.agent_result,\n};\nconst payloadBase64 = Buffer.from(JSON.stringify(payload), 'utf8').toString('base64');\nconst parts = [\n  quote(cfg.pythonBin),\n  quote(cfg.renderScriptPath),\n  '--payload-base64',\n  quote(payloadBase64),\n];\nreturn [{ json: { ...$json, renderCommand: parts.join(' ') } }];"
      },
      "id": "0bc64182-6609-40f5-8d1d-d98c5ac1f14f",
      "name": "Build Render Command",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2320,
        360
      ]
    },
    {
      "parameters": {
        "command": "={{ $json.renderCommand }}"
      },
      "id": "b06f11cb-e934-4c7b-bc82-6d8e335d2462",
      "name": "Render Documents",
      "type": "n8n-nodes-base.executeCommand",
      "typeVersion": 1,
      "position": [
        2580,
        360
      ]
    },
    {
      "parameters": {
        "jsCode": "const parsed = JSON.parse($json.stdout || '{}');\nreturn [{ json: parsed }];"
      },
      "id": "23491955-60ec-48c2-a59c-992730e14519",
      "name": "Parse Render Result",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2840,
        360
      ]
    },
    {
      "parameters": {
        "mode": "runOnceForAllItems",
        "jsCode": "const items = $input.all().map((entry) => entry.json);\nreturn [{\n  json: {\n    status: items.every((item) => item.ok !== false) ? 'success' : 'partial_or_failed',\n    totalItems: items.length,\n    folders: items.map((item) => ({\n      mun_name: item.mun_name,\n      output_folder: item.output_folder,\n      review_path: item.review_path,\n      case_agent_status: item.case_agent_status,\n      case_agent_summary: item.case_agent_summary,\n    })),\n  },\n}];"
      },
      "id": "aaf3f56d-b0f8-4c65-8db4-18521d7029b1",
      "name": "Final Summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3100,
        360
      ]
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Workflow Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Workflow Config": {
      "main": [
        [
          {
            "node": "Build Prepare Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Prepare Command": {
      "main": [
        [
          {
            "node": "Prepare Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Rows": {
      "main": [
        [
          {
            "node": "Expand Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Expand Rows": {
      "main": [
        [
          {
            "node": "Build AI Request",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build AI Request": {
      "main": [
        [
          {
            "node": "OpenAI Case Review",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Case Review": {
      "main": [
        [
          {
            "node": "Normalize AI Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalize AI Result": {
      "main": [
        [
          {
            "node": "Build Render Command",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Render Command": {
      "main": [
        [
          {
            "node": "Render Documents",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Render Documents": {
      "main": [
        [
          {
            "node": "Parse Render Result",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Render Result": {
      "main": [
        [
          {
            "node": "Final Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "28238eb1-fc0c-4f43-b544-fdd4d623c728",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "tags": []
}