From d239f26052bfde4992e72421a911b36e68104e38 Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 11 Jul 2026 20:11:38 +0000 Subject: [PATCH 1/3] chore(reviewbot): expand multi-model cascade with fallback endpoints Currently, LocalAgentConfig only configures a single default model without an explicit endpoint attached, which causes startup errors or rate-limit failures when quota runs out. To fix, import GeminiAPIEndpoint and ModelTarget, and configure a prioritized multi-model cascade across Gemini 3 and Gemini 2 so the agent automatically falls back to available quota. --- tools/private/reviewbot/antigravity_review.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tools/private/reviewbot/antigravity_review.py b/tools/private/reviewbot/antigravity_review.py index e415e42a91..c9db02cbdf 100644 --- a/tools/private/reviewbot/antigravity_review.py +++ b/tools/private/reviewbot/antigravity_review.py @@ -10,6 +10,7 @@ from pathlib import Path from google.antigravity import Agent, CapabilitiesConfig, LocalAgentConfig +from google.antigravity.models import GeminiAPIEndpoint, ModelTarget def parse_args(): @@ -30,9 +31,28 @@ async def main(): "reviews on pull requests." ) + # Create the default endpoint picking up GEMINI_API_KEY from the environment. + endpoint = GeminiAPIEndpoint() + # Initialize the Antigravity Agent in read-only mode for security. # Register the review-pr skill from the local reviewbot folder. + # Provide a comprehensive prioritized cascade across Gemini 3 and Gemini 2 + # to automatically fall back if any model hits free-tier quota limits (429) + # or temporary unavailability. config = LocalAgentConfig( + models=[ + ModelTarget(name="gemini-3.5-flash", endpoint=endpoint), + ModelTarget(name="gemini-3.1-pro-preview", endpoint=endpoint), + ModelTarget(name="gemini-3.1-flash-lite", endpoint=endpoint), + ModelTarget(name="gemini-3-pro-preview", endpoint=endpoint), + ModelTarget(name="gemini-2.5-pro", endpoint=endpoint), + ModelTarget(name="gemini-2.5-flash", endpoint=endpoint), + ModelTarget(name="gemini-2.5-flash-lite", endpoint=endpoint), + ModelTarget(name="gemini-2.0-flash", endpoint=endpoint), + ModelTarget(name="gemini-2.0-flash-lite", endpoint=endpoint), + ModelTarget(name="gemini-flash-latest", endpoint=endpoint), + ModelTarget(name="gemini-pro-latest", endpoint=endpoint), + ], system_instructions=system_instructions, skills_paths=[str(Path(__file__).parent / "skills" / "review-pr" / "SKILL.md")], capabilities=CapabilitiesConfig( From 2609dd5f09bd6a15db497ac6339f8f8d22c4de9c Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sat, 11 Jul 2026 20:20:01 +0000 Subject: [PATCH 2/3] rm gemini 2 as they are no longer usable --- tools/private/reviewbot/antigravity_review.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/private/reviewbot/antigravity_review.py b/tools/private/reviewbot/antigravity_review.py index c9db02cbdf..a986065434 100644 --- a/tools/private/reviewbot/antigravity_review.py +++ b/tools/private/reviewbot/antigravity_review.py @@ -45,11 +45,6 @@ async def main(): ModelTarget(name="gemini-3.1-pro-preview", endpoint=endpoint), ModelTarget(name="gemini-3.1-flash-lite", endpoint=endpoint), ModelTarget(name="gemini-3-pro-preview", endpoint=endpoint), - ModelTarget(name="gemini-2.5-pro", endpoint=endpoint), - ModelTarget(name="gemini-2.5-flash", endpoint=endpoint), - ModelTarget(name="gemini-2.5-flash-lite", endpoint=endpoint), - ModelTarget(name="gemini-2.0-flash", endpoint=endpoint), - ModelTarget(name="gemini-2.0-flash-lite", endpoint=endpoint), ModelTarget(name="gemini-flash-latest", endpoint=endpoint), ModelTarget(name="gemini-pro-latest", endpoint=endpoint), ], From 5c007934991171ac653340bda308997fc4944e4b Mon Sep 17 00:00:00 2001 From: Richard Levasseur Date: Sun, 12 Jul 2026 05:57:50 +0000 Subject: [PATCH 3/3] perf(reviewbot): optimize quota usage and git diff calculation The automated PR review bot previously consumed excessive free-tier quota and triggered on every opened PR due to multi-turn filesystem exploration and automatic event listeners. To reduce token consumption and request-per-minute limits: - Pre-hydrate prompt with git diff in python runner and lock tools to read-only - Condense SKILL.md and instruct single-pass analysis - Remove automatic PR open trigger so bot only runs on explicit /review comment - Negotiate minimal git fetch history in CI via dynamic deepening loop --- .github/workflows/automated_pr_review.yaml | 48 +++++++++++++------ tools/private/reviewbot/antigravity_review.py | 28 ++++++++--- .../reviewbot/skills/review-pr/SKILL.md | 2 + 3 files changed, 57 insertions(+), 21 deletions(-) diff --git a/.github/workflows/automated_pr_review.yaml b/.github/workflows/automated_pr_review.yaml index af2da7e0ca..927adbbd4c 100644 --- a/.github/workflows/automated_pr_review.yaml +++ b/.github/workflows/automated_pr_review.yaml @@ -5,8 +5,6 @@ name: Automated Code Review # to secrets (like GEMINI_API_KEY) even for fork PRs. # Using pull_request for now during setup/testing. on: - pull_request: - types: [opened] issue_comment: types: [created] @@ -15,21 +13,24 @@ permissions: pull-requests: read jobs: + # Always runs so the workflow run exits cleanly without a "No jobs ran" error + # when the review job's if-condition evaluates to false. + noop: + runs-on: ubuntu-latest + steps: + - name: Workflow trigger check + run: echo "Workflow triggered successfully." + review: runs-on: ubuntu-latest - # Trigger only if: - # 1. It is a pull_request event, it is NOT a draft, and the author is a maintainer - # (OWNER, MEMBER, or COLLABORATOR). - # 2. OR it is a regular conversation comment on a pull request, the comment body has a line starting with "/review", - # and the commenter is a maintainer. + # Trigger only if it is a regular comment on a pull request, the comment body has a line + # starting with "/review", and the commenter is a maintainer (OWNER, MEMBER, or COLLABORATOR). if: > - (github.event_name == 'pull_request' && !github.event.pull_request.draft && - contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.pull_request.author_association)) || - (github.event_name == 'issue_comment' && github.event.issue.pull_request != null && - (startsWith(github.event.comment.body, '/review') || - contains(github.event.comment.body, '\n/review') || - contains(github.event.comment.body, '\r\n/review')) && - contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association)) + github.event_name == 'issue_comment' && github.event.issue.pull_request != null && + (startsWith(github.event.comment.body, '/review') || + contains(github.event.comment.body, '\n/review') || + contains(github.event.comment.body, '\r\n/review')) && + contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) steps: - name: Checkout PR Branch uses: actions/checkout@v7 @@ -40,6 +41,25 @@ jobs: ref: refs/pull/${{ github.event.pull_request.number || github.event.issue.number }}/head persist-credentials: false + - name: Fetch Base Branch and Negotiate Minimal Diff History + env: + PR_COMMITS: ${{ github.event.pull_request.commits }} + run: | + # 1. Fetch the tip of main + git fetch origin main:refs/remotes/origin/main --depth=1 + + # 2. Check if we already have the merge base (common ancestor) + if ! git merge-base origin/main HEAD >/dev/null 2>&1; then + # If we know the exact number of commits in the PR, deepen by (PR_COMMITS + 10) + if [ -n "$PR_COMMITS" ] && [ "$PR_COMMITS" != "null" ]; then + git fetch --deepen="$((PR_COMMITS + 10))" + fi + # 3. If it's an issue_comment event (where PR_COMMITS is null) or still shallow, unshallow/deepen + if ! git merge-base origin/main HEAD >/dev/null 2>&1; then + git fetch --unshallow || git fetch --deepen=50 + fi + fi + - name: Checkout Reviewbot (Base Branch) uses: actions/checkout@v7 with: diff --git a/tools/private/reviewbot/antigravity_review.py b/tools/private/reviewbot/antigravity_review.py index a986065434..3aef480f3b 100644 --- a/tools/private/reviewbot/antigravity_review.py +++ b/tools/private/reviewbot/antigravity_review.py @@ -2,15 +2,16 @@ # requires-python = ">=3.11" # dependencies = [ # "google-antigravity", -# "requests", # ] # /// import argparse import asyncio +import subprocess from pathlib import Path from google.antigravity import Agent, CapabilitiesConfig, LocalAgentConfig from google.antigravity.models import GeminiAPIEndpoint, ModelTarget +from google.antigravity.types import BuiltinTools def parse_args(): @@ -19,11 +20,26 @@ def parse_args(): return parser.parse_args() +def get_pr_diff() -> str: + """Fetches the git diff for the current pull request against origin/main.""" + try: + return subprocess.check_output( + ["git", "diff", "origin/main...HEAD"], text=True, stderr=subprocess.DEVNULL + ) + except Exception: + return "No diff could be automatically extracted via git commands." + + async def main(): args = parse_args() - # Read prompt file - prompt = Path(args.prompt).read_text() + # Read prompt file and pre-hydrate with the exact PR code diff + base_prompt = Path(args.prompt).read_text() + diff_text = get_pr_diff() + prompt = ( + f"{base_prompt}\n\n## Pull Request Git Diff\n" + f"Here is the exact code diff for this pull request:\n```diff\n{diff_text}\n```" + ) # General coordinator instructions for the reviewer agent. system_instructions = ( @@ -36,7 +52,7 @@ async def main(): # Initialize the Antigravity Agent in read-only mode for security. # Register the review-pr skill from the local reviewbot folder. - # Provide a comprehensive prioritized cascade across Gemini 3 and Gemini 2 + # Provide a comprehensive prioritized cascade across Gemini 3 models # to automatically fall back if any model hits free-tier quota limits (429) # or temporary unavailability. config = LocalAgentConfig( @@ -51,9 +67,7 @@ async def main(): system_instructions=system_instructions, skills_paths=[str(Path(__file__).parent / "skills" / "review-pr" / "SKILL.md")], capabilities=CapabilitiesConfig( - allow_filesystem_read=True, - allow_filesystem_write=False, - allow_network=False, + enabled_tools=BuiltinTools.read_only(), ), ) diff --git a/tools/private/reviewbot/skills/review-pr/SKILL.md b/tools/private/reviewbot/skills/review-pr/SKILL.md index 18c998f9ed..915a526686 100644 --- a/tools/private/reviewbot/skills/review-pr/SKILL.md +++ b/tools/private/reviewbot/skills/review-pr/SKILL.md @@ -5,6 +5,8 @@ description: Perform a read-only code review on a pull request. # review-pr +IMPORTANT: The exact git diff for the pull request is pre-provided right in your prompt. Analyze this provided diff directly in a single pass without calling exploratory directory listing or file reading tools unless you specifically need surrounding lines of context from a modified file. + You are an expert Starlark, Python, and Bazel code reviewer. Analyze the changed files for correctness, edge cases, and performance. Focus strictly on logical correctness, concurrency safety, system architecture, performance bottlenecks,