{
  "id": "esKLjvDFKU61J141",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Curate Contributor-Friendly Issues with AI and Send GitHub Newsletter via Email copy",
  "tags": [],
  "nodes": [
    {
      "id": "f0427aa1-48aa-487a-98ef-f42b5ea6bffe",
      "name": "When clicking \u2018Execute workflow\u2019",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        816,
        592
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "ac914444-a804-4da0-b479-6282a656ec03",
      "name": "Load Repo Info",
      "type": "n8n-nodes-base.code",
      "position": [
        1024,
        592
      ],
      "parameters": {
        "jsCode": "return {\n      owner: \"vercel\",\n      name: \"next.js\"\n  }"
      },
      "typeVersion": 2
    },
    {
      "id": "a24e69e7-f24b-425f-8623-db39c8e81b3f",
      "name": "Get Issue From Github",
      "type": "n8n-nodes-base.graphql",
      "position": [
        1216,
        592
      ],
      "parameters": {
        "query": "=\n    query {\n        repository(owner: \"{{ $json.owner }}\", name: \"{{ $json.name }}\") {\n            releases (first: 1, orderBy: { field: CREATED_AT, direction: DESC }) {\n                nodes {\n                    name\n                    description\n                    createdAt\n\t\t\t\t\t\t\t\t\t\turl\n                }\n            }\n            issues(first: 10, states: OPEN, orderBy: { field: CREATED_AT, direction: DESC }) {\n                nodes {\n                    title\n                    url\n                    body\n                    author {\n                        ... on User {\n                            login\n                        }\n                    }\n                    issueType {\n                        name\n                    }\n                    labels(first: 5) {\n                        edges {\n                            node {\n                                name\n                            }\n                        }\n                    }\n                    comments(first: 10) {\n                        nodes {\n                            body\n                        }\n                    }\n                }\n            }\n        }\n    }\n",
        "endpoint": "https://api.github.com/graphql",
        "variables": "=",
        "authentication": "headerAuth"
      },
      "credentials": {
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1,
      "alwaysOutputData": true
    },
    {
      "id": "d9fead98-632b-4ede-b96f-d33969ae5a47",
      "name": "get Top Fit Issues",
      "type": "n8n-nodes-base.code",
      "position": [
        2128,
        592
      ],
      "parameters": {
        "jsCode": "const suitabilityOrder = { 'high': 0, 'medium': 1, 'low': 2 };\n  const sortedIssues = $input.first().json.issues.sort((a, b) => {\n      const suitabilityA = suitabilityOrder[a.issueSuitability.level];\n      const suitabilityB = suitabilityOrder[b.issueSuitability.level];\n      return suitabilityA - suitabilityB\n  }).slice(0, 3);\n\n  return {\n      \"issues\" : sortedIssues\n  }"
      },
      "executeOnce": false,
      "retryOnFail": false,
      "typeVersion": 2
    },
    {
      "id": "b4c4e831-93bb-46e4-b1c8-c3284135e11b",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        3184,
        608
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "fieldsToMatchString": "issueURL"
      },
      "executeOnce": false,
      "typeVersion": 3.2
    },
    {
      "id": "a6e5bf4c-cd55-4898-bce5-86b36bfaf8c8",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -384,
        656
      ],
      "parameters": {
        "width": 736,
        "height": 928,
        "content": "## Usecases\nReceive a periodic newsletter featuring curated, contributor-friendly issues from your favorite repositories.\n\nBy regularly reviewing active issues and new releases, you'll naturally develop stronger habits around open source contribution as your brain starts recognizing these projects as important.\n\n\n### How it works\n* Collects the latest issues, comments, and recent commits using the GitHub API\n* Uses an AI model to select up to three beginner-friendly issues worth contributing to\n* Summarizes each issue\u2014including contribution guidance and relevance\u2014based on Deepwiki MCP data\n* Converts the summary into HTML and delivers it as an email newsletter\n\n### Requirements\n* [Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token)\n* [Openrouter API key](https://openrouter.ai/settings/keys)\n* [Google App Password](https://myaccount.google.com/apppasswords)\n* Verify that your target open-source project is indexed\nat\u00a0https://deepwiki.com/{owner}/{repo}\u00a0(e.g.\u00a0https://deepwiki.com/vercel/next.js)\n\n### How to Use\n1. Update the \"Load repo info\" configuration with your target repository's owner and name (e.g., owner: `vercel`, repo: `next.js`)\n2. Add your GitHub Personal Access Token to the credentials of the \"Get Issues from GitHub\" node\n3. Connect your OpenRouter API key to all models linked to the Agent node\n4. Add your Google App Password to the \"Send email\" node credentials\n5. Enter the email address of the account authenticated with the Google App Password in both the \"to email\" and \"from email\" fields\u2014the newsletter will be sent to that address\n\n### Customization\n* Modify the maximum number of contributor-friendly issues retrieved in the \"Get Top Fit Issues\" node\n* For improved results, adjust the models connected to the Agent node\n* Refine the criteria for \"contributor-friendliness\" in the \"IssueRank Agent\" node\n\n### Cron Setup\n- Replace the manual trigger with a Schedule Trigger node or another scheduling-capable node\n- If you don't have an n8n Cloud account, use this [alternative](https://github.com/dst03106/n8n-issue-collector?tab=readme-ov-file#%EF%B8%8F-how-to-use) instead: fork the repository and follow the setup instructions\nd follow the setup instructions.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "af2d0fef-f0fa-470b-8e68-81a6eda335a0",
      "name": "Sticky Note7",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3488,
        400
      ],
      "parameters": {
        "color": 4,
        "width": 584,
        "height": 636,
        "content": "# 3. Generating a Newsletter Title\n### Creates a newsletter title based on the issue content."
      },
      "typeVersion": 1
    },
    {
      "id": "1adc7dd3-6d4d-4751-8cee-18ed435e6ce4",
      "name": "Sticky Note8",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2496,
        400
      ],
      "parameters": {
        "color": 5,
        "width": 808,
        "height": 636,
        "content": "# 2.Summarizing Contribution Details\n### Generates information about contribution methods and levels (such as contribution suitability and technical difficulty using Deepwiki MCP. "
      },
      "typeVersion": 1
    },
    {
      "id": "806d707a-ae5a-42cd-bf27-2ce346d2e8a3",
      "name": "Sticky Note9",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4160,
        400
      ],
      "parameters": {
        "width": 696,
        "height": 636,
        "content": "# 4. Converting Issue Data to HTML and Sending via email\n### Converts issue-related data into HTML format and sends it through email."
      },
      "typeVersion": 1
    },
    {
      "id": "172e044c-6a6b-4bf5-89ac-4d46c0dce150",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        80
      ],
      "parameters": {
        "width": 508,
        "height": 488,
        "content": "## Newsletter\n![](https://lh3.googleusercontent.com/d/1iHWRiMm5mrWkA2r-t81Uzykqcss2Pl8G)"
      },
      "typeVersion": 1
    },
    {
      "id": "9e93773e-6cd5-4d0d-9b7e-51033501671d",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        224,
        1632
      ],
      "parameters": {
        "width": 444,
        "height": 1064,
        "content": "## Newsletter\n![](https://lh3.googleusercontent.com/d/1Pfdp4mttjAM9E7rV_y5qDbhgqmunECF_)"
      },
      "typeVersion": 1
    },
    {
      "id": "36b2de08-ff2b-4728-8396-2d8ec9ad1520",
      "name": "convert MJML to HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        4464,
        608
      ],
      "parameters": {
        "jsCode": "const mjml2html = require('mjml');\n  return {\n      htmlOutput: mjml2html($input.first().json.issueHTML)\n  }"
      },
      "executeOnce": false,
      "typeVersion": 2
    },
    {
      "id": "de11b35d-7f55-41f4-a85b-30cac04f6529",
      "name": "convert MJML",
      "type": "n8n-nodes-base.code",
      "position": [
        4240,
        608
      ],
      "parameters": {
        "jsCode": "const createBulletedList = (items) => {\n    if (!Array.isArray(items) || items.length === 0) {\n        return '';\n    }\n    return items.map(item => `<mj-text mj-class=\"section-content\">\u2022 ${item}</mj-text>`).join('<br/>');\n};\nconst latestReleaseInfo = $('Issue Analysis Langchain Agent').first().json.latestRelease;\nlet latestRelease = `\n    <mj-section padding=\"15px\">\n        <mj-column border=\"3px solid #52af0f\">\n            <mj-text mj-class=\"section-title\">\ud83d\ude80 Latest Release <a href=\"${latestReleaseInfo.url}\">(${latestReleaseInfo.name})</a></mj-text>\n\t\t`;\nfor (const { category, descriptions } of latestReleaseInfo.details) {\n\tlatestRelease += `<mj-text mj-class=\"section-content\" font-weight=\"bold\">[${category}]</mj-text>`;\n\tlatestRelease += createBulletedList(descriptions);\n}\nlatestRelease += '</mj-column></mj-section>';\nlet summary = `\n    <mj-section padding=\"15px\">\n        <mj-column border=\"3px solid #193404\">\n            <mj-text mj-class=\"section-title\">\ud83d\udccc Quick Summary</mj-text>\n            ${createBulletedList($('Merge').all().map(item => item.json.summary))}\n        </mj-column>\n    </mj-section>`;\n\nlet issues = [];\n\nfor (const issue of $('Merge').all()) {\n    let issueInfo = `<mj-text mj-class=\"issue-title\">${issue.json.issueTitle}</mj-text>\n                    <mj-spacer/>\n                    <mj-text mj-class=\"section-title\">\ud83d\udd11 Keywords</mj-text>\n                    <mj-text mj-class=\"section-content\">${issue.json.keyword.join(', ')}</mj-text>\n\t\t\t\t\t\t\t\t\t\t`;\n\n\t\tlet analogy = `<mj-text mj-class=\"section-title\">\ud83d\udd04 Analogy</mj-text>`;\n\t\tanalogy += createBulletedList(issue.json.analogy)\t\n\n\t\tlet issueDescription = `<mj-text mj-class=\"section-title\">\ud83e\uddfe Issue Description</mj-text>`;\n\t\tissueDescription += createBulletedList(issue.json.issueDescription);\n\n\t\tlet rootCause = `<mj-text mj-class=\"section-title\">\ud83e\udde9 Root Cause</mj-text>`\n\t\trootCause += createBulletedList(issue.json.rootCause);\n\n    let resolutionApproach = `<mj-text mj-class=\"section-title\">\ud83d\udee0\ufe0f Resolution Approach</mj-text><mj-text font-size=\"14px\" line-height=\"1.6\">`;\n    resolutionApproach += createBulletedList(issue.json.resolutionApproach);\n    resolutionApproach += '</mj-text>';\n\n    let issueSuitability = `<mj-text mj-class=\"section-title\">\u2705 Issue Suitability: ${issue.json.issueSuitability.level}</mj-text><mj-text font-size=\"14px\" line-height=\"1.6\">`\n    issueSuitability += createBulletedList(issue.json.issueSuitability.reasons);\n    issueSuitability += '</mj-text>';\n    \n    let technicalDifficulty = `<mj-text mj-class=\"section-title\">\ud83e\uddd7 Technical Difficulty: ${issue.json.technicalDifficulty.level}</mj-text><mj-text font-size=\"14px\" line-height=\"1.6\">`\n    technicalDifficulty += createBulletedList(issue.json.technicalDifficulty.reasons);\n    technicalDifficulty += '</mj-text>';\n\n    let issuelink = `<mj-text mj-class=\"section-title\"><p>\ud83d\udc49 Go to the Issue <a href=\"${issue.json.issueURL}\">(Link)</a></p></mj-text>`;\n    let deepwikiLink = `<mj-text mj-class=\"section-title\"><p>\ud83c\udf00 Check the code-level explanation on Deepwiki <a href=\"${issue.json.deepwikiLink}\">(Link)</a></p></mj-text>`;\n\n    issues.push(\n        issueInfo + analogy + issueDescription +\n        rootCause + resolutionApproach + issueSuitability +\n        technicalDifficulty + issuelink + deepwikiLink\n    );\n}\n\nreturn {\n    issueHTML: `\n        <mjml>\n          <mj-head>\n                <mj-attributes>\n                    <mj-class name=\"issue-title\" font-size=\"22px\" />\n                    <mj-class name=\"section-title\" font-size=\"15px\" font-weight=\"bold\"/>\n                    <mj-class name=\"section-content\" font-size=\"14px\" line-height=\"1.6\"/>\n                </mj-attributes>\n            </mj-head>\n            <mj-body>\n                <mj-section>\n                    <mj-column>\n                        <mj-image src=\"https://lh3.googleusercontent.com/d/1VYyXuiNQOnHCBELXh4_6ZDEoL30x7OQk\" width=\"1000\" height=\"150\"/>\n                    </mj-column>\n                </mj-section>\n                <mj-divider  border-width=\"1px\" border-color=\"#D3D3D3\"/> \n                <mj-wrapper>\n                    ${latestRelease}\n                    ${summary}\n                    <mj-section>\n                        <mj-column>\n                            ${issues.join(`<mj-divider  border-width=\"1px\" border-color=\"#D3D3D3\"/>`)}\n                        </mj-column>\n                    </mj-section>\n                </mj-wrapper>\n        </mj-body>\n        </mjml>`\n}"
      },
      "executeOnce": false,
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "af3bd974-1a38-4016-866e-8564dbc654e0",
      "name": "Send email",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        4688,
        608
      ],
      "parameters": {
        "html": "={{ $('convert MJML to HTML').item.json.htmlOutput.html }}",
        "options": {},
        "subject": "=[Issue Report] {{ $('Load Repo Info').first().json.owner }}/{{ $('Load Repo Info').first().json.name }} - {{ $('Title Generator Langchain Agent').first().json.title }}",
        "toEmail": "",
        "fromEmail": ""
      },
      "credentials": {
        "smtp": {
          "name": "<your credential>"
        }
      },
      "executeOnce": false,
      "typeVersion": 2.1
    },
    {
      "id": "e8f85c79-afb3-4921-a108-00060217b4ce",
      "name": "Deepwiki Langchain agent",
      "type": "@n8n/n8n-nodes-langchain.code",
      "position": [
        2864,
        688
      ],
      "parameters": {
        "code": {
          "execute": {
            "code": "const { createAgent, createMiddleware, modelRetryMiddleware, providerStrategy } = require(\"langchain\");\nconst { traceable } = require(\"langsmith/traceable\");\nconst { AIMessage } = require(\"@langchain/core/messages\");\nconst { StateGraph, START, END, Annotation, Send } = require(\"@langchain/langgraph\");\nconst { ToolNode } = require(\"@langchain/langgraph/prebuilt\");\nconst { MultiServerMCPClient } = require(\"@langchain/mcp-adapters\");\n\nconst languageModel = await this.getInputConnectionData('ai_languageModel', 0);\n\nconst mcpClient = new MultiServerMCPClient({  \n\tdeepwiki: {\n\t\ttransport: \"http\",\n\t\turl: \"https://mcp.deepwiki.com/mcp\",\n\t},\n});\nconst tools = await mcpClient.getTools()\nconst askQuestionTool = tools.filter(t => t.name === \"ask_question\")[0];\n\nconst { owner, name } = $('Load Repo Info').item.json;\nconst repoName = `${owner}/${name}`;\nconst issues = $('get Top Fit Issues').item.json.issues;\n\nconst DeepwikiResponseSchema = $('Deepwiki Response Schema').item.json;\nconst wrappedSchema = {\n\ttype: \"object\",\n\tproperties: {\n\t\toutput: DeepwikiResponseSchema\n\t},\n\trequired: [\"output\"]\n};\n\nconst getLanguageDisplayName = (code) => {\n\ttry {\n\t\treturn new Intl.DisplayNames([code], { type: 'language' }).of(code);\n\t} catch {\n\t\treturn '';\n\t}\n};\n\nconst buildUserPrompt = (deepwikiResponse) => `\n\t[ROLE]\n\tYou are an AI assistant that processes DeepWiki GitHub issue analysis.\n\t\n\t[OUTPUT RULES]\n\tYou must format your output as a JSON value that adheres to a given \"JSON Schema\" instance.\n\t\"JSON Schema\" is a declarative language that allows you to annotate and validate JSON documents.\n\tDo not include markdown code blocks in the output.\n\tOutput ONLY the JSON object. No additional text.\n\n\t[TRANSLATE EXAMPLE]\n\tko-KR -> ${getLanguageDisplayName(\"ko-KR\")}\n\n\t[JSON SCHEMA]\n\t${JSON.stringify(wrappedSchema, null, 2)}\n\n\t[TASK]\n\tExtract and return ONLY a JSON object following the exact schema.\n\tPreserve the full length and content of the original text rather than summarizing.\n\n\t[REQUIREMENTS]\n\t- All content must be in \"ko-KR\"\n\t- translationLanguageCode should be \"ko-KR\"\n\t- deepwikiLink must start with https://deepwiki.com/\n\t- technicalDifficulty.level must be one of: High, Medium, Low\n\t- keyword should contain 1-5 relevant keywords\n\n\t[EXAMPLE OUTPUT]\n\t{\n\t\t\"output\": {\n\t\t\t\"translationLanguageCode\": \"en\",\n\t\t\t\"deepwikiLink\": \"https://deepwiki.com/search/here-is-a-github-issue-title-s_d2cf1a6c-1109-479c-986c-40cc2fa1f1c1\",\n\t\t\t\"rootCause\": \"Memory leak occurs during component rendering due to missing cleanup\",\n\t\t\t\"resolutionApproach\": [\n\t\t\t\t\"Add useEffect cleanup function\",\n\t\t\t\t\"Implement event listener removal logic\"\n\t\t\t],\n\t\t\t\"technicalDifficulty\": {\n\t\t\t\t\"level\": \"Medium\",\n\t\t\t\t\"reasons\": [\n\t\t\t\t\t\"Requires understanding of React lifecycle\",\n\t\t\t\t\t\"Need memory profiling experience\"\n\t\t\t\t]\n\t\t\t},\n\t\t\t\"summary\": \"Resolve React component memory leak by implementing proper cleanup function\",\n\t\t\t\"keyword\": [\"React\", \"memory leak\", \"useEffect\", \"cleanup\"],\n\t\t\t\"analogy\": \"Like leaving a faucet running - without cleanup, memory keeps leaking continuously\"\n\t\t}\n\t}\n\n\t[OUTPUT FORMAT]\n\t- Return JSON only\n\t- No extra text\n\n\t[INPUT]\n\tDeepWiki Analysis:\n\t${deepwikiResponse}\n`;\n\nasync function deepwikiToolNode({ issue }) {\n\tconst toolCall = {\n\t\tname: \"ask_question\",\n\t\targs: {\n\t\t\trepoName: repoName,\n\t\t\tquestion: `Here is a GitHub issue.\n\t\t\t\t Title: ${issue.issueTitle}\n\t\t\t\t Body: ${issue.issueDescription}\n\t\t\t\t How can this issue be resolved, what is its root cause, what is the recommended resolution approach, what is the technical difficulty, and what is a simple analogy for the issue and its resolution approach? Please provide the answer in ko-KR.`\n\t\t},\n\t\tid: `call_${Date.now()}`,\n\t\ttype: \"tool\"\n\t};\n\tconst aiMessage = new AIMessage({\n\t\tcontent: \"\",\n\t\ttool_calls: [toolCall]\n\t});\n\tconst result = await new ToolNode([askQuestionTool]).invoke({\n\t\tmessages: [aiMessage]\n\t});\n\treturn { deepwikiResponses: [{ \n\t\tdeepwikiResponse: result.messages.at(-1)?.content,\n\t\tissueURL: issue.issueURL\t\n\t}]};\n}\n\nclass TimeoutError extends Error {\n\tconstructor(message = 'Operation timed out') {\n\t\tsuper(message);\n\t\tthis.name = 'TimeoutError';\n\t}\n}\n\nconst timeoutMiddleware = (timeout = 90 * 1000) => {\n\tconst middlewareName = \"timeoutMiddleware\";\n\treturn createMiddleware({\n\t\tname: middlewareName,\n\t\twrapModelCall: async (request, handler) => {\n\t\t\tlet timeoutObj;\n\t\t\ttry {\n\t\t\t\treturn await Promise.race([\n\t\t\t\t\thandler(request),\n\t\t\t\t\tnew Promise((_, reject) => {\n\t\t\t\t\t\ttimeoutObj = setTimeout(() => reject(new TimeoutError()), timeout);\n\t\t\t\t\t})\n\t\t\t\t]);\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timeoutObj);\n\t\t\t}\n\t\t},\n\t});\n};\n\nconst jitterMiddleware = createMiddleware({\n\tname: \"jitterMiddleware\",\n\twrapModelCall: async (request, handler) => {\n\t\tconst delay = Math.floor(Math.random() * 100);\n\t\tawait new Promise(resolve => setTimeout(resolve, delay));\n\t\treturn handler(request);\n\t}\n});\n\nconst validateResponseMiddleware = createMiddleware({\n\tname: \"validateResponseDeepwikiMiddleware\",\n\tafterModel: {\n\t\tcanJumpTo: [\"model\"],\n\t\thook: (state) => {\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\t\t\tif (!lastMessage.content?.trim()) {\n\t\t\t\treturn { jumpTo: \"model\" }\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n});\n\nconst agent = createAgent({\n\tmodel: languageModel,\n\tresponseFormat: providerStrategy(wrappedSchema),\n\tmiddleware: [\n\t\tvalidateResponseMiddleware,\n\t\tmodelRetryMiddleware({\n\t\t\tmaxRetries: 2,\n\t\t\tbackoffFactor: 2.0,\n\t\t\tinitialDelayMs: 5 * 1000,\n\t\t\tjitter: true,\n\t\t\tonFailure: \"error\",\n\t\t}),\n\t\tjitterMiddleware,\n\t\ttimeoutMiddleware(120 * 1000),\n\t]\n});\nconst outputParser = await this.getInputConnectionData('ai_outputParser', 0);\n\nasync function reasonNode({ deepwikiResponse, issueURL }) {\n\tconst userPrompt = buildUserPrompt(deepwikiResponse);\n\tconst reasonResult = await agent.invoke({ messages: [{ role: \"user\", content: userPrompt }] });\n\tconst aiMessage = reasonResult.messages.findLast(m => m.type === \"ai\")?.content;\n\tconst parsedMessage = await outputParser.parse(aiMessage);\n\tparsedMessage.output.issueURL = issueURL\n\treturn { finalAnswers: [parsedMessage.output] };\n}\n\nconst MessagesState = Annotation.Root({\n\tdeepwikiResponses: Annotation({\n\t\treducer: (current, update) => current.concat(update),\n\t\tdefault: () => [],\n\t}),\n\tfinalAnswers: Annotation({\n\t\treducer: (current, update) => current.concat(update),\n\t\tdefault: () => [],\n\t}),\n});\nconst workflow = new StateGraph(MessagesState)\n\t.addNode(\"deepwikiTool\", deepwikiToolNode)\n\t.addNode(\"reason\", reasonNode)\n\t.addConditionalEdges(START, (_state) => {\n\t\treturn issues.map((issue) => new Send(\"deepwikiTool\", { issue }));\n\t})\n\t.addConditionalEdges(\"deepwikiTool\", ({ deepwikiResponses }) => {\n\t\treturn deepwikiResponses.map(({ deepwikiResponse, issueURL }) => new Send(\"reason\", { deepwikiResponse, issueURL }));\n\t})\n\t.addEdge(\"reason\", END)\n\t.compile();\n\nconst config = $('Get Workflow Run Id').first().json;\nconst result = await traceable(\n\tasync () => {\n\t\treturn await workflow.invoke({});\n\t},\n\t{ \n\t\tname: \"DeepWiki Analysis\",\n\t\t...config\n\t},\n)();\nreturn result.finalAnswers;"
          }
        },
        "inputs": {
          "input": [
            {
              "type": "ai_outputParser",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "main",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "ai_languageModel",
              "required": true,
              "maxConnections": 1
            }
          ]
        },
        "outputs": {
          "output": [
            {
              "type": "main"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "802dc12c-b4b2-49f3-b4fc-9ebc893f618c",
      "name": "Issue Analysis Langchain Agent",
      "type": "@n8n/n8n-nodes-langchain.code",
      "position": [
        1824,
        592
      ],
      "parameters": {
        "code": {
          "execute": {
            "code": "const { createAgent, createMiddleware, modelRetryMiddleware, providerStrategy } = require(\"langchain\");\nconst { traceable } = require(\"langsmith/traceable\");\n \nconst outputParser = await this.getInputConnectionData('ai_outputParser', 0);\nconst languageModel = await this.getInputConnectionData('ai_languageModel', 0);\n\nconst repositoryInfo = $('Get Issue From Github').item.json.data.repository;\nconst issues = repositoryInfo.issues.nodes;\nconst release =\trepositoryInfo.releases.nodes[0];\n\nconst IssueAnalysisSchema = $('Issue Analysis Schema').item.json;\nconst wrappedSchema = {\n\ttype: \"object\",\n\tproperties: {\n\t\toutput: IssueAnalysisSchema \n\t},\n\trequired: [\"output\"]\n};\n\nconst getLanguageDisplayName = (code) => {\n\ttry {\n\t\treturn new Intl.DisplayNames([code], { type: 'language' }).of(code);\n\t} catch {\n\t\treturn '';\n\t}\n};\n\nconst userPrompt = `\n\t\t[ROLE]\n\t\tYou are a 10-year experienced developer with extensive open-source contribution experience.\n\n\t\t[OUTPUT RULES]\n\t\tYou must format your output as a JSON value that adheres to a given \"JSON Schema\" instance.\n\t\t\"JSON Schema\" is a declarative language that allows you to annotate and validate JSON documents.\n\t\tDo not include markdown code blocks in the output.\n\t\tKeep keys in English.\n\t\tTranslate all user-facing strings into ko-KR.\n\n\t[TRANSLATE EXAMPLE]\n\tko-KR -> ${getLanguageDisplayName(\"ko-KR\")}\n\n\t\t[JSON SCHEMA]\n\t\t${JSON.stringify(wrappedSchema, null, 2)}\n\n\t\t[TASK]\n\t1) First, summarize the key changes from the latest release (1-3 lines, in ko-KR).\n\t2) For each issue, analyze and summarize the issueDescription in 1-2 concise lines (in ko-KR), capturing the core problem or feature request - do NOT copy the original description verbatim.\n\t\t3) Classify each issue's contribution opportunity level based on the criteria below.\n\t\t4) For each issue, provide level and reasons (2-4 reasons).\n\t\t5) Finally, select the top 3-5 most suitable issues and output as JSON.\n\n\t\t[CRITERIA FOR GOOD CONTRIBUTION OPPORTUNITIES]\n\t\t1. Issues with detailed and well-written content\n\t\t2. Issues where bug/error logs and reproduction steps are clearly specified\n\t\t3. Issues where the location of suspicious source code has been identified\n\t\t4. Issues where the maintainer has confirmed the problem or requested contributions\n\t\t5. Issues directly created by the maintainer\n\t\t6. Issues with \"good first issue\" label (no \"blocked\" or \"wait-for-triage\" labels)\n\t\t7. Issues without an existing PR\n\n\t\t[OUTPUT FORMAT]\n\t\t- level: \"high\" | \"medium\" | \"low\"\n\t\t- reasons: array of 2-4 reasons explaining the level\n\t\t- Output JSON only, no extra text\n\n\t\t[INPUT]\n\t\t[ISSUES]\n\t\t${JSON.stringify(issues, null, 2)}\n\n\t\t[RELEASE]\n\t\t${JSON.stringify(release)}\n\t`.trim();\n\nclass TimeoutError extends Error {\n\tconstructor(message = 'Operation timed out') {\n\t\tsuper(message);\n\t\tthis.name = 'TimeoutError';\n\t}\n}\n\nconst timeoutMiddleware = (timeout = 90 * 1000) => {\n\tconst middlewareName = \"timeoutMiddleware\";\n\treturn createMiddleware({\n\t\tname: middlewareName,\n\t\twrapModelCall: async (request, handler) => {\n\t\t\tlet timeoutObj;\n\t\t\ttry {\n\t\t\t\treturn await Promise.race([\n\t\t\t\t\thandler(request),\n\t\t\t\t\tnew Promise((_, reject) => {\n\t\t\t\t\t\ttimeoutObj = setTimeout(() => reject(new TimeoutError()), timeout);\n\t\t\t\t\t})\n\t\t\t\t]);\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timeoutObj);\n\t\t\t}\n\t\t},\n\t});\n};\n\nconst validateResponseMiddleware = createMiddleware({\n\tname: \"validateResponseMiddleware\",\n\tafterModel: {\n\t\tcanJumpTo: [\"model\"],\n\t\thook: (state) => {\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\t\t\tif (!lastMessage.content?.trim()) {\n\t\t\t\treturn { jumpTo: \"model\" }\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n});\nconst agent = createAgent({\n\tmodel: languageModel,\n\tresponseFormat: providerStrategy(wrappedSchema),\n\tmiddleware: [\n\t\tvalidateResponseMiddleware,\n\t\tmodelRetryMiddleware({\n\t\t\tmaxRetries: 2,\n\t\t\tbackoffFactor: 2.0,\n\t\t\tinitialDelayMs: 20000,\n\t\t\tjitter: true,\n\t\t\tonFailure: \"error\",\n\t\t}),\n\t\ttimeoutMiddleware(120 * 1000),\n\t]\n});\nconst config = $('Get Workflow Run Id').first().json;\nconst result = await traceable(\n\t\tasync () => {\n\t\t\t\treturn await agent.invoke({ \n\t\t\t\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t\t\t\t});\n\t\t},\n\t\t{ \n\t\t\tname: \"Issue Analysis\",\n\t\t\t...config\n\t\t},\n)();\nconst aiMessage = result.messages.findLast(m => m.type === \"ai\")?.content; \n\nconst parsedMessage = await outputParser.parse(aiMessage)\nparsedMessage.output.latestRelease = {\n\t...parsedMessage.output.latestRelease,\n\turl: release.url,\n\tname: release.name,\n};\nreturn [parsedMessage.output];"
          }
        },
        "inputs": {
          "input": [
            {
              "type": "ai_outputParser",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "main",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "ai_languageModel",
              "required": true,
              "maxConnections": 1
            }
          ]
        },
        "outputs": {
          "output": [
            {
              "type": "main"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "93c69f30-844c-4a0e-8e1b-2a1c2852faad",
      "name": "Title Generator Langchain Agent",
      "type": "@n8n/n8n-nodes-langchain.code",
      "position": [
        3744,
        608
      ],
      "parameters": {
        "code": {
          "execute": {
            "code": "const { createAgent, createMiddleware, modelRetryMiddleware, providerStrategy } = require(\"langchain\");\nconst { traceable } = require(\"langsmith/traceable\");\n\nconst languageModel = await this.getInputConnectionData('ai_languageModel', 0);\n\nconst outputParser = await this.getInputConnectionData('ai_outputParser', 0);\n\nconst issue = $('Merge').first().json;\n\nconst NewsletterTitleSchema = $('Title Generator Schema').item.json;\nconst wrappedSchema = {\n\ttype: \"object\",\n\tproperties: {\n\t\toutput: NewsletterTitleSchema \n\t},\n\trequired: [\"output\"]\n};\n\nconst getLanguageDisplayName = (code) => {\n\ttry {\n\t\treturn new Intl.DisplayNames([code], { type: 'language' }).of(code);\n\t} catch {\n\t\treturn '';\n\t}\n};\n\nconst userPrompt = `\n\t\t\t[ROLE]\n\t\t\tYou are an AI assistant who creates newsletter titles based on GitHub issues.\n\n\t\t\t[OUTPUT RULES]\n\t\t\tYou must format your output as a JSON value that adheres to a given \"JSON Schema\" instance.\n\t\t\t\"JSON Schema\" is a declarative language that allows you to annotate and validate JSON documents.\n\t\t\tDo not include markdown code blocks in the output.\n\t\t\tTranslate all user-facing string values into ko-KR. Keep the keys in English.\n\n\t\t\t[TRANSLATE EXAMPLE]\n\t\t\tko-KR -> ${getLanguageDisplayName(\"ko-KR\")}\n\n\t\t\t[JSON SCHEMA]\n\t\t\t${JSON.stringify(wrappedSchema, null, 2)}\n\n\t\t\t[TASK]\n\t\t\t1. Read the provided GitHub issue.\n\t\t\t2. Generate a short, catchy newsletter title.\n\t\t\t3. The title should meet these criteria:\n\t\t\t\t\t- Include a relevant emoji at the beginning.\n\t\t\t\t\t- Be easy for a non-technical audience to understand.\n\t\t\t\t\t- Be fun and intriguing to maximize open rates.\n\t\t\t4. Translate the final title into ko-KR.\n\n\t\t\t[INPUT]\n\t\t\tPlease create a newsletter title for the issue below.\n\n\t\t\t### Issue\n\t\t\tTitle: ${issue.issueTitle}\n\t\t\tBody: ${issue.issueDescription}\n\t\t\tSummary: ${issue.summary}\n\t\t\tAnalogy: ${issue.analogy}\n\n\t\t\tOutput:\n\t`;\nclass TimeoutError extends Error {\n\tconstructor(message = 'Operation timed out') {\n\t\tsuper(message);\n\t\tthis.name = 'TimeoutError';\n\t}\n}\n\nconst timeoutMiddleware = (timeout = 90 * 1000) => {\n\tconst middlewareName = \"timeoutMiddleware\";\n\treturn createMiddleware({\n\t\tname: middlewareName,\n\t\twrapModelCall: async (request, handler) => {\n\t\t\tlet timeoutObj;\n\t\t\ttry {\n\t\t\t\treturn await Promise.race([\n\t\t\t\t\thandler(request),\n\t\t\t\t\tnew Promise((_, reject) => {\n\t\t\t\t\t\ttimeoutObj = setTimeout(() => reject(new TimeoutError()), timeout);\n\t\t\t\t\t})\n\t\t\t\t]);\n\t\t\t} finally {\n\t\t\t\tclearTimeout(timeoutObj);\n\t\t\t}\n\t\t},\n\t});\n};\n\nconst validateResponseMiddleware = createMiddleware({\n\tname: \"validateResponseTitleMiddleware\",\n\tafterModel: {\n\t\tcanJumpTo: [\"model\"],\n\t\thook: (state) => {\n\t\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\t\t\tif (!lastMessage.content?.trim()) {\n\t\t\t\treturn { jumpTo: \"model\" }\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t}\n});\nconst agent = createAgent({\n\tmodel: languageModel,\n\tresponseFormat: providerStrategy(wrappedSchema),\n\tmiddleware: [\n\t\tvalidateResponseMiddleware,\n\t\tmodelRetryMiddleware({\n\t\t\tmaxRetries: 2,\n\t\t\tbackoffFactor: 2.0,\n\t\t\tinitialDelayMs: 20000,\n\t\t\tjitter: true,\n\t\t\tonFailure: \"error\",\n\t\t}),\n\t\ttimeoutMiddleware(120 * 1000),\n\t]\n});\nconst config = $('Get Workflow Run Id').first().json;\nconst result = await traceable(\n\tasync () => {\n\t\treturn await agent.invoke({ \n\t\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t\t});\n\t},\n\t{ \n\t\tname: \"Title Generation\",\n\t\t...config\n\t},\n)();\nconst aiMessage = result.messages.findLast(m => m.type === \"ai\")?.content; \n\nconst parsedMessage = await outputParser.parse(aiMessage)\nreturn [parsedMessage.output];"
          }
        },
        "inputs": {
          "input": [
            {
              "type": "ai_outputParser",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "main",
              "required": true,
              "maxConnections": 1
            },
            {
              "type": "ai_languageModel",
              "required": true,
              "maxConnections": 1
            }
          ]
        },
        "outputs": {
          "output": [
            {
              "type": "main"
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f570e2fd-90b2-425e-839b-dd430dc8e41e",
      "name": "Sticky Note10",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1584,
        400
      ],
      "parameters": {
        "color": 2,
        "width": 696,
        "height": 636,
        "content": "# 1. Analyze GitHub Issues for Contributors\n### Evaluates open-source issues and releases to identify the best contribution opportunities, categorizing them by suitability level and providing translated summaries for potential contributors."
      },
      "typeVersion": 1
    },
    {
      "id": "3bc31e74-50fd-4b41-8927-a6deb5f688c4",
      "name": "Split Out",
      "type": "n8n-nodes-base.splitOut",
      "position": [
        2880,
        544
      ],
      "parameters": {
        "options": {},
        "fieldToSplitOut": "issues"
      },
      "typeVersion": 1
    },
    {
      "id": "3727f129-bb7e-4100-ac18-b2d563107c54",
      "name": "Issue Analysis Schema",
      "type": "n8n-nodes-base.code",
      "position": [
        1632,
        592
      ],
      "parameters": {
        "jsCode": "const { owner, name } = $('Load Repo Info').first().json;\n\nconst issueAnalysisOutputSchema = {\n\ttitle: \"issue_analysis_output\",\n\tdescription: \"Analysis result of GitHub issues and latest release\",\n\ttype: \"object\",\n\tproperties: {\n\t\ttranslationLanguageCode: {\n\t\t\ttype: \"string\",\n\t\t\tdescription: \"Language code\"\n\t\t},\n\t\tlatestRelease: {\n\t\t\ttype: \"object\",\n\t\t\tproperties: {\n\t\t\t\tdetails: {\n\t\t\t\t\ttype: \"array\",\n\t\t\t\t\titems: {\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tcategory: {\n\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\tdescription: \"Category for the descriptions (e.g., 'breaking change', 'internal change')\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tdescriptions: {\n\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t\t\t\tmaxItems: 3,\n\t\t\t\t\t\t\t\tdescription: \"Compressed release descriptions (maximum 3 items)\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\trequired: [\"category\", \"descriptions\"]\n\t\t\t\t},\n\t\t\t\tmaxItems: 3,\n\t\t\t\tdescription: \"Array of release details (maximum 3 items)\"\n\t\t\t\t}\n\t\t\t},\n\t\t\trequired: [\"details\"]\n\t\t},\n\t\tissues: {\n\t\t\ttype: \"array\",\n\t\t\titems: {\n\t\t\t\ttype: \"object\",\n\t\t\t\tproperties: {\n\t\t\t\t\tissueTitle: { type: \"string\", description: \"Issue title\" },\n\t\t\t\t\tissueURL: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tpattern: `^https://github.com/${owner}/${name}/issues/\\\\d+$`,\n\t\t\t\t\t\tdescription: \"Issue URL\"\n\t\t\t\t\t},\n\t\t\t\t\tissueDescription: { \n\t\t\t\t\t\ttype: \"array\", \n\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t\tdescription: \"A 1-2 line concise summary of the original issue description capturing the core problem or feature request (NOT a verbatim copy)\"\n\t\t\t\t\t},\n\t\t\t\t\tissueSuitability: {\n\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\tlevel: {\n\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\tenum: [\"high\", \"medium\", \"low\"],\n\t\t\t\t\t\t\t\tdescription: \"Contribution opportunity level\"\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\treasons: {\n\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\titems: { type: \"string\" },\n\t\t\t\t\t\t\t\tdescription: \"Reasons for the level\"\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\trequired: [\"level\", \"reasons\"]\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\trequired: [\"issueTitle\", \"issueURL\", \"issueDescription\", \"issueSuitability\"]\n\t\t\t},\n\t\t\tdescription: \"Top 3-5 suitable issues\"\n\t\t}\n\t},\n\trequired: [\"translationLanguageCode\", \"latestRelease\", \"issues\"]\n};\nreturn issueAnalysisOutputSchema"
      },
      "typeVersion": 2
    },
    {
      "id": "ea8e1a37-e31a-410c-8fda-2cc887c62f04",
      "name": "Deepwiki Response Schema",
      "type": "n8n-nodes-base.code",
      "position": [
        2624,
        688
      ],
      "parameters": {
        "jsCode": "const TechnicalDifficultySchema = {\n\ttype: \"object\",\n\ttitle: \"technical_difficulty_assessment\",\n\tdescription: \"Schema for evaluating the technical complexity and difficulty level of resolving a GitHub issue\",\n\tproperties: {\n\t\tlevel: {\n\t\t\ttype: \"string\",\n\t\t\tenum: [\"High\", \"Medium\", \"Low\"],\n\t\t\tdescription: \"High | Medium | Low\"\n\t\t},\n\t\treasons: {\n\t\t\ttype: \"array\",\n\t\t\titems: { type: \"string\" },\n\t\t\tdescription: \"Reasons for the difficulty level\"\n\t\t}\n\t},\n\trequired: [\"level\", \"reasons\"]\n};\n\nconst IssueResponseSchema = {\n\ttype: \"object\",\n\ttitle: \"deepwiki_issue_analysis_response\",\n\tdescription: \"Structured response schema containing comprehensive analysis of GitHub issues including root cause, resolution approach, difficulty assessment, and explanatory content\",\n\tproperties: {\n\t\ttranslationLanguageCode: {\n\t\t\ttype: \"string\",\n\t\t\tdescription: \"Language code\"\n\t\t},\n\t\tdeepwikiLink: {\n\t\t\ttype: \"string\",\n\t\t\tpattern: \"^https://deepwiki\\\\.com/search/.*\",\n\t\t\terrorMessage: \"Must start with https://deepwiki.com/search/\"\n\t\t},\n\t\trootCause: {\n\t\t\ttype: \"array\",\n\t\titems: { type: \"string\" },\n\t\t\tdescription: \"Root cause of the issue\"\n\t\t},\n\t\tresolutionApproach: {\n\t\t\ttype: \"array\",\n\t\t\titems: { type: \"string\" },\n\t\t\tdescription: \"List of resolution approaches\"\n\t\t},\n\t\ttechnicalDifficulty: {\n\t\t\t...TechnicalDifficultySchema,\n\t\t\tdescription: \"Technical difficulty assessment\"\n\t\t},\n\t\tsummary: {\n\t\t\ttype: \"string\",\n\t\t\tdescription: \"One-sentence contribution summary\"\n\t\t},\n\t\tkeyword: {\n\t\t\ttype: \"array\",\n\t\t\titems: { type: \"string\" },\n\t\t\tdescription: \"1-5 relevant keywords\"\n\t\t},\n\t\tanalogy: {\n\t\t\ttype: \"array\",\n\t\t\titems: { type: \"string\" },\n\t\t\tdescription: \"Simple analogy for issue and resolution\"\n\t\t}\n\t},\n\trequired: [\"translationLanguageCode\", \"deepwikiLink\", \"rootCause\", \"resolutionApproach\", \"technicalDifficulty\", \"summary\", \"keyword\", \"analogy\"]\n};\nreturn IssueResponseSchema;"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "35b3b1ce-1f0d-4095-8cce-986c5638ad97",
      "name": "Issue Analysis Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        1696,
        816
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "={{ $json.toJsonString() }}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3,
      "alwaysOutputData": false,
      "waitBetweenTries": 5000
    },
    {
      "id": "008e4a4e-08f4-4884-b2b2-4bac7d672f0b",
      "name": "Deepwiki Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "maxTries": 3,
      "position": [
        2576,
        896
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "={{ $json.toJsonString() }}"
      },
      "executeOnce": false,
      "retryOnFail": true,
      "typeVersion": 1.3,
      "waitBetweenTries": 5000
    },
    {
      "id": "dd67528b-5bb7-4c62-9ee0-f1278a678163",
      "name": "Title Generator Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        3520,
        880
      ],
      "parameters": {
        "autoFix": true,
        "schemaType": "manual",
        "inputSchema": "={{ $json.toJsonString() }}"
      },
      "retryOnFail": true,
      "typeVersion": 1.3,
      "waitBetweenTries": 5000
    },
    {
      "id": "7de65c31-96a4-4248-9159-61f2e398c227",
      "name": "Title Generator Schema",
      "type": "n8n-nodes-base.code",
      "position": [
        3552,
        608
      ],
      "parameters": {
        "jsCode": "return {\n\t\ttitle: \"newsletter_title_output\",\n\t\tdescription: \"Generated newsletter title based on GitHub issue\",\n\t\ttype: \"object\",\n\t\tproperties: {\n\t\t\t\ttranslationLanguageCode: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tdescription: \"Language code\"\n\t\t\t\t},\n\t\t\t\ttitle: {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\tdescription: \"Catchy newsletter title with emoji, translated to target language\"\n\t\t\t\t}\n\t\t},\n\t\trequired: [\"translationLanguageCode\", \"title\"]\n};"
      },
      "typeVersion": 2,
      "alwaysOutputData": false
    },
    {
      "id": "206678b6-ed2b-495f-87ba-ee7edd9b82e8",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1632,
        1024
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "2d5b8487-7f1f-4916-a887-77aeb688b095",
      "name": "OpenRouter Chat Model1",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        1984,
        816
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "190ff326-6592-448a-aa9c-0484b5784ed5",
      "name": "OpenRouter Chat Model2",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        2576,
        1104
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "073cccf8-40e8-4b3f-8824-2fd79022f012",
      "name": "OpenRouter Chat Model3",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        2912,
        912
      ],
      "parameters": {
        "model": "openrouter/aurora-alpha",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "f83d0bf2-b556-4311-9bd5-7e72361a8046",
      "name": "OpenRouter Chat Model4",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3520,
        1088
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "b6e3d9be-9130-4c3e-b5a7-9291420a48df",
      "name": "OpenRouter Chat Model5",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        3840,
        880
      ],
      "parameters": {
        "model": "openrouter/free",
        "options": {
          "timeout": 90000,
          "maxRetries": 0
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "46ffce0f-923e-4ab4-a927-54a00073c269",
      "name": "Get Workflow Run Id",
      "type": "n8n-nodes-base.code",
      "position": [
        1424,
        592
      ],
      "parameters": {
        "jsCode": "const workflowRunId = crypto.randomUUID();\nconst repoInfo = `${$('Load Repo Info').first().json.owner}/${$('Load Repo Info').first().json.name}`;\nconst config = {\n\tmetadata: {\n\t\trun_id: workflowRunId,\n\t\trepo: repoInfo,\n\t\temail: \"\",\n\t}\n};\nreturn config"
      },
      "typeVersion": 2
    }
  ],
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "446a7cc6-212f-449e-8e4a-73788dca73cb",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Title Generator Schema",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Split Out": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "convert MJML": {
      "main": [
        [
          {
            "node": "convert MJML to HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Load Repo Info": {
      "main": [
        [
          {
            "node": "Get Issue From Github",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "get Top Fit Issues": {
      "main": [
        [
          {
            "node": "Deepwiki Response Schema",
            "type": "main",
            "index": 0
          },
          {
            "node": "Split Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Workflow Run Id": {
      "main": [
        [
          {
            "node": "Issue Analysis Schema",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "convert MJML to HTML": {
      "main": [
        [
          {
            "node": "Send email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Issue From Github": {
      "main": [
        [
          {
            "node": "Get Workflow Run Id",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Issue Analysis Schema": {
      "main": [
        [
          {
            "node": "Issue Analysis Langchain Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Issue Analysis Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model1": {
      "ai_languageModel": [
        [
          {
            "node": "Issue Analysis Langchain Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Deepwiki Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model3": {
      "ai_languageModel": [
        [
          {
            "node": "Deepwiki Langchain agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model4": {
      "ai_languageModel": [
        [
          {
            "node": "Title Generator Structured Output Parser",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model5": {
      "ai_languageModel": [
        [
          {
            "node": "Title Generator Langchain Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Title Generator Schema": {
      "main": [
        [
          {
            "node": "Title Generator Langchain Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deepwiki Langchain agent": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Deepwiki Response Schema": {
      "main": [
        [
          {
            "node": "Deepwiki Langchain agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Issue Analysis Langchain Agent": {
      "main": [
        [
          {
            "node": "get Top Fit Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Title Generator Langchain Agent": {
      "main": [
        [
          {
            "node": "convert MJML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Deepwiki Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Deepwiki Langchain agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "When clicking \u2018Execute workflow\u2019": {
      "main": [
        [
          {
            "node": "Load Repo Info",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Issue Analysis Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Issue Analysis Langchain Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    },
    "Title Generator Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "Title Generator Langchain Agent",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}