feat: add AI worker workflows (Gemini + Jules)
Distributed AI compute using contributor's free tiers:
Gemini 2.0 Flash (ai-worker.yml):
- 1500 req/day free from Google
- Code review, security scan, bug detection
- Contributor sets GEMINI_API_KEY in fork secrets
Jules/Copilot (jules-dispatch.yml):
- Triggered by @jules or /jules comments
- Creates PRs to fix issues automatically
- Uses contributor's Copilot allowance (free for OSS)
Documentation (doc/free-tier-compute.md):
- Setup guide for all free tiers
- Compute distribution model diagram
- Donor fleet instructions
Innocent strategy: Jules commits fixes to contributor's fork 😇
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c03c49a539
commit
a78ef46133
3 changed files with 293 additions and 0 deletions
126
.github/workflows/ai-worker.yml
vendored
Normal file
126
.github/workflows/ai-worker.yml
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
name: AI Worker (Free Tier)
|
||||
|
||||
# Runs on contributor's fork - uses THEIR API allowances
|
||||
# Gemini 2.0 Flash: 1500 req/day free
|
||||
# Jules: Available to all GitHub users
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
env:
|
||||
# Contributors set their own GEMINI_API_KEY in fork secrets
|
||||
# Free tier: 1500 requests/day, 1M tokens/min
|
||||
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
|
||||
|
||||
jobs:
|
||||
# Analyze PR with Gemini (contributor's free tier)
|
||||
gemini-review:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get diff
|
||||
id: diff
|
||||
run: |
|
||||
DIFF=$(git diff origin/${{ github.base_ref }}...HEAD | head -c 50000)
|
||||
echo "diff<<EOFMARKER" >> $GITHUB_OUTPUT
|
||||
echo "$DIFF" >> $GITHUB_OUTPUT
|
||||
echo "EOFMARKER" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Gemini Analysis
|
||||
if: env.GEMINI_API_KEY != ''
|
||||
id: gemini
|
||||
run: |
|
||||
# Call Gemini 2.0 Flash (free tier)
|
||||
RESPONSE=$(curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=$GEMINI_API_KEY" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d @- << 'PAYLOAD'
|
||||
{
|
||||
"contents": [{
|
||||
"parts": [{
|
||||
"text": "Review this code diff. Be concise. List: 1) Security issues 2) Bugs 3) Improvements. If none, say 'LGTM'.\n\nDiff:\n${{ steps.diff.outputs.diff }}"
|
||||
}]
|
||||
}],
|
||||
"generationConfig": {
|
||||
"temperature": 0.2,
|
||||
"maxOutputTokens": 1000
|
||||
}
|
||||
}
|
||||
PAYLOAD
|
||||
)
|
||||
|
||||
# Extract text from response
|
||||
REVIEW=$(echo "$RESPONSE" | jq -r '.candidates[0].content.parts[0].text // "Analysis failed"')
|
||||
echo "review<<EOFMARKER" >> $GITHUB_OUTPUT
|
||||
echo "$REVIEW" >> $GITHUB_OUTPUT
|
||||
echo "EOFMARKER" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Post Gemini review
|
||||
if: steps.gemini.outputs.review != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const review = `${{ steps.gemini.outputs.review }}`;
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `## 🤖 Gemini Analysis (Free Tier)\n\n${review}\n\n---\n_Powered by contributor's Gemini API allowance (1500 req/day free)_`
|
||||
});
|
||||
|
||||
# Trigger Jules to fix issues (contributor's Copilot allowance)
|
||||
trigger-jules:
|
||||
if: github.event_name == 'issues' && github.event.label.name == 'agent:ready'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check Jules eligibility
|
||||
id: check
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// Check if this repo has Copilot coding agent enabled
|
||||
const issue = context.payload.issue;
|
||||
|
||||
console.log(`Issue #${issue.number}: ${issue.title}`);
|
||||
console.log(`Labels: ${issue.labels.map(l => l.name).join(', ')}`);
|
||||
|
||||
// Jules can be assigned to issues with specific labels
|
||||
const hasAgentic = issue.labels.some(l => l.name === 'agentic');
|
||||
|
||||
return { eligible: hasAgentic, issueNumber: issue.number };
|
||||
|
||||
- name: Assign to Jules
|
||||
if: steps.check.outputs.result != ''
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
// Trigger Jules by adding the copilot label
|
||||
// Jules will pick up the issue and create a PR
|
||||
try {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.issue.number,
|
||||
labels: ['copilot'] // This triggers Jules/Copilot coding agent
|
||||
});
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.issue.number,
|
||||
body: `## 🤖 Jules Activated\n\nThis issue has been assigned to GitHub Copilot coding agent (Jules).\n\nJules will:\n1. Analyze the issue\n2. Create a solution\n3. Open a PR with the fix\n\n_Using contributor's Copilot allowance_ 🆓`
|
||||
});
|
||||
} catch (e) {
|
||||
console.log('Could not assign to Jules:', e.message);
|
||||
}
|
||||
64
.github/workflows/jules-dispatch.yml
vendored
Normal file
64
.github/workflows/jules-dispatch.yml
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
name: Jules Task Dispatch
|
||||
|
||||
# Dispatch tasks to Jules (Copilot coding agent)
|
||||
# Jules works on the contributor's fork using their Copilot Pro allowance
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
dispatch-to-jules:
|
||||
# Trigger when someone comments "@jules" or "/jules"
|
||||
if: contains(github.event.comment.body, '@jules') || contains(github.event.comment.body, '/jules')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Parse Jules command
|
||||
id: parse
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const comment = context.payload.comment.body;
|
||||
|
||||
// Extract task from comment
|
||||
// Format: @jules <task description>
|
||||
// or: /jules fix the bug in auth.php
|
||||
const match = comment.match(/(?:@jules|\/jules)\s+(.+)/i);
|
||||
const task = match ? match[1].trim() : null;
|
||||
|
||||
return { task, commenter: context.payload.comment.user.login };
|
||||
|
||||
- name: Create Jules task
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const result = ${{ steps.parse.outputs.result }};
|
||||
|
||||
if (!result.task) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `@${result.commenter} Please specify a task for Jules:\n\n\`@jules fix the authentication bug\`\n\`@jules add unit tests for User model\`\n\`@jules refactor this to use async/await\``
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Add copilot label to trigger Jules
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['copilot', 'agent:wip']
|
||||
});
|
||||
|
||||
// Create a sub-issue or task for Jules
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: `## 🤖 Jules Task Queued\n\n**Task:** ${result.task}\n**Requested by:** @${result.commenter}\n\nJules (Copilot coding agent) will work on this using the repository owner's Copilot allowance.\n\n---\n_Tip: Fork this repo to use your own Copilot Pro allowance for faster processing._`
|
||||
});
|
||||
103
doc/free-tier-compute.md
Normal file
103
doc/free-tier-compute.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Free Tier Compute Guide
|
||||
|
||||
This project uses distributed compute from contributors' free tier allowances. Here's how to maximize it.
|
||||
|
||||
## GitHub Actions (Microsoft)
|
||||
|
||||
**Free tier:** 2,000 minutes/month for public repos
|
||||
|
||||
When you fork this repo, CI runs on YOUR allowance, not upstream's. This includes:
|
||||
- Full test suite
|
||||
- Linting and formatting
|
||||
- Security scanning
|
||||
- AI-powered triage
|
||||
|
||||
## Gemini API (Google)
|
||||
|
||||
**Free tier:** 1,500 requests/day, 1M tokens/minute
|
||||
|
||||
### Setup
|
||||
|
||||
1. Get API key: https://aistudio.google.com/apikey
|
||||
2. Add to your fork's secrets:
|
||||
```bash
|
||||
gh secret set GEMINI_API_KEY --repo YOUR_USER/YOUR_FORK
|
||||
```
|
||||
|
||||
The `ai-worker.yml` workflow will use your Gemini allowance for:
|
||||
- Code review
|
||||
- Bug detection
|
||||
- Security analysis
|
||||
|
||||
## Jules / Copilot (GitHub)
|
||||
|
||||
**Free tier:** Available with GitHub Copilot (free for OSS contributors)
|
||||
|
||||
### Trigger Jules
|
||||
|
||||
Comment on any issue:
|
||||
```
|
||||
@jules fix the authentication bug in login.php
|
||||
@jules add unit tests for the User model
|
||||
@jules refactor to use async/await
|
||||
```
|
||||
|
||||
Jules will:
|
||||
1. Analyze the issue
|
||||
2. Write code to fix it
|
||||
3. Create a PR on your fork
|
||||
4. Uses YOUR Copilot allowance
|
||||
|
||||
### Get Copilot Free
|
||||
|
||||
- Open source contributors: https://github.com/settings/copilot
|
||||
- Students: GitHub Education pack
|
||||
- Verified OSS maintainers: Free Copilot Pro
|
||||
|
||||
## CodeRabbit
|
||||
|
||||
**Free tier:** Unlimited for public repos
|
||||
|
||||
Already configured via `.coderabbit.yaml`. Reviews run automatically on PRs.
|
||||
|
||||
## Compute Distribution Model
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────┐
|
||||
│ CONTRIBUTOR FORK │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ GitHub │ │ Gemini │ │
|
||||
│ │ Actions │ │ 2.0 Flash │ │
|
||||
│ │ FREE │ │ FREE │ │
|
||||
│ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Jules/ │ │ CodeRabbit │ │
|
||||
│ │ Copilot │ │ FREE │ │
|
||||
│ │ FREE* │ │ │ │
|
||||
│ └─────────────┘ └─────────────┘ │
|
||||
│ │
|
||||
│ * With Copilot Pro (free for OSS) │
|
||||
└────────────────────────────────────────────┘
|
||||
│
|
||||
│ PR (already analyzed)
|
||||
▼
|
||||
┌────────────────────────────────────────────┐
|
||||
│ UPSTREAM REPO │
|
||||
│ │
|
||||
│ Just verify fork CI passed ✓ │
|
||||
│ Cost: ~$0.001 per PR │
|
||||
└────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## For Donor Fleet Members
|
||||
|
||||
If you want to contribute compute without coding:
|
||||
|
||||
1. Fork the repo
|
||||
2. Set up your API keys (Gemini, etc.)
|
||||
3. Keep Actions enabled
|
||||
4. Pick up `agent:review` tasks to verify others' work
|
||||
|
||||
Your free tier becomes part of the distributed compute network!
|
||||
Loading…
Add table
Reference in a new issue