CI code review
Every pull request can get an automated review without anyone installing a local tool. A GitHub Actions job captures the PR diff, sends it to POST https://api.mirapi.ai/v1/chat/completions, and posts the model’s findings back as a comment.
Deliverable: every pull request gets an automated review comment from MirAPI.
Prerequisites: a GitHub repository with Actions enabled; a MIRAPI_API_KEY stored as a repository secret.
1. Store the key
Section titled “1. Store the key”- Create a dedicated key in https://console.mirapi.ai → API Keys.
- Set a spend quota on it — CI runs unattended, and the quota caps the damage of a buggy workflow.
- In GitHub: Settings → Secrets and variables → Actions, add
MIRAPI_API_KEY.
Never put the key in the workflow file or in .env inside the repo. A dedicated CI key also keeps that spend visible in one place for reconciliation.
2. The workflow
Section titled “2. The workflow”Create .github/workflows/mirapi-review.yml:
name: MirAPI code review
on: pull_request: types: [opened, synchronize]
permissions: contents: read pull-requests: write
jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Capture the diff run: | git fetch origin "${{ github.event.pull_request.base.ref }}" git diff "origin/${{ github.event.pull_request.base.ref }}...HEAD" > /tmp/pr.diff wc -c /tmp/pr.diff
- name: Review with MirAPI env: MIRAPI_API_KEY: ${{ secrets.MIRAPI_API_KEY }} run: | python3 - <<'PY' import os, sys, requests diff = open("/tmp/pr.diff", encoding="utf-8", errors="replace").read()[:30000] resp = requests.post( "https://api.mirapi.ai/v1/chat/completions", headers={"Authorization": f"Bearer {os.environ['MIRAPI_API_KEY']}"}, json={ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "You are a careful code reviewer. Report bugs, security issues, and regressions concisely."}, {"role": "user", "content": f"Review this diff:\n\n{diff}"}, ], "max_tokens": 1500, }, timeout=120, ) if resp.status_code != 200: print(f"MirAPI error {resp.status_code}: {resp.text}", file=sys.stderr) sys.exit(1) body = resp.json()["choices"][0]["message"]["content"] open("/tmp/review.txt", "w").write(body) PY
- name: Post the comment env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh pr comment "${{ github.event.pull_request.number }}" --body-file /tmp/review.txtThe request body has three fields that matter here:
| Field | Purpose |
|---|---|
model |
The model that writes the review — deepseek-chat in the example. |
messages |
A system prompt with review instructions plus a user message carrying the diff. |
max_tokens |
Caps the review length, which also caps the token spend on each run. |
Optional tuning: set reasoning_effort to make a reasoning model think harder (reasoning tokens bill as output), or use response_format to have the model return JSON when it supports structured output.
3. Review request reference
Section titled “3. Review request reference”The same key works across three protocol skins. The example above uses the OpenAI skin; the other two let you fire the same review from Anthropic- or Gemini-style clients.
| Protocol | Base URL | Endpoint | Auth header |
|---|---|---|---|
| OpenAI | https://api.mirapi.ai/v1 |
POST /chat/completions |
Authorization: Bearer |
| Anthropic | https://api.mirapi.ai |
POST /v1/messages |
x-api-key or Authorization: Bearer |
| Gemini | https://api.mirapi.ai |
POST /v1beta/models/{model}:generateContent |
x-goog-api-key |
OpenAI (chat completions)
Section titled “OpenAI (chat completions)”POST https://api.mirapi.ai/v1/chat/completionsAuthorization: Bearer sk-...{ "model": "deepseek-chat", "messages": [ {"role": "system", "content": "You are a careful code reviewer."}, {"role": "user", "content": "Review this diff:\n\n…"} ], "max_tokens": 1500}Anthropic (messages)
Section titled “Anthropic (messages)”POST https://api.mirapi.ai/v1/messagesx-api-key: sk-...{ "model": "deepseek-chat", "max_tokens": 1500, "system": "You are a careful code reviewer.", "messages": [ {"role": "user", "content": "Review this diff:\n\n…"} ]}Gemini (generateContent)
Section titled “Gemini (generateContent)”POST https://api.mirapi.ai/v1beta/models/deepseek-chat:generateContentx-goog-api-key: sk-...{ "contents": [ { "role": "user", "parts": [{"text": "You are a careful code reviewer.\n\nReview this diff:\n\n…"}] } ]}Not every model is reachable through every skin — check GET /v1/models for a model’s capabilities before wiring it into CI.
4. Error handling
Section titled “4. Error handling”The workflow should fail loudly rather than post an empty comment, so the script exits non-zero on a non-200 response. The status codes you are most likely to meet in CI:
| Code | Meaning | What to do |
|---|---|---|
| 401 | Missing or invalid key | Fix the MIRAPI_API_KEY secret |
| 403 | Valid key, request rejected (balance, whitelist, quota, IP) | Resolve the cause, then retry |
| 429 | Rate limited | Back off with jitter (1s → 2s → 4s, capped ~30s) — the gateway does not retry for you |
| 500 | Gateway or upstream failure | Retry idempotent calls |
The error body uses the OpenAI envelope — {"error": {"message", "type", "param", "code"}} — and every message ends with a request ID. Save that ID from a failed run; it is what support needs to look up the call.
5. Verify
Section titled “5. Verify”Open a pull request and check the review comment appears. Then confirm the request was billed:
curl https://api.mirapi.ai/api/log/token -H "Authorization: Bearer $MIRAPI_API_KEY"/api/log/token lists per-request rows; https://api.mirapi.ai/api/usage/token gives account-level totals. A non-streaming response only reports token counts in usage — the dollar amount lives in the console and these logs.
Pitfalls
Section titled “Pitfalls”- Rate limits: several commits in one push still produce one review (one
synchronizeevent). If you run several models or longer prompts, pace them; MirAPI returns 429 when you exceed the limit. - Diff size: the script truncates at 30 000 characters; split very large diffs across calls if needed.
- 403 with a valid key means the quota is exhausted or the model is not whitelisted — raise the quota or fix the whitelist.
Approximate cost
Section titled “Approximate cost”One review of a typical PR is a few thousand tokens — usually a fraction of a cent to a few cents. Spend draws from your prepaid balance, which never expires, so an idle workflow costs nothing. To keep reviews cheap, lower max_tokens, truncate the diff, and keep the system prompt identical across runs so it can benefit from automatic prompt caching.
Related links
Section titled “Related links”- Automatic code review — the same idea inside Claude Code.
- First integration — a minimal first call if this workflow is your first time using MirAPI.
- Errors — the full status-code and retry reference.
- Configuration — key constraints (whitelist, quota, validity, IP allowlist).
- Billing & top-ups — prepaid balance and usage reconciliation.