From f6ef97c53f1b1d7d7a94df6fd00be96b71efd2b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 7 Jul 2026 23:11:23 +0900 Subject: [PATCH] fix(opencode-review): add native OpenAI backend as lead review model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of the org-wide merge freeze: the required `opencode-review` check has failed 100% of recent runs across all repos, leaving 12+ PRs stuck at REVIEW_REQUIRED and every run hanging to the 350-min timeout. The embedded OpenCode config routed EVERY model in the pool through the single `github-models` provider (`{env:STRIX_GITHUB_MODELS_TOKEN}` at https://models.github.ai/inference). GitHub Models rate-limits ("Too many requests") and caps request bodies at ~4000 tokens (413 tokens_limit_reached), so the reviewer never produced a verdict on any model and the pool exhausted the whole step without approving. Fix: give the reviewer a working, un-throttled backend by adding a native OpenAI provider that hits api.openai.com directly with the org `OPENAI_API_KEY` secret, and lead the model pool with it. - opencode.jsonc config: add `provider.openai` (npm @ai-sdk/openai, baseURL https://api.openai.com/v1, apiKey {env:OPENAI_API_KEY}) with gpt-5 and gpt-5-mini defined reasoning=true / reasoningEffort=high so they satisfy assert_opencode_reasoning_effort.py; add "openai" to enabled_providers. - OPENCODE_MODEL_CANDIDATES: prepend `openai/gpt-5 openai/gpt-5-mini` as the lead candidates. The pool passes --model per attempt, so candidate order (not the config `model` default) is the real lead; the existing github-models entries are kept unchanged as fallbacks. - Expose OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} in both env blocks that invoke opencode with this config (the model-pool step and the approve/failed-check-diagnosis step) so {env:OPENAI_API_KEY} resolves. The config `model`/`small_model` defaults are intentionally left on github-models/deepseek: they are overridden at runtime by the pool's explicit --model, and static self-tests pin those strings. This unblocks real reviews without weakening the review gate — if the OpenAI key is absent or a direct call fails, the pool falls through to the existing github-models candidates exactly as before. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01AxU2xaupAjp912oDNFuWyd --- .github/workflows/opencode-review.yml | 80 +++++++++++++++++++++++---- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index b0f38796..8be016e3 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -2006,7 +2006,7 @@ jobs: "$schema": "https://opencode.ai/config.json", "model": "github-models/deepseek/deepseek-r1-0528", "small_model": "github-models/deepseek/deepseek-v3-0324", - "enabled_providers": ["github-models"], + "enabled_providers": ["openai", "github-models"], "lsp": true, "mcp": { "codegraph": { @@ -2132,6 +2132,50 @@ jobs: } }, "provider": { + "openai": { + "npm": "@ai-sdk/openai", + "name": "OpenAI (direct)", + "options": { + "baseURL": "https://api.openai.com/v1", + "apiKey": "{env:OPENAI_API_KEY}" + }, + "models": { + "gpt-5": { + "name": "OpenAI GPT-5 (direct)", + "tool_call": true, + "reasoning": true, + "options": { + "reasoningEffort": "high" + }, + "variants": { + "high": { + "reasoningEffort": "high" + } + }, + "limit": { + "context": 400000, + "output": 128000 + } + }, + "gpt-5-mini": { + "name": "OpenAI GPT-5 Mini (direct)", + "tool_call": true, + "reasoning": true, + "options": { + "reasoningEffort": "high" + }, + "variants": { + "high": { + "reasoningEffort": "high" + } + }, + "limit": { + "context": 400000, + "output": 128000 + } + } + } + }, "github-models": { "npm": "@ai-sdk/openai-compatible", "name": "GitHub Models", @@ -2339,20 +2383,31 @@ jobs: env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} + # Native OpenAI backend for the lead review model. GitHub Models + # rate-limits every request and caps bodies at ~4000 tokens, so the + # rate-starved shared pool never returned a verdict; hitting + # api.openai.com directly with the org OPENAI_API_KEY gives the lead + # model a working, un-throttled backend. Resolves {env:OPENAI_API_KEY} + # in the opencode.jsonc "openai" provider block. + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} USE_GITHUB_TOKEN: "true" SHARE: "false" NPM_CONFIG_IGNORE_SCRIPTS: "true" NO_COLOR: "1" - # Ordered contract-reliability first, then quota, with the rate-starved - # flagships last. The mini reasoning models (o4-mini, o3-mini, gpt-5- - # mini/nano/chat) reliably emit the strict review contract — every - # required label and only source-backed findings — so they lead. The - # high-quota non-reasoning models (deepseek-v3, mistral, llama-4) emit - # bare or hallucinated reviews the publish/approve gates reject, so - # they are fallbacks only. gpt-5/o3 ("Reasoning" tier, 8-12 req/day) - # stay last: first-placing them stalled every review until timeout - # because a rate-limited/hung flagship never fell back. - OPENCODE_MODEL_CANDIDATES: "github-models/openai/o4-mini github-models/openai/o3-mini github-models/openai/gpt-5-mini github-models/openai/gpt-5-nano github-models/openai/gpt-5-chat github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1 github-models/deepseek/deepseek-v3-0324 github-models/mistral-ai/mistral-medium-2505 github-models/meta/llama-4-maverick-17b-128e-instruct-fp8 github-models/meta/llama-4-scout-17b-16e-instruct github-models/openai/o3 github-models/openai/gpt-5" + # Lead with the NATIVE OpenAI backend (openai/gpt-5, openai/gpt-5-mini + # via api.openai.com with the org OPENAI_API_KEY). GitHub Models + # rate-limited ("Too many requests") and 4000-token-capped + # (413 tokens_limit_reached) EVERY model in the shared pool, so the + # reviewer never produced a verdict and every run hung to the 350-min + # timeout — a 100% org-wide failure. The native provider is not subject + # to those limits, so it can actually complete and approve. The + # existing github-models entries stay as fallbacks (tried only if the + # native key is missing or the direct call fails). + # github-models ordering rationale (unchanged): contract-reliable mini + # reasoning models first, high-quota non-reasoning models next, and the + # rate-starved github-models flagships (gpt-5/o3, 8-12 req/day) last so + # a throttled/hung leader always falls back instead of eating the step. + OPENCODE_MODEL_CANDIDATES: "openai/gpt-5 openai/gpt-5-mini github-models/openai/o4-mini github-models/openai/o3-mini github-models/openai/gpt-5-mini github-models/openai/gpt-5-nano github-models/openai/gpt-5-chat github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1 github-models/deepseek/deepseek-v3-0324 github-models/mistral-ai/mistral-medium-2505 github-models/meta/llama-4-maverick-17b-128e-instruct-fp8 github-models/meta/llama-4-scout-17b-16e-instruct github-models/openai/o3 github-models/openai/gpt-5" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. @@ -2732,6 +2787,9 @@ jobs: CHECK_LOOKUP_GH_TOKEN: ${{ github.token }} GH_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.inputs.target_repository || github.repository }} STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} + # Exposed so the "openai" provider in opencode.jsonc resolves during the + # failed-check diagnosis opencode run that shares this config. + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} OPENCODE_APP_TOKEN: ${{ steps.opencode_app_token.outputs.token }} OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md