From eb20b0614631b8c30bf0d8797d4c1c6839b0cce9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 7 Jul 2026 21:56:40 +0900 Subject: [PATCH 1/2] fix(opencode): add OpenAI direct provider to reviewer model pool (prevents github-models exhaustion) --- .github/workflows/opencode-review.yml | 49 +++++++++++++++++++++++++-- tests/test_opencode_agent_contract.py | 2 ++ 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index b0f38796..dad7a967 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": ["github-models", "openai"], "lsp": true, "mcp": { "codegraph": { @@ -2326,6 +2326,50 @@ jobs: } } } + }, + "openai": { + "npm": "@ai-sdk/openai", + "name": "OpenAI", + "options": { + "baseURL": "https://api.openai.com/v1", + "apiKey": "{env:OPENAI_API_KEY}" + }, + "models": { + "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 + } + }, + "o4-mini": { + "name": "OpenAI o4-mini (direct)", + "tool_call": true, + "reasoning": true, + "options": { + "reasoningEffort": "high" + }, + "variants": { + "high": { + "reasoningEffort": "high" + } + }, + "limit": { + "context": 200000, + "output": 100000 + } + } + } } } }' >"${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" @@ -2339,6 +2383,7 @@ jobs: env: STRIX_GITHUB_MODELS_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} GITHUB_TOKEN: ${{ secrets.STRIX_GITHUB_MODELS_TOKEN || github.token }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} USE_GITHUB_TOKEN: "true" SHARE: "false" NPM_CONFIG_IGNORE_SCRIPTS: "true" @@ -2352,7 +2397,7 @@ jobs: # 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" + 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 openai/gpt-5-mini openai/o4-mini 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. diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index ece0a6bc..dc3cd3a9 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -297,6 +297,8 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): "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 " + "openai/gpt-5-mini " + "openai/o4-mini " "github-models/openai/o3 " 'github-models/openai/gpt-5"' ) in workflow From f3ec80099e2ddeabbdd3a591eaae0f829ae33648 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 8 Jul 2026 10:38:44 +0900 Subject: [PATCH 2/2] fix(ci): reconcile stale contract tests with intended behavior; drop redundant SSRF check Three tests broke the coverage-evidence gate because intentional changes landed without updating their assertions (or, for noema, added a redundant guard that shadowed an existing one): - test_call_llm_handles_configuration_and_verdicts: the redundant startswith() scheme guard (#297) was shadowed by the comprehensive, case-insensitive scheme check from #294, was case-sensitive (wrongly rejecting valid HTTPS:// URLs the suite exercises), and leaked the URL in its error (violating the "no original URL in error" intent). Removed the dead guard and its duplicate test block; file:// rejection stays covered by the existing assertion. Restores scripts/ci 100% coverage. - test_workflow_provisions_sandbox_tool_and_reviewer_agent: the opencode-review-target if: block was expanded to a multi-line form in a4df728c (cancel runs on PR close), growing the header past the regex's 240-char proximity window. Widened to 400; timeout-minutes: 360 is unique. - test_merge_scheduler_uses_escalating_mutation_credentials: #338 throttled review_dispatch_limit default from "-1" to "1"; updated the stale assertion. The "-1" empty-input fallback assertion is retained. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01RTAMs4bpSZS77Xe3RQjv9P --- scripts/ci/noema_review_gate.py | 3 --- tests/test_noema_review_gate.py | 5 ----- tests/test_opencode_agent_contract.py | 4 ++-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/scripts/ci/noema_review_gate.py b/scripts/ci/noema_review_gate.py index 621e4506..cb039dba 100644 --- a/scripts/ci/noema_review_gate.py +++ b/scripts/ci/noema_review_gate.py @@ -299,9 +299,6 @@ def call_llm(repo: str, number: int, pr: dict[str, Any], diff: str, truncated: b if ip.is_private or ip.is_loopback or ip.is_link_local or ip.is_multicast or ip.is_unspecified: raise ValueError("URL cannot target internal IP addresses") - if not (api_url.startswith("http://") or api_url.startswith("https://")): - raise ValueError(f"NOEMA_LLM_API_URL must start with http:// or https:// to prevent SSRF vulnerabilities, got: {api_url}") - prompt = { "role": "user", "content": "\n".join( diff --git a/tests/test_noema_review_gate.py b/tests/test_noema_review_gate.py index cc68ff28..e278f063 100644 --- a/tests/test_noema_review_gate.py +++ b/tests/test_noema_review_gate.py @@ -204,11 +204,6 @@ def test_call_llm_handles_configuration_and_verdicts(monkeypatch): monkeypatch.delenv("NOEMA_LLM_API_KEY", raising=False) assert noema.call_llm("owner/repo", 1, pr, "diff", False) is None - monkeypatch.setenv("NOEMA_LLM_API_URL", "file:///etc/passwd") - monkeypatch.setenv("NOEMA_LLM_API_KEY", "secret") - with pytest.raises(ValueError, match="must start with http:// or https://"): - noema.call_llm("owner/repo", 1, pr, "diff", False) - monkeypatch.setenv("NOEMA_LLM_API_URL", "https://llm.example.test/chat") monkeypatch.setenv("NOEMA_LLM_API_KEY", "secret") monkeypatch.setenv("NOEMA_LLM_MODEL", "review-model") diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index dc3cd3a9..13702ba3 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -280,7 +280,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent(): assert '"## Review outcome"' in workflow assert '"## Check outcome"' not in workflow assert "publish REQUEST_CHANGES when coverage-evidence blocker states" in workflow - assert re.search(r"opencode-review-target:[\s\S]{0,240}timeout-minutes: 360", workflow) + assert re.search(r"opencode-review-target:[\s\S]{0,400}timeout-minutes: 360", workflow) assert 'timeout-minutes: 75' in workflow assert re.search(r"Run OpenCode PR Review model pool[\s\S]{0,240}timeout-minutes: 350", workflow) assert 'APPROVAL_CHECK_WAIT_ATTEMPTS: "81"' in workflow @@ -414,7 +414,7 @@ def test_merge_scheduler_uses_escalating_mutation_credentials(): assert "steps.scheduler_app_token.outputs.token" in workflow assert "SCHEDULER_READ_TOKEN: ${{ github.token }}" in workflow assert "SCHEDULER_MUTATION_TOKEN_SOURCE" in workflow - assert 'default: "-1"' in workflow + assert 'default: "1"' in workflow assert 'review_dispatch_limit="-1"' in workflow