From 25213fabaaa1cadfb31fac256f2c19997b6b459a Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 18 Sep 2025 11:53:02 +0200 Subject: [PATCH] Add AI label review step to autolabeler workflow --- .github/workflows/autolabeler.yml | 117 ++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/.github/workflows/autolabeler.yml b/.github/workflows/autolabeler.yml index 03472a33f..81cc34eaf 100644 --- a/.github/workflows/autolabeler.yml +++ b/.github/workflows/autolabeler.yml @@ -112,3 +112,120 @@ jobs: labels: Array.from(labelsToAdd), }); } + ai-check: + needs: autolabeler + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Load priority config + id: config + run: | + echo "priority=$(cat .github/label-priority.json)" >> $GITHUB_OUTPUT + + - name: Fetch PR metadata + id: pr + uses: actions/github-script@v7 + with: + script: | + const pr = context.payload.pull_request; + const files = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pr.number + }); + return { + title: pr.title, + body: pr.body || "", + files: files.data.map(f => f.filename) + }; + + - name: AI Label Review + id: ai + uses: actions/github-script@v7 + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + with: + script: | + const fetch = require("node-fetch"); + const prData = { + title: ${{ steps.pr.outputs.title }}, + body: ${{ steps.pr.outputs.body }}, + files: ${{ steps.pr.outputs.files }} + }; + + const prompt = ` + You are a GitHub labeling bot. + Task: + - Analyze PR title, body, and file list. + - For each possible label, return a confidence score (0–1). + - Consider conflicts: bugfix vs refactor, feature vs breaking change. + - Output JSON: {"labels": [{"name":"bugfix","score":0.9}, {"name":"refactor","score":0.6}]} + + Valid labels: [new script, update script, delete script, bugfix, feature, breaking change, maintenance, refactor, website, json, api, core, github, addon, pve-tool, vm]. + + PR data: ${JSON.stringify(prData)} + `; + + const response = await fetch("https://api.openai.com/v1/chat/completions", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer " + process.env.OPENAI_API_KEY, + }, + body: JSON.stringify({ + model: "gpt-4.1-mini", + messages: [{role: "user", content: prompt}], + temperature: 0 + }) + }); + const data = await response.json(); + const labels = JSON.parse(data.choices[0].message.content).labels; + core.setOutput("labels", JSON.stringify(labels)); + + - name: Apply AI Labels + uses: actions/github-script@v7 + with: + script: | + const raw = JSON.parse('${{ steps.ai.outputs.labels }}'); + const prNumber = context.payload.pull_request.number; + const config = JSON.parse('${{ steps.config.outputs.priority }}'); + + let toApply = []; + let toSuggest = []; + + raw.forEach(l => { + if (l.score >= 0.8) { + // check conflicts + const conflicts = config.conflicts[l.name] || []; + const hasStrongerConflict = conflicts.some(c => + raw.some(x => x.name === c && x.score >= 0.6 && config.priorities[c] >= config.priorities[l.name]) + ); + if (!hasStrongerConflict) { + toApply.push(l.name); + } + } else if (l.score >= 0.5) { + toSuggest.push(`${l.name} (${Math.round(l.score*100)}%)`); + } + }); + + if (toApply.length > 0) { + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + labels: toApply + }); + } + + if (toSuggest.length > 0) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: `🤖 AI suggests these possible labels (uncertain): ${toSuggest.join(", ")}` + }); + }