AutomationFlowsAI & RAG › Convert Github Code Commits to Linkedin Posts with Gemini AI and Code Image…

Convert Github Code Commits to Linkedin Posts with Gemini AI and Code Image…

Original n8n title: Convert Github Code Commits to Linkedin Posts with Gemini AI and Code Image Generation

ByMarco Florez @florezai on n8n.io

Turn your code commits into engaging social media content automatically. This workflow monitors a GitHub repository, uses AI to write a LinkedIn post about your changes, generates a beautiful "Mac-window" style image of your code, and publishes it all to LinkedIn. GitHub…

Event trigger★★★★☆ complexityAI-powered20 nodesGithub TriggerGitHubLinkedInAgentHTTP RequestOutput Parser StructuredOpenRouter Chat
AI & RAG Trigger: Event Nodes: 20 Complexity: ★★★★☆ AI nodes: yes Added:

This workflow corresponds to n8n.io template #12131 — we link there as the canonical source.

This workflow follows the Agent → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "id": "rc4n2nMUBWhg7IIv",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Github Code to LinkedIn Publisher",
  "tags": [],
  "nodes": [
    {
      "id": "0667bada-510c-4bac-a863-8ef1b29f06ee",
      "name": "Main Info",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -2592,
        128
      ],
      "parameters": {
        "width": 686,
        "height": 423,
        "content": "## \ud83d\ude80 Github Code to LinkedIn Publisher\n\n### How it works\nAutomatically turns your code commits into engaging LinkedIn posts! \n1. **Detects** new/modified code in Github.\n2. **Analyzes** the code with AI to write a catchy post & extract key snippets.\n3. **Generates** a beautiful \"Mac-window\" style image of the code.\n4. **Uploads** the image back to Github & **Posts** everything to LinkedIn.\n\n### Setup steps\n- [ ] **Credentials**: Configure Github, LinkedIn, OpenRouter, and HCTI API.\n- [ ] **Github Nodes**: Update `owner` and `repository` fields in the Trigger and Download nodes.\n- [ ] **LinkedIn Node**: Update the `person` URN.\n- [ ] **HCTI**: Ensure you have an account for image generation."
      },
      "typeVersion": 1
    },
    {
      "id": "a3290627-b926-4b7c-b853-1c9384738f54",
      "name": "Section 1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1840,
        176
      ],
      "parameters": {
        "color": 7,
        "width": 896,
        "height": 372,
        "content": "### 1. Monitor & Fetch\nWatches for `push` events, extracts modified files, and downloads the raw code content."
      },
      "typeVersion": 1
    },
    {
      "id": "d1d2dcd2-b6df-4680-bf7d-7327ee944ceb",
      "name": "Section 2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1744,
        672
      ],
      "parameters": {
        "color": 7,
        "width": 532,
        "height": 468,
        "content": "### 2. AI Content Creation\nUses an LLM to analyze the code, write a LinkedIn post, and select the best snippet."
      },
      "typeVersion": 1
    },
    {
      "id": "eff6680c-f3cc-4559-b15d-c8b9a1415b3a",
      "name": "Section 3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -1200,
        672
      ],
      "parameters": {
        "color": 7,
        "width": 652,
        "height": 340,
        "content": "### 3. Image Generation\nCreates HTML/CSS for a \"pretty\" code window and converts it to an image via API."
      },
      "typeVersion": 1
    },
    {
      "id": "9bf75f05-3346-40b6-9e50-13c450d64905",
      "name": "Section 4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -928,
        96
      ],
      "parameters": {
        "color": 7,
        "width": 844,
        "height": 492,
        "content": "### 4. Store & Publish\nUploads the generated image to your repo for hosting, then combines text + image for the final LinkedIn post."
      },
      "typeVersion": 1
    },
    {
      "id": "53ff6b5d-cda9-4bf5-b28a-a8983e8b8d5e",
      "name": "Github Trigger1",
      "type": "n8n-nodes-base.githubTrigger",
      "position": [
        -1808,
        352
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "your-github-username"
        },
        "events": [
          "push"
        ],
        "options": {},
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "your-source-repo-name"
        }
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "ae31ca41-9c74-48ef-bb43-9ff5627c1c0f",
      "name": "GitHub File Download",
      "type": "n8n-nodes-base.github",
      "position": [
        -1392,
        352
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "your-github-username"
        },
        "filePath": "={{ $json.filePath }}",
        "resource": "file",
        "operation": "get",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "your-source-repo-name"
        },
        "additionalParameters": {}
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "c19ca9b7-3d0a-4f8a-be54-bff3b188c12a",
      "name": "Post to LinkedIn",
      "type": "n8n-nodes-base.linkedIn",
      "position": [
        -304,
        416
      ],
      "parameters": {
        "text": "={{ $('LinkedIn Content Creator').item.json.output.post_title }}\n\n{{ $('LinkedIn Content Creator').item.json.output.post_content }}\n{{ $('LinkedIn Content Creator').item.json.output.hashtags }}\n\nLink to Github: {{ $json.content._links.html }}\n",
        "person": "your-linkedin-urn",
        "additionalFields": {
          "visibility": "PUBLIC"
        },
        "shareMediaCategory": "IMAGE"
      },
      "credentials": {
        "linkedInOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "3eb75e0a-0c99-4cf1-b6a5-105a5fb705d4",
      "name": "LinkedIn Content Creator",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        -1616,
        784
      ],
      "parameters": {
        "text": "=Data\n{{ $json.data }}",
        "options": {
          "systemMessage": "You are a LinkedIn content strategist and copywriter specialising in professional, engaging technical content focused on infrastructure as code with bicep for azure workloads.\n\nAnalyze the provided code file and create:\n1. A LinkedIn post with compelling narrative (without including the actual code)\n2. Extract the most important/interesting code snippet (10-20 lines max) that showcases the key concept\n\nFor the LinkedIn post:\n- Open with a compelling hook that grabs attention\n- Explain what the code does and why it's valuable\n- Use professional yet conversational tone\n- Include a clear call-to-action\n- Stay within 400 characters\n- DO NOT include the actual code in the post content\n\nFor the code snippet:\n- Select the most interesting or important part\n- Keep it concise (10-20 lines)\n- Ensure it's self-contained and understandable\n- Include any necessary context as comments\n\nIMPORTANT: Return hashtags as a single string with space-separated hashtags, NOT as an array.\n\nFormat your response as JSON with the following structure:\n{\n  \"post_title\": \"string\",\n  \"post_content\": \"string (narrative without code and without hashtags)\",\n  \"code_snippet\": \"string (the actual code to display)\",\n  \"code_language\": \"string (javascript, typescript, python, etc.)\",\n  \"hashtags\": \"string\",\n  \"character_count\": integer\n}"
        },
        "promptType": "define",
        "hasOutputParser": true
      },
      "typeVersion": 2
    },
    {
      "id": "377af262-7c5f-45ad-81a3-ea8af25f3862",
      "name": "Create Code HTML",
      "type": "n8n-nodes-base.code",
      "position": [
        -1152,
        752
      ],
      "parameters": {
        "jsCode": "// Get the code snippet and language from previous node\nconst codeSnippet = $json.code_snippet || '';\nconst language = $json.code_language || 'javascript';\n\n// Calculate dimensions based on content\nconst lines = codeSnippet.split('\\n');\nconst lineCount = lines.length;\nconst maxLineLength = Math.max(...lines.map(line => line.length));\n\n// Adjust sizing calculations\nconst charWidth = 8.4; // approximate width per character in monospace\nconst lineHeight = 24; // pixels per line\nconst containerPadding = 20; // padding inside code container\nconst bodyPadding = 10; // minimal body padding\nconst headerHeight = 40; // header with dots\n\n// Calculate dimensions and FORCE INTEGERS (Math.ceil) to prevent API errors\nconst calculatedWidth = Math.max(400, maxLineLength * charWidth + (containerPadding * 2));\nconst contentWidth = Math.ceil(Math.min(calculatedWidth, 1200)); \nconst contentHeight = Math.ceil((lineCount * lineHeight) + headerHeight + (containerPadding * 2));\n\n// Create HTML with minimal padding, sized to content\nconst html = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n  <link href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css\" rel=\"stylesheet\" />\n  <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n  <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n  <link href=\"https://fonts.googleapis.com/css2?family=Fira+Code:wght@400;500&display=swap\" rel=\"stylesheet\">\n  <style>\n    * {\n      margin: 0;\n      padding: 0;\n      box-sizing: border-box;\n    }\n    html, body {\n      width: 100%;\n      height: 100%;\n      margin: 0;\n      padding: 0;\n    }\n    body {\n      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n      font-family: 'Fira Code', 'Consolas', monospace;\n      padding: ${bodyPadding}px;\n      display: flex;\n      align-items: center;\n      justify-content: center;\n    }\n    .code-container {\n      background: #2d2d2d;\n      border-radius: 10px;\n      padding: ${containerPadding}px;\n      box-shadow: 0 10px 30px rgba(0,0,0,0.4);\n      width: auto;\n      max-width: 100%;\n    }\n    .header {\n      display: flex;\n      gap: 8px;\n      margin-bottom: 15px;\n    }\n    .dot {\n      width: 12px;\n      height: 12px;\n      border-radius: 50%;\n    }\n    .dot-red { background: #ff5f56; }\n    .dot-yellow { background: #ffbd2e; }\n    .dot-green { background: #27c93f; }\n    pre {\n      margin: 0;\n      padding: 0;\n      overflow-x: visible;\n      white-space: pre;\n      max-width: 100%;\n    }\n    code {\n      font-size: 12px;\n      line-height: 1.5;\n      display: block;\n      font-family: 'Fira Code', 'Consolas', monospace;\n    }\n    /* Override Prism's default margins */\n    pre[class*=\"language-\"] {\n      margin: 0;\n      padding: 0;\n      background: transparent;\n    }\n  </style>\n</head>\n<body>\n  <div class=\"code-container\">\n    <div class=\"header\">\n      <div class=\"dot dot-red\"></div>\n      <div class=\"dot dot-yellow\"></div>\n      <div class=\"dot dot-green\"></div>\n    </div>\n    <pre><code class=\"language-${language}\">${codeSnippet.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre>\n  </div>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js\"></script>\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-${language}.min.js\"></script>\n</body>\n</html>\n`;\n\nreturn [{\n  json: {\n    ...items[0].json,\n    html: html,\n    estimatedWidth: contentWidth + (bodyPadding * 2),\n    estimatedHeight: contentHeight + (bodyPadding * 2)\n  }\n}];"
      },
      "typeVersion": 1
    },
    {
      "id": "d1d0195c-ec4e-4990-bce9-0b103ceef218",
      "name": "Generate Code Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -960,
        752
      ],
      "parameters": {
        "url": "https://hcti.io/v1/image",
        "method": "POST",
        "options": {
          "timeout": 30000,
          "response": {
            "response": {
              "responseFormat": "json"
            }
          }
        },
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "html",
              "value": "={{ $json.html }}"
            },
            {
              "name": "google_fonts",
              "value": "Fira Code"
            },
            {
              "name": "viewport_width",
              "value": "={{ Math.min($json.estimatedWidth || 800, 1920) }}"
            },
            {
              "name": "viewport_height",
              "value": "={{ Math.min($json.estimatedHeight || 400, 1080) }}"
            },
            {
              "name": "device_scale",
              "value": "={{ 2 }}"
            }
          ]
        },
        "genericAuthType": "httpBasicAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBasicAuth": {
          "name": "<your credential>"
        },
        "httpHeaderAuth": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "b900240c-9695-4c2a-8766-a0703c530721",
      "name": "GET the genarted Image",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        -752,
        752
      ],
      "parameters": {
        "url": "={{ $json.url }}",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "typeVersion": 4.2,
      "alwaysOutputData": true
    },
    {
      "id": "88ce4578-58f3-4cbc-8ae4-fed95b73a75d",
      "name": "Upload to Image Repo",
      "type": "n8n-nodes-base.github",
      "position": [
        -864,
        192
      ],
      "parameters": {
        "owner": {
          "__rl": true,
          "mode": "name",
          "value": "your-github-username"
        },
        "filePath": "={{ $('GitHub File Download').item.json.filePath }}",
        "resource": "file",
        "repository": {
          "__rl": true,
          "mode": "name",
          "value": "your-image-storage-repo"
        },
        "fileContent": "={{ $json.data }}",
        "commitMessage": "=Upload generated image for {{ $('GitHub File Download').item.json.filePath }}"
      },
      "credentials": {
        "githubApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1.1
    },
    {
      "id": "b99f1752-f6cd-4149-88c7-ba02975aeaf4",
      "name": "Merge",
      "type": "n8n-nodes-base.merge",
      "position": [
        -496,
        416
      ],
      "parameters": {
        "mode": "combine",
        "options": {},
        "combineBy": "combineAll"
      },
      "typeVersion": 3.2
    },
    {
      "id": "d9397243-bad3-4699-9056-a22e41009a4d",
      "name": "Extract Modified Files",
      "type": "n8n-nodes-base.code",
      "notes": "Needed to extract the path of different types of changes in Github, be it modified, added, etc.",
      "position": [
        -1584,
        352
      ],
      "parameters": {
        "jsCode": "// Get the head_commit object from the webhook body\nconst headCommit = $json.body.head_commit;\n\n// Get the arrays of added and modified files. Default to empty arrays if they don't exist.\nconst addedFiles = headCommit.added || [];\nconst modifiedFiles = headCommit.modified || [];\n\n// Combine both arrays into a single list of file paths\nconst allChangedFiles = [...addedFiles, ...modifiedFiles];\n\n// If there are no changed files, return an empty result to stop the workflow gracefully.\nif (allChangedFiles.length === 0) {\n  return [];\n}\n\n// Create a new list of n8n items. Each item will contain one file path.\nconst items = allChangedFiles.map(filePath => {\n  return {\n    json: {\n      // We store the path in a new field called 'filePath'\n      filePath: filePath\n    }\n  };\n});\n\n// Return the new list of items\nreturn items;"
      },
      "typeVersion": 2
    },
    {
      "id": "b00dc7bd-c999-4617-b752-1c78a93fabd7",
      "name": "Extract from File",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -1120,
        352
      ],
      "parameters": {
        "options": {},
        "operation": "text"
      },
      "typeVersion": 1
    },
    {
      "id": "426f6797-088e-42f2-9f5d-ebcf2b853974",
      "name": "Structured Output Parser",
      "type": "@n8n/n8n-nodes-langchain.outputParserStructured",
      "position": [
        -1472,
        1008
      ],
      "parameters": {
        "schemaType": "manual",
        "inputSchema": "{\n\t\"type\": \"object\",\n\t\"properties\": {\n\t\t\"post_title\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n      \"post_content\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n      \"code_snippet\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n      \"code_language\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n      \"hashtags\": {\n\t\t\t\"type\": \"string\"\n\t\t},\n      \"character_count\": {\n\t\t\t\"type\": \"integer\"\n\t\t}\n\t}\n}"
      },
      "typeVersion": 1.3
    },
    {
      "id": "b1c72cac-7e02-4a24-8dc0-99ea96a00c44",
      "name": "Edit Fields",
      "type": "n8n-nodes-base.set",
      "position": [
        -1328,
        752
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "13b0cb01-c54d-43bb-90df-a48f12d76fb3",
              "name": "post_title",
              "type": "string",
              "value": "={{ $json.output.post_title }}"
            },
            {
              "id": "f3b23908-6876-46b7-be5f-d8a41c209202",
              "name": "post_content",
              "type": "string",
              "value": "={{ $json.output.post_content }}"
            },
            {
              "id": "a915c874-e76a-408e-ad92-d8863397a5e0",
              "name": "code_snippet",
              "type": "string",
              "value": "={{ $json.output.code_snippet }}"
            },
            {
              "id": "571704c3-425c-4aa2-a44a-1147ac56ad1c",
              "name": "code_language",
              "type": "string",
              "value": "={{ $json.output.code_language }}"
            },
            {
              "id": "6599c4be-cbaf-4155-b7b9-4930b1386e48",
              "name": "hashtags",
              "type": "string",
              "value": "={{ $json.output.hashtags }}"
            },
            {
              "id": "0e9bde61-489f-4c20-b68a-0193cd8a388e",
              "name": "character_count",
              "type": "number",
              "value": "={{ $json.output.character_count }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "7d068914-c1cc-4553-945d-0327ea5e66df",
      "name": "OpenRouter Chat Model",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "position": [
        -1616,
        1008
      ],
      "parameters": {
        "model": "google/gemini-2.5-flash",
        "options": {
          "responseFormat": "json_object"
        }
      },
      "credentials": {
        "openRouterApi": {
          "name": "<your credential>"
        }
      },
      "typeVersion": 1
    },
    {
      "id": "5afb4709-1651-4ba5-9cb7-e9da7437afe4",
      "name": "Extract from File1",
      "type": "n8n-nodes-base.extractFromFile",
      "position": [
        -1120,
        192
      ],
      "parameters": {
        "options": {},
        "operation": "binaryToPropery"
      },
      "typeVersion": 1.1
    }
  ],
  "active": false,
  "settings": {
    "availableInMCP": false,
    "executionOrder": "v1"
  },
  "versionId": "7d6f38a7-c1b0-496a-8c19-2c533b631ddf",
  "connections": {
    "Merge": {
      "main": [
        [
          {
            "node": "Post to LinkedIn",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Create Code HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Github Trigger1": {
      "main": [
        [
          {
            "node": "Extract Modified Files",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Code HTML": {
      "main": [
        [
          {
            "node": "Generate Code Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File": {
      "main": [
        [
          {
            "node": "LinkedIn Content Creator",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract from File1": {
      "main": [
        [
          {
            "node": "Upload to Image Repo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Code Image": {
      "main": [
        [
          {
            "node": "GET the genarted Image",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitHub File Download": {
      "main": [
        [
          {
            "node": "Extract from File",
            "type": "main",
            "index": 0
          },
          {
            "node": "Extract from File1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Upload to Image Repo": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "LinkedIn Content Creator",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Extract Modified Files": {
      "main": [
        [
          {
            "node": "GitHub File Download",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GET the genarted Image": {
      "main": [
        [
          {
            "node": "Merge",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "LinkedIn Content Creator": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Structured Output Parser": {
      "ai_outputParser": [
        [
          {
            "node": "LinkedIn Content Creator",
            "type": "ai_outputParser",
            "index": 0
          }
        ]
      ]
    }
  }
}

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Turn your code commits into engaging social media content automatically. This workflow monitors a GitHub repository, uses AI to write a LinkedIn post about your changes, generates a beautiful "Mac-window" style image of your code, and publishes it all to LinkedIn. GitHub…

Source: https://n8n.io/workflows/12131/ — original creator credit. Request a take-down →

More AI & RAG workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

AI & RAG

Before adding a new npm package as a dependency, you should know if it's actively maintained, widely used, and safe to build on. This workflow does that analysis automatically.

@Mendable/N8N Nodes Firecrawl, OpenAI Chat, Form Trigger +6
AI & RAG

Automatically generate GitHub release notes using AI.

Github Trigger, GitHub, OpenRouter Chat +2
AI & RAG

The AI-Powered Shopify SEO Content Automation is an enterprise-grade workflow that transforms product content creation for e-commerce stores. This sophisticated multi-agent system integrates GPT-4o, C

Perplexity Tool, Memory Buffer Window, Agent +15
AI & RAG

RAG CHATBOT Main. Uses telegram, telegramTrigger, lmChatOpenAi, n8n-nodes-mcp. Event-driven trigger; 87 nodes.

Telegram, Telegram Trigger, OpenAI Chat +8
AI & RAG

Deep Research new (fr). Uses outputParserStructured, formTrigger, chainLlm, form. Event-driven trigger; 82 nodes.

Output Parser Structured, Form Trigger, Chain Llm +8