{
  "id": "sYEXpXSAAF75tWKC",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "LLM Council",
  "tags": [],
  "nodes": [
    {
      "id": "9e8c8e51-b1e8-431b-b104-675d4bb2cc57",
      "name": "Initialize Variables",
      "type": "n8n-nodes-base.set",
      "position": [
        224,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "user-query",
              "name": "userQuery",
              "type": "string",
              "value": "={{ $json.chatInput }}"
            },
            {
              "id": "council-models",
              "name": "councilModels",
              "type": "array",
              "value": "=[\"openai/gpt-4o\", \"google/gemini-2.5-flash\", \"anthropic/claude-sonnet-4.5\",\"perplexity/sonar-pro-search\"]"
            },
            {
              "id": "chairman-model",
              "name": "chairmanModel",
              "type": "string",
              "value": "google/gemini-2.5-flash"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "11432589-2dd5-42c5-9a05-d85b7908fd23",
      "name": "Split by Model",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        368,
        0
      ],
      "parameters": {
        "include": "={{ $json.userQuery }}",
        "options": {},
        "fieldToSplitOut": "councilModels"
      },
      "typeVersion": 1
    },
    {
      "id": "f304d235-b1a1-41b2-88ef-15a3fa53123c",
      "name": "When chat message received",
      "type": "@n8n/n8n-nodes-langchain.chatTrigger",
      "position": [
        64,
        0
      ],
      "parameters": {
        "options": {}
      },
      "typeVersion": 1.4
    },
    {
      "id": "234da11b-e5f3-49a2-a5f7-502a9d713b9e",
      "name": "Stage 1: Query Model Answers",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        544,
        0
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "body": "={{   JSON.stringify({     model: $json.councilModels,     messages: [       {         role: \"user\",         content: $('Initialize Variables').item.json.userQuery       }     ]   }) }}",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "rawContentType": "application/json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "openRouterApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "c00798aa-8e82-453e-9030-3e875a5d1ec6",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 496,
        "height": 624,
        "content": "## INPUT and INITIALIZATION\n- Ask the question\n- Initialize the models"
      },
      "typeVersion": 1
    },
    {
      "id": "19faade7-71a3-493c-8d77-7bcc3b7ddfe4",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        496,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 480,
        "height": 624,
        "content": "## Stage 1: Individual Model Responses\n- Feed user query to **all** Council Member Models\n- Collect the Model responses  from **all** Council Member Models"
      },
      "typeVersion": 1
    },
    {
      "id": "f78c2ed3-b1a7-42e0-8a9d-4ed0c2e4df36",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        976,
        -304
      ],
      "parameters": {
        "color": 7,
        "width": 1328,
        "height": 624,
        "content": "## Stage 2: Response Evaluation and Ranking\n- **Anonymize model responses**\n- Ask models to evaluate and rank the answers"
      },
      "typeVersion": 1
    },
    {
      "id": "e61dbdc2-87d9-438e-a732-dbe74fd63467",
      "name": "Stage 2: Query Model Answer Rankings",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1776,
        0
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "body": "={{   JSON.stringify({     model: $json.councilModels,     messages: [       {         role: \"user\",         content: $json.rankingPrompt       }     ]   }) }}",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "rawContentType": "application/json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "openRouterApi"
      },
      "typeVersion": 4.2
    },
    {
      "id": "dd5219c2-c4a0-4f46-aa12-21114a02a92a",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 736,
        "height": 624,
        "content": "## Stage 3: The Chairman's verdict\n- The Chairman model synthesizes the Council's result\n"
      },
      "typeVersion": 1
    },
    {
      "id": "33fdeb1d-bfed-4d24-aa7f-e5bc604c3369",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1744,
        368
      ],
      "parameters": {
        "color": 7,
        "width": 560,
        "height": 624,
        "content": "## Output\n- Build a result report (as mail body) with:\n-- the initial query\n-- the Chairman verdict \n-- the Ranking\n-- the individual model answers"
      },
      "typeVersion": 1
    },
    {
      "id": "8e66ab1a-49c6-4f06-a344-5982824b6125",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        368
      ],
      "parameters": {
        "width": 992,
        "height": 624,
        "content": "## How it works:\nThis workflow is inspired by a vibe coding project of Andrej Karpathy: https://github.com/karpathy/llm-council .\nThe LLM Council serves as a moderation board to synthesize different LLM responses to the same prompt.\n- Stage 1: The same question / user query is answered by different LLMs, the \"Council Members\".\n- Stage 2: The answers are anonymized and then the same models must judge and rank all responses.\n- Stage 3: All this (the individual answers and the judgments and rankings of the models on their answers) is forwarded to a \"Council Chairman\" who synthesizes an overall answer (the Verdict) out of this input.\n- Besides this \"Chairman Verdict\", all other essential information is integrated in the output:\n\t- The original query\n\t- The Chairman Verdict\n\t- The ranking of each model for all responses\n\t- The original query responses of each model\n\n\n\n## Setup steps:\n1. Set up openRouter Credential:\n**Configure it in each of the HTTP Request nodes of Stage 1, 2 and 3.**\n2. Initialize the model variables in the node of the same name:\n**Use any model from https://openrouter.ai/models.**\n2a) councilModels -> Council Member LLMs:\n**All models that are asked to respond to the input query and subsequently evaluate and rank all responses.**\n2b) chairmanModel -> the Council Chairman LLM:\n**The model to summarize and issue the Council's verdict.**\n3. Define your mail output node:\n**You must have access to an SMTP mailbox and know the mail address you want to send the output to.**\n4. Start the chatTrigger to send your query to the LLM council.\n5. Check the workflow result in the configured mailbox.\n\n"
      },
      "typeVersion": 1
    },
    {
      "id": "9ed93ba9-221e-40e0-8c03-5a6b33051eee",
      "name": "Stage 3: Chairman Synthesis",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1616,
        672
      ],
      "parameters": {
        "url": "https://openrouter.ai/api/v1/chat/completions",
        "body": "={{   JSON.stringify({     model: $json.chairmanModel,     messages: [       {         role: \"user\",         content: $json.chairmanPrompt       }     ]   }) }}",
        "method": "POST",
        "options": {
          "timeout": 120000
        },
        "sendBody": true,
        "contentType": "raw",
        "sendHeaders": true,
        "authentication": "predefinedCredentialType",
        "rawContentType": "application/json",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "nodeCredentialType": "openRouterApi"
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "91f0f387-f9d5-4e46-a70b-202f9c17df0e",
      "name": "Stage 1: Extract Responses",
      "type": "n8n-nodes-base.set",
      "position": [
        704,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "extract-response",
              "name": "model",
              "type": "string",
              "value": "={{ $json.model }}"
            },
            {
              "id": "extract-content",
              "name": "response",
              "type": "string",
              "value": "={{ $json.choices[0].message.content }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "2e37b91f-b4da-4976-842c-e8e223242b36",
      "name": "Stage 1: Aggregate Responses",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        848,
        0
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "39f7355c-db9c-437a-8037-a8700515aa37",
      "name": "Stage 2: Create Anonymized Responses",
      "type": "n8n-nodes-base.set",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "create-labels",
              "name": "labeledResponses",
              "type": "array",
              "value": "={{ JSON.stringify(   $json.data.map((item, index) => ({     label: String.fromCharCode(65 + index),     model: item.model,     response: item.response,   })) ) }}"
            },
            {
              "id": "anonymized-text",
              "name": "anonymizedText",
              "type": "string",
              "value": "={{ $json.data\n  .map((item, index) => `Response ${String.fromCharCode(65 + index)}:\\n${item.response}`)\n  .join('\\n\\n') }}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "06fd85e8-2ff1-4bb6-9110-8666d1568fb5",
      "name": "Stage 2: Build Ranking Prompt",
      "type": "n8n-nodes-base.set",
      "position": [
        1232,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ranking-prompt",
              "name": "=rankingPrompt",
              "type": "string",
              "value": "={{\n`You are evaluating different responses to the following question:\n\nQuestion:\n${$('Initialize Variables').first().json.userQuery}\n\nHere are the responses from different models (anonymized):\n\n${$json.anonymizedText}\nYour task:\n1. First, evaluate each response individually. For each response, explain what it does well and what it does poorly.\n2. Then, at the very end of your response, provide a final ranking. \n\nIMPORTANT: Your final ranking MUST be formatted EXACTLY as follows:\n- Start with the line \"FINAL RANKING:\" (all caps, with colon)\n- Then list the responses from best to worst as a numbered list \n- Each line should be: number, period, space, then ONLY the response label (e.g., \"1. Response A\") \n- Do not add any other text or explanations in the ranking section \n\nExample of the correct format for your ENTIRE response: \nResponse A provides good detail on X but misses Y... \nResponse B is accurate but lacks depth on Z... \nResponse C offers the most comprehensive answer...  \n\nFINAL RANKING:\n1. Response C \n2. Response A \n3. Response B  \n\nNow provide your evaluation and ranking:`\n}}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "28fd25b9-b44f-4617-99aa-4947d449cc0b",
      "name": "Stage 2: Re-Add councilModels",
      "type": "n8n-nodes-base.set",
      "position": [
        1408,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "545b8760-cb12-4118-9632-fdeac63a457e",
              "name": "rankingPrompt",
              "type": "string",
              "value": "={{ $json.rankingPrompt }}"
            },
            {
              "id": "cff5bee4-58ef-4925-b23c-9d3fc81e3edf",
              "name": "councilModels",
              "type": "array",
              "value": "={{ $('Initialize Variables').item.json.councilModels }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "99a510c0-64b2-49b2-93ac-a28a3836ddfb",
      "name": "Stage 2: rankingPrompt per LLM",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        1584,
        0
      ],
      "parameters": {
        "include": "allOtherFields",
        "options": {},
        "fieldToSplitOut": "councilModels"
      },
      "typeVersion": 1
    },
    {
      "id": "ba8e9264-c6a3-474d-af2b-73e10db73a4e",
      "name": "Stage 2: Extract Rankings",
      "type": "n8n-nodes-base.set",
      "position": [
        1936,
        0
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "ranking-model",
              "name": "model",
              "type": "string",
              "value": "={{ $json.model }}"
            },
            {
              "id": "ranking-content",
              "name": "ranking",
              "type": "string",
              "value": "={{ $json.choices[0].message.content }}"
            },
            {
              "id": "parse-ranking",
              "name": "parsedRanking",
              "type": "array",
              "value": "={{ $json.choices[0].message.content.split('FINAL RANKING:')[1]?.match(/Response [A-Z]/g) || [] }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "f7abd792-9809-4b5a-bc53-4186f63b28be",
      "name": "Stage 3: Build ChairmanPrompt Part 1",
      "type": "n8n-nodes-base.set",
      "position": [
        1264,
        672
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "stage1-text",
              "name": "stage1Text",
              "type": "string",
              "value": "={{ ($('Stage 1: Aggregate Responses').item.json.data || $('Stage 1: Aggregate Responses').item.json[0].data).map(r => `Model: ${r.model}\\nResponse: ${r.response}`).join('\\n\\n') }}"
            },
            {
              "id": "stage2-text",
              "name": "stage2Text",
              "type": "string",
              "value": "={{ $json.stage2Results[0].data.map(r => `Model: ${r.model}\\nRanking: ${r.ranking}`).join('\\n\\n') }}"
            }
          ]
        },
        "includeOtherFields": true
      },
      "typeVersion": 3.4
    },
    {
      "id": "0566f6cf-1c1a-41ff-9dee-27387305c98c",
      "name": "Stage 3: Build Chairman Prompt Part 2",
      "type": "n8n-nodes-base.set",
      "position": [
        1440,
        672
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "chairman-prompt",
              "name": "=chairmanPrompt",
              "type": "string",
              "value": "=You are the Chairman of an LLM Council. Multiple AI models have provided responses to a user's question, and then ranked each other's responses.\n\n# Original Question: {{ $('Initialize Variables').item.json.userQuery }}\n\n# STAGE 1 - Individual Responses from each model:\n{{ $json.stage1Text }}\n\n# STAGE 2 - Peer Rankings from each model\n{{ $json.stage2Text }}\n\n# Your TASK\nAs the Chairman of the LLM council, you must synthesize all of this information into a single, comprehensive, accurate answer to the user's original question. Consider:\n- The individual responses and their insights\n- The peer rankings and what they reveal about response quality\n- Any patterns of agreement or disagreement\n\nProvide a clear, well-reasoned final answer that represents the council's collective wisdom:"
            },
            {
              "id": "c9e0da1c-18c6-42ac-9f9a-cee2c35146bf",
              "name": "chairmanModel",
              "type": "string",
              "value": "={{ $('Initialize Variables').item.json.chairmanModel }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "cdaa1b7e-2d8d-4610-9d4e-0f366286401a",
      "name": "Stage 4: Format Output Part 1",
      "type": "n8n-nodes-base.set",
      "position": [
        1792,
        672
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "chairman-model-used",
              "name": "chairmanModel",
              "type": "string",
              "value": "={{ $('Initialize Variables').item.json.chairmanModel }}"
            },
            {
              "id": "final-response",
              "name": "finalResponse",
              "type": "string",
              "value": "={{ $json.choices[0].message.content }}"
            },
            {
              "id": "aggregate-rankings",
              "name": "aggregateRankings",
              "type": "string",
              "value": "={{ $('Stage 3: Calculate Aggregate Rankings').item.json.aggregateRankings }}"
            },
            {
              "id": "stage1-responses",
              "name": "stage1Responses",
              "type": "string",
              "value": "={{ $('Stage 1: Aggregate Responses').item.json }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "8891adea-9665-4813-8068-564b873c8138",
      "name": "Stage 4: Format Output Part 2",
      "type": "n8n-nodes-base.set",
      "position": [
        1984,
        672
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "90ea0f47-c094-4dcc-8a22-3375b4c3e532",
              "name": "MailBody",
              "type": "string",
              "value": "={{\n`You are evaluating different responses to the following question:\n##############\nQuestion:\n##############\n${$('Initialize Variables').first().json.userQuery}\n---------------------------------------\n##############\nFINAL RESPONSE\n##############\nof the chairman model ( ${$json.chairmanModel} ) is:\n\n${$json.finalResponse}\n\n---------------------------------------\n##############\nRANKINGS\n##############\n\nThis was the aggregated ranking of the models' mutual evaluation that has guided the chairman model (lower ranking is better):\n\n${$('Stage 3: Calculate Aggregate Rankings').first().json.aggregateRankings.map(r => 'Model: ' + r.model + '\\nAverage Rank: ' + r.averageRank + '\\nRankings Count: ' + r.rankingsCount).join('\\n\\n------\\n\\n')}\n\nbased on the following per-model-ranking:\n\n${$('Stage 2: Aggregate').first().json.data.map(r => 'Model: ' + r.model + '\\nRanked:\\n' + r.parsedRanking.map((resp, i) => (i + 1) + '. ' + resp).join('\\n')).join('\\n\\n------\\n\\n')}\n\n\n---------------------------------------\n####################\nINDIV. MODEL ANSWERS\n####################\nFor reference: these were the individual responses from each different models:\n\n${$('Stage 2: Create Anonymized Responses').first().json.labeledResponses.map(r => 'Response ' + r.label + ' (' + r.model + '):\\n' + r.response).join('\\n\\n------------\\n\\n')}\n\n`\n}}\n"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "05670818-5ce9-4f61-8cde-4c2b2e41abf5",
      "name": "Stage 4: Send Result per Email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        2176,
        672
      ],
      "parameters": {
        "text": "={{ $json.MailBody }}",
        "options": {
          "appendAttribution": false
        },
        "subject": "=LLM-Council-Resultat vom: {{ new Date().toLocaleDateString('en-GB').replace(/\\//g, '-') }}\n",
        "toEmail": "user@example.com",
        "fromEmail": "user@example.com",
        "emailFormat": "text"
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 2.1
    },
    {
      "id": "4d1c1bb1-655f-427e-89fc-dddc7cc19bf6",
      "name": "Stage 2: Aggregate",
      "type": "n8n-nodes-base.aggregate",
      "position": [
        2112,
        0
      ],
      "parameters": {
        "options": {},
        "aggregate": "aggregateAllItemData"
      },
      "typeVersion": 1
    },
    {
      "id": "be8a9bc5-b8d6-4ea2-8470-8f648727bed6",
      "name": "Stage 3: Calculate Aggregate Rankings",
      "type": "n8n-nodes-base.code",
      "position": [
        1088,
        672
      ],
      "parameters": {
        "jsCode": "// Calculate aggregate rankings\nconst stage2Results = $input.all();\nconst labeledResponses = $('Stage 2: Create Anonymized Responses').item.json.labeledResponses;\n\n// Create label to model mapping\nconst labelToModel = {};\nlabeledResponses.forEach(item => {\n  labelToModel[`Response ${item.label}`] = item.model;\n});\n\n// Track positions for each model\nconst modelPositions = {};\n\nstage2Results.forEach(ranking => {\n  // Access the data array first\n  const dataArray = ranking.json.data;\n  \n  // Iterate through each ranking in the data array\n  dataArray.forEach(rankingData => {\n    const parsedRanking = rankingData.parsedRanking;\n    \n    parsedRanking.forEach((label, index) => {\n      const position = index + 1;\n      const modelName = labelToModel[label];\n      \n      if (modelName) {\n        if (!modelPositions[modelName]) {\n          modelPositions[modelName] = [];\n        }\n        modelPositions[modelName].push(position);\n      }\n    });\n  });\n});\n\n// Calculate average rankings\nconst aggregateRankings = Object.entries(modelPositions).map(([model, positions]) => {\n  const avgRank = positions.reduce((a, b) => a + b, 0) / positions.length;\n  return {\n    model: model,\n    averageRank: Math.round(avgRank * 100) / 100,\n    rankingsCount: positions.length\n  };\n});\n\n// Sort by average rank (lower is better)\naggregateRankings.sort((a, b) => a.averageRank - b.averageRank);\n\nreturn {\n  json: {\n    aggregateRankings: aggregateRankings,\n    labelToModel: labelToModel,\n    stage2Results: stage2Results.map(r => r.json)\n  }\n};"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "a134c2b7-eb0d-4c41-b072-2909c1fcfe7a",
  "connections": {
    "Split by Model": {
      "main": [
        [
          {
            "node": "Stage 1: Query Model Answers",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Aggregate": {
      "main": [
        [
          {
            "node": "Stage 3: Calculate Aggregate Rankings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Initialize Variables": {
      "main": [
        [
          {
            "node": "Split by Model",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Extract Rankings": {
      "main": [
        [
          {
            "node": "Stage 2: Aggregate",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 1: Extract Responses": {
      "main": [
        [
          {
            "node": "Stage 1: Aggregate Responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When chat message received": {
      "main": [
        [
          {
            "node": "Initialize Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 3: Chairman Synthesis": {
      "main": [
        [
          {
            "node": "Stage 4: Format Output Part 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 1: Aggregate Responses": {
      "main": [
        [
          {
            "node": "Stage 2: Create Anonymized Responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 1: Query Model Answers": {
      "main": [
        [
          {
            "node": "Stage 1: Extract Responses",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Build Ranking Prompt": {
      "main": [
        [
          {
            "node": "Stage 2: Re-Add councilModels",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Re-Add councilModels": {
      "main": [
        [
          {
            "node": "Stage 2: rankingPrompt per LLM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 4: Format Output Part 1": {
      "main": [
        [
          {
            "node": "Stage 4: Format Output Part 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 4: Format Output Part 2": {
      "main": [
        [
          {
            "node": "Stage 4: Send Result per Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: rankingPrompt per LLM": {
      "main": [
        [
          {
            "node": "Stage 2: Query Model Answer Rankings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Create Anonymized Responses": {
      "main": [
        [
          {
            "node": "Stage 2: Build Ranking Prompt",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 2: Query Model Answer Rankings": {
      "main": [
        [
          {
            "node": "Stage 2: Extract Rankings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 3: Build ChairmanPrompt Part 1": {
      "main": [
        [
          {
            "node": "Stage 3: Build Chairman Prompt Part 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 3: Build Chairman Prompt Part 2": {
      "main": [
        [
          {
            "node": "Stage 3: Chairman Synthesis",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Stage 3: Calculate Aggregate Rankings": {
      "main": [
        [
          {
            "node": "Stage 3: Build ChairmanPrompt Part 1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}