Reusable GitHub Action for automated Pull Request code review with multi-provider AI support (OpenAI, Anthropic, Google, Mistral, OpenAI-compatible) via AI SDK.
This action:
- Runs on
pull_requestevents. - Reviews all changed files that match
include/excludefilters. - Uses planner + subagents (general/security/performance/testing) in multi-round batches for large diffs, with parallel batch and dimension execution within each round.
- Publishes:
- one PR Review (
pulls.createReview) with inline comments (LEFT/RIGHT), and - one updatable summary issue comment (marker-based, no spam).
- one PR Review (
- Tracks coverage and budget limits; outputs uncovered files + reasons when budget is exhausted.
Simple flow explanation:
Plannerdecides each round's batches undermax_rounds,max_model_calls, andmax_files_per_batch.SubAgent(general)always runs first for each batch, and can dynamically request extra dimensions (security/performance/testing).- Within each round, batches execute in parallel (controlled by
max_concurrency); within each batch, remaining dimensions run in parallel aftergeneralcompletes. - All sub-agent outputs are aggregated, normalized, deduplicated, then mapped to inline-commentable diff lines.
- The publisher writes one review + one updatable summary, with historical dedupe and best-effort outdated comment minimization.
- Multi-provider AI support: OpenAI, Anthropic, Google, Mistral, and OpenAI-compatible endpoints via AI SDK.
- Parallel execution: batches and dimensions within each round run concurrently, controlled by
max_concurrency(default 4). Setmax_concurrency=1for serial execution. - Full coverage target over filtered file set, including no-patch/binary files as file-level review entries.
- Structured schema output validation with one repair retry.
- Degradation mode: if structured output still fails after repair, posts summary-only with explicit reason.
- Duplicate suppression for same
head_sha+ same digest. - Two-stage historical inline lifecycle control:
- Stage 1: dedupe by
path + side + line + issue-keyacross runs. - Stage 2: auto-minimize outdated historical inline comments (GraphQL best-effort).
- Stage 1: dedupe by
- Confidence/evidence gating and semantic deduplication to reduce repeated/low-quality findings.
- Configurable review language via
review_language(defaultEnglish). - Enforces
openai_api_basesafety: HTTPS only, no URL credentials, and hostname allowlist (defaultapi.openai.com). - Automatically loads project guidance from
AGENTS.md,AGENT.md, orCLAUDE.md(priority order) and passes it to review agents. - General-first routing: batch review starts with
general, and onlygeneralcan dynamically request extra dimensions for that batch. - Security note: when using
openai_api_base, only point to trusted HTTPS gateways you control (prefer an allowlist); this endpoint receives review context payloads.
name: PR AI Review
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
ai-review:
runs-on: ubuntu-latest
steps:
- name: AI Code Review
uses: TiyAgents/code-review-agent-action@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
ai_provider: openai
api_key: ${{ secrets.OPENAI_API_KEY }}
api_base: ${{ vars.OPENAI_API_BASE }}
api_base_allowlist: |
api.openai.com
your-gateway.example.com
include: |
**/*.js
**/*.ts
**/*.py
exclude: |
**/*.lock
**/dist/**
**/*.min.js
planner_model: gpt-5.3-codex
reviewer_model: gpt-5.3-codex
review_dimensions: general,security,performance,testing
review_language: English
min_finding_confidence: 0.72
missing_confidence_policy: na
fallback_confidence_value: 0.5
coverage_first_round_primary_only: true
auto_minimize_outdated_comments: true
max_rounds: 8
max_concurrency: 4
max_model_calls: 128 # example override (default: 40)
max_files_per_batch: 8
max_context_chars: 256000 # example override (default: 128000)
max_findings: 60
max_inline_comments: 30| Name | Required | Default | Description |
|---|---|---|---|
github_token |
yes | - | GitHub token with review/comment write permissions |
ai_provider |
no | openai |
AI provider type: openai, anthropic, google, mistral, or openai-compatible |
api_key |
no | env OPENAI_API_KEY |
API key for the selected AI provider |
api_base |
no | env OPENAI_API_BASE |
Optional base URL for the AI provider API endpoint |
api_base_allowlist |
no | api.openai.com |
Allowed hostnames for api_base (HTTPS only) |
openai_api_key |
no | - | Deprecated: use api_key |
openai_api_base |
no | - | Deprecated: use api_base |
openai_api_base_allowlist |
no | - | Deprecated: use api_base_allowlist |
include |
no | ** |
Include globs (comma/newline separated) |
exclude |
no | empty | Exclude globs (comma/newline separated) |
planner_model |
no | gpt-5.3-codex |
Planner model |
reviewer_model |
no | gpt-5.3-codex |
Subagent model |
review_dimensions |
no | general,security,performance,testing |
Subagent dimensions |
review_language |
no | English |
Preferred language for review comments and summary |
min_finding_confidence |
no | 0.72 |
Keep only findings at or above this confidence (0-1) |
missing_confidence_policy |
no | na |
Handling for missing/invalid confidence: drop, na, or fallback |
fallback_confidence_value |
no | 0.5 |
Fallback confidence used only when missing_confidence_policy=fallback |
coverage_first_round_primary_only |
no | true |
Round 1 runs only primary dimension for faster file coverage |
auto_minimize_outdated_comments |
no | true |
Best-effort GraphQL minimize for outdated historical inline comments from this action |
max_rounds |
no | 8 |
Max planning/review rounds |
max_concurrency |
no | 4 |
Max concurrent API calls within a round (batch + dimension parallelism) |
max_model_calls |
no | 40 |
Hard cap for model calls |
max_files_per_batch |
no | 8 |
Batch size cap |
max_context_chars |
no | 128000 |
Per-batch context cap |
max_findings |
no | 60 |
Max findings retained after dedupe/sort |
max_inline_comments |
no | 30 |
Max inline comments posted |
This action spends model calls by rounds × batches × dimensions. With parallel execution (max_concurrency > 1), wall-clock time decreases but total call count stays the same.
Approximation:
calls ~= rounds * (1 + batches * dimensions)
batches ~= ceil(patch_files / max_files_per_batch)
1is the planner call in each round.dimensionsis fromreview_dimensions(default: 4).- Cost scales more with changed file count than changed line count.
- With
coverage_first_round_primary_only=true(default), round 1 often costs less than full dimensions.
Examples:
- If
patch_files=15,max_files_per_batch=8,dimensions=4, one round is about1 + 2*4 = 9calls. - If
patch_files=100,max_files_per_batch=8,dimensions=4, one round is about1 + 13*4 = 53calls.
Practical guidance:
- For medium PRs (10-20 files), start with
max_model_calls: 30-50. - For large PRs (~100 files), start with
max_model_calls: 80-120. - If coverage is low, increase
max_model_callsfirst, thenmax_rounds. - To control cost, reduce
review_dimensions(for examplegeneral,security).
| Name | Description |
|---|---|
covered_files |
Number of covered files in filtered target set |
target_files |
Number of files in filtered target set |
uncovered_files |
Number of uncovered files |
degraded |
true if summary-only degradation was triggered |
- Finding
confidencecan benullwhen the model cannot confidently estimate a value. - Inline comments show unknown values as
N/A. min_finding_confidenceis applied only when confidence is numeric.- Use
missing_confidence_policy=fallbackif your downstream expects numeric confidence only. - When
missing_confidence_policyisdroporna,fallback_confidence_valueis ignored.
- For public fork PRs, repository secrets are typically unavailable on
pull_request. - If
OPENAI_API_KEYis unavailable, this action cannot call the model. - If you choose to run on
pull_request_target, evaluate security risk carefully before using untrusted code context.
- Push this repository to GitHub.
- Tag a release, for example
v1.0.0. - Consumers reference:
uses: TiyAgents/code-review-agent-action@v1.
Use this when validating which structured-output mode a configured model/base URL can successfully follow.
- Create
.envfrom.env.exampleand set:OPENAI_API_KEYOPENAI_API_BASE(optional)MODEL(supports|to test multiple models, e.g.model-a|model-b)COMPATIBILITY_MODES(optional,|or,separated; defaults to all explicit modes)BUG_PROBE_REQUIRED(optional, defaultfalse)
- Run:
npm run test:schema-supportThe script performs planner/reviewer checks across the supported compatibility modes and reports which mode succeeded.
It also includes a seeded-bug probe (bug_probe) to gauge defect detection capability:
- By default, bug probe is non-blocking (reported as PASS/FAIL).
- Set
BUG_PROBE_REQUIRED=trueto make bug probe failure exit non-zero.
- Trigger support: this action expects
pull_requestevent payload. - Inline comments use
path+side+line, with fallback to summary-only file-level entries when mapping is invalid. - Inline comments include a stable hidden issue key marker for cross-run dedupe and stale-thread minimization.
- Summary comment update uses marker metadata and deduplicates by
head_sha+ digest.
