From dc44cfcba2408711cfbfbbec455fad1f06493cc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82?= Date: Thu, 30 Jul 2026 19:01:49 +0200 Subject: [PATCH] fix: swap AI moderator from GitHub Models to Groq (free tier) --- .github/workflows/ai-moderator.yml | 118 +++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ai-moderator.yml b/.github/workflows/ai-moderator.yml index 55d4e8d..97b0561 100644 --- a/.github/workflows/ai-moderator.yml +++ b/.github/workflows/ai-moderator.yml @@ -1,31 +1,123 @@ -name: AI Community Moderator +name: AI Moderator on: issues: types: [opened] - pull_request: - types: [opened] issue_comment: types: [created] pull_request_review_comment: types: [created] - discussion: - types: [created] - discussion_comment: - types: [created] permissions: - contents: read issues: write pull-requests: write - discussions: write - models: read + contents: read jobs: moderate: runs-on: ubuntu-latest steps: - - uses: benbalter/ai-community-moderator@main + - uses: actions/checkout@v4 + - uses: actions/github-script@v9 + env: + GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }} with: - github-token: ${{ secrets.GITHUB_TOKEN }} - severity-threshold: 5 + script: | + const fs = require('fs'); + + const content = context.payload.issue?.body + || context.payload.comment?.body + || ''; + if (!content.trim()) { + console.log('No content to analyze'); + return; + } + + const coc = fs.readFileSync('.github/CODE_OF_CONDUCT.md', 'utf8'); + + const prompt = `You are a community moderator for the Antergos NeXT project. + + Code of Conduct: + ${coc} + + Analyze the following content for violations of this Code of Conduct. + Respond with ONLY valid JSON — no markdown, no backticks, no explanation. + + { + "violation": true/false, + "reason": "short reason if violation else empty", + "severity": 1-10 + } + + Content: + ${content}`; + + const model = 'llama-3.1-8b-instant'; + + let decision; + try { + const response = await fetch('https://api.groq.com/openai/v1/chat/completions', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${process.env.GROQ_API_KEY}` + }, + body: JSON.stringify({ + model, + messages: [ + { role: 'system', content: 'You are a helpful community moderator. Respond only with valid JSON as instructed.' }, + { role: 'user', content: prompt } + ], + temperature: 0.1, + max_tokens: 150 + }) + }); + + const data = await response.json(); + const raw = data.choices?.[0]?.message?.content || '{}'; + decision = JSON.parse(raw.replace(/```json|```/g, '').trim()); + console.log(`Decision: ${JSON.stringify(decision)}`); + } catch (err) { + console.log(`API error: ${err}`); + return; + } + + if (!decision.violation) { + console.log('Content deemed acceptable'); + return; + } + + const issueNumber = context.payload.issue?.number + || context.payload.pull_request?.number; + if (!issueNumber) { + console.log('No issue/PR number found'); + return; + } + + const warning = `**AI Moderator Notice** + +Your comment appears to violate the project's [Code of Conduct](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/blob/master/.github/CODE_OF_CONDUCT.md). + +Reason: ${decision.reason} + +Please keep discussions respectful and constructive. Repeated violations may result in moderation actions.`; + + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: warning + }); + + await github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + labels: ['ai-moderated'] + }); + + console.log('Warning posted and label added'); + } catch (err) { + console.log(`GitHub API error: ${err}`); + }