Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 22 additions & 2 deletions .github/workflows/opencode-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3413,7 +3413,7 @@ jobs:
"$schema": "https://opencode.ai/config.json",
"model": "nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5",
"small_model": "nvidia-nim/meta/llama-3.3-70b-instruct",
"enabled_providers": ["nvidia-nim", "opencode-free", "opencode", "openai", "openrouter", "github-models"],
"enabled_providers": ["nvidia-nim", "opencode-free", "omniroute", "opencode", "openai", "openrouter", "github-models"],
"lsp": false,
"mcp": {},
"permission": {
Expand Down Expand Up @@ -3600,6 +3600,24 @@ jobs:
}
}
},
"omniroute": {
"npm": "@ai-sdk/openai-compatible",
"name": "OmniRoute Free Gateway",
"options": {
"baseURL": "{env:OMNIROUTE_API_BASE_URL}",
"apiKey": "{env:OMNIROUTE_API_KEY}"
},
"models": {
"combo": {
"name": "OmniRoute Combo (free gateway)",
"tool_call": true,
"limit": {
"context": 200000,
"output": 32768
}
}
}
},
"opencode": {
"npm": "@ai-sdk/openai",
"name": "OpenCode Zen",
Expand Down Expand Up @@ -4037,6 +4055,8 @@ jobs:
# opencode.jsonc expects env NVIDIA_API_KEY for nvidia-nim/* models.
NVIDIA_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY || secrets.NVIDIA_API_KEY }}
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
OMNIROUTE_API_BASE_URL: ${{ vars.OMNIROUTE_API_BASE_URL }}
OMNIROUTE_API_KEY: ${{ secrets.OMNIROUTE_API_KEY }}
SHARE: "false"
NPM_CONFIG_IGNORE_SCRIPTS: "true"
NO_COLOR: "1"
Expand All @@ -4057,7 +4077,7 @@ jobs:
# cost-efficient tier, cheaper than the legacy gpt-5 it replaced
# ($1/$6 vs $1.25/$10 per 1M tokens) so the org OpenAI budget
# stretches further between top-ups.
OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free ' || '' }}nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 nvidia-nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 nvidia-nim/nvidia/nemotron-3-super-120b-a12b nvidia-nim/meta/llama-3.3-70b-instruct nvidia-nim/deepseek-ai/deepseek-v4-pro nvidia-nim/mistralai/codestral-22b-instruct-v0.1 opencode/gpt-5.6-terra github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
OPENCODE_MODEL_CANDIDATES: "${{ needs.validate-pr-metadata.outputs.is_private == 'false' && 'opencode-free/nemotron-3-ultra-free opencode-free/deepseek-v4-flash-free opencode-free/north-mini-code-free opencode-free/laguna-s-2.1-free opencode-free/ling-3.0-flash-free opencode-free/big-pickle opencode-free/mimo-v2.5-free omniroute/combo ' || '' }}nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 nvidia-nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 nvidia-nim/nvidia/nemotron-3-super-120b-a12b nvidia-nim/meta/llama-3.3-70b-instruct nvidia-nim/deepseek-ai/deepseek-v4-pro nvidia-nim/mistralai/codestral-22b-instruct-v0.1 opencode/gpt-5.6-terra github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5 github-models/openai/gpt-5-chat github-models/openai/o3 github-models/deepseek/deepseek-r1-0528 github-models/deepseek/deepseek-r1"
# 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.
Expand Down
13 changes: 12 additions & 1 deletion scripts/ci/run_opencode_review_model_pool.sh
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,13 @@ is_openrouter_candidate() {
esac
}

is_omniroute_candidate() {
case "$1" in
omniroute/*) return 0 ;;
*) return 1 ;;
esac
}

is_nvidia_nim_candidate() {
case "$1" in
nvidia-nim/*) return 0 ;;
Expand Down Expand Up @@ -387,6 +394,10 @@ should_skip_model_candidate() {
printf 'Skipping OpenCode %s because OPENROUTER_API_KEY is not configured; falling back to the next provider-qualified candidate.\n' "$model_candidate"
return 0
fi
if is_omniroute_candidate "$model_candidate" && [ -z "${OMNIROUTE_API_BASE_URL:-}" ]; then
printf 'Skipping OpenCode %s because OMNIROUTE_API_BASE_URL is not configured; falling back to the next provider-qualified candidate.\n' "$model_candidate"
return 0
fi
if is_nvidia_nim_candidate "$model_candidate" && [ -z "${NVIDIA_API_KEY:-}" ]; then
printf 'Skipping OpenCode %s because NVIDIA_API_KEY is not configured; falling back to the next provider-qualified candidate.\n' "$model_candidate"
return 0
Expand All @@ -400,7 +411,7 @@ cap_model_run_timeout() {
local cap_seconds

case "$model_candidate" in
opencode-free/*)
opencode-free/* | omniroute/*)
cap_seconds="$(env_integer_or_default OPENCODE_FREE_RUN_TIMEOUT_SECONDS 3600)"
;;
github-models/openai/gpt-5 | github-models/openai/gpt-5-chat)
Expand Down
7 changes: 7 additions & 0 deletions scripts/ci/test_strix_quick_gate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,11 @@ assert_opencode_review_uses_codegraph_and_gpt5_fallback() {
assert_file_contains "$workflow_file" 'is_private: ${{ steps.validate.outputs.is_private }}' "opencode review carries validated repository privacy into model routing"
assert_file_contains "$workflow_file" '"opencode-free"' "opencode review enables its anonymous Zen free provider"
assert_file_contains "$workflow_file" '"baseURL": "https://opencode.ai/zen/v1"' "opencode review routes the free provider through the official Zen endpoint"
assert_file_contains "$workflow_file" '"omniroute"' "opencode review enables the keyless OmniRoute free gateway provider"
assert_file_contains "$workflow_file" '"baseURL": "{env:OMNIROUTE_API_BASE_URL}"' "opencode review resolves the OmniRoute base URL from an org variable instead of a hardcoded endpoint"
assert_file_contains "$workflow_file" '"apiKey": "{env:OMNIROUTE_API_KEY}"' "opencode review keeps the OmniRoute key optional through env resolution"
assert_file_contains "$workflow_file" 'OMNIROUTE_API_BASE_URL: ${{ vars.OMNIROUTE_API_BASE_URL }}' "opencode review wires the OmniRoute base URL from the org variable"
assert_file_contains "$workflow_file" 'OMNIROUTE_API_KEY: ${{ secrets.OMNIROUTE_API_KEY }}' "opencode review wires the optional OmniRoute key from secrets"
assert_file_contains "$workflow_file" '"north-mini-code-free"' "opencode review declares the current Zen coding model"
assert_file_contains "$workflow_file" "needs.validate-pr-metadata.outputs.is_private == 'false'" "opencode review limits data-retaining free models to public repositories"
assert_file_matches "$workflow_file" 'uses:[[:space:]]+actions/checkout@[0-9a-fA-F]{40}([[:space:]]|$)' "opencode review workflow pins checkout to a full commit SHA"
Expand Down Expand Up @@ -724,6 +729,7 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" '
assert_file_contains "$workflow_file" "Run OpenCode PR Review model pool" "opencode review includes a broad catalog fallback pool"
assert_file_not_contains "$workflow_file" "steps.opencode_review_model_pool.outcome == 'success'" "opencode approval gate still runs after model pool failure to publish a reason"
assert_file_contains "$workflow_file" "opencode-free/north-mini-code-free" "opencode review starts public repository reviews with a free coding model"
assert_file_contains "$workflow_file" "opencode-free/mimo-v2.5-free omniroute/combo ' || ''" "opencode review adds the keyless OmniRoute gateway as an early public-only candidate"
assert_file_contains "$workflow_file" "nvidia-nim/nvidia/llama-3.3-nemotron-super-49b-v1.5 nvidia-nim/nvidia/llama-3.1-nemotron-ultra-253b-v1 nvidia-nim/nvidia/nemotron-3-super-120b-a12b nvidia-nim/meta/llama-3.3-70b-instruct nvidia-nim/deepseek-ai/deepseek-v4-pro nvidia-nim/mistralai/codestral-22b-instruct-v0.1 opencode/gpt-5.6-terra github-models/deepseek/deepseek-v3-0324 openai/gpt-5.6-luna openrouter/deepseek/deepseek-v3.2 openrouter/qwen/qwen3-coder github-models/openai/gpt-4.1 github-models/openai/gpt-5" "opencode review retains paid Zen and DeepSeek V3 before full-size GPT fallbacks"
assert_file_contains "$workflow_file" "The publish gate re-runs source-backed validation against PR-head data" "opencode review publish gate validates model output against the PR-head worktree"
assert_file_contains "$workflow_file" '"openai/o3"' "opencode config declares OpenAI o3 fallback"
Expand Down Expand Up @@ -865,6 +871,7 @@ assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" '
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "OpenCode model pool has no configured model candidates." "opencode model pool fails fast when no candidates are configured"
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "OPENAI_API_KEY is not configured" "opencode model pool skips native OpenAI candidates when the org secret is absent"
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "OPENROUTER_API_KEY is not configured" "opencode model pool skips OpenRouter candidates when the org secret is absent"
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "OMNIROUTE_API_BASE_URL is not configured" "opencode model pool skips the keyless OmniRoute gateway when its base URL is not configured"
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" "configured max cycle count" "opencode model pool exits before the job timeout after configured cycles"
assert_file_contains "$REPO_ROOT/scripts/ci/run_opencode_review_model_pool.sh" 'OPENCODE_TOTAL_RETRY_BUDGET_SECONDS:-1500' "opencode model pool keeps a bounded default retry budget unless the workflow explicitly disables it"
assert_file_not_contains "$workflow_file" "no model produced a valid review control block" "opencode model-failure path no longer documents a final exhausted state"
Expand Down
28 changes: 26 additions & 2 deletions tests/test_opencode_agent_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
"opencode-free/laguna-s-2.1-free "
"opencode-free/ling-3.0-flash-free "
"opencode-free/big-pickle "
"opencode-free/mimo-v2.5-free ' || '' }}"
"opencode-free/mimo-v2.5-free "
"omniroute/combo ' || '' }}"
)
candidates_text = candidates_match.group(1)
assert candidates_text.startswith(conditional_public_candidate)
Expand All @@ -110,6 +111,7 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
"opencode-free/ling-3.0-flash-free",
"opencode-free/big-pickle",
"opencode-free/mimo-v2.5-free",
"omniroute/combo",
*candidates_text.removeprefix(conditional_public_candidate).split(),
]
candidate_pairs = [candidate.split("/", 1) for candidate in candidates]
Expand Down Expand Up @@ -137,6 +139,13 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
["opencode-free", "ling-3.0-flash-free"],
["opencode-free", "big-pickle"],
["opencode-free", "mimo-v2.5-free"],
["omniroute", "combo"],
["nvidia-nim", "nvidia/llama-3.3-nemotron-super-49b-v1.5"],
["nvidia-nim", "nvidia/llama-3.1-nemotron-ultra-253b-v1"],
["nvidia-nim", "nvidia/nemotron-3-super-120b-a12b"],
["nvidia-nim", "meta/llama-3.3-70b-instruct"],
["nvidia-nim", "deepseek-ai/deepseek-v4-pro"],
["nvidia-nim", "mistralai/codestral-22b-instruct-v0.1"],
["opencode", "gpt-5.6-terra"],
["github-models", "deepseek/deepseek-v3-0324"],
["openai", "gpt-5.6-luna"],
Expand Down Expand Up @@ -210,6 +219,16 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
"context": 200000,
"output": 32000,
}
omniroute_provider = generated_config["provider"]["omniroute"]
assert omniroute_provider["options"]["baseURL"] == "{env:OMNIROUTE_API_BASE_URL}"
assert omniroute_provider["options"]["apiKey"] == "{env:OMNIROUTE_API_KEY}"
omniroute_models = omniroute_provider["models"]
assert set(omniroute_models) == {"combo"}
assert omniroute_models["combo"]["tool_call"] is True
assert omniroute_models["combo"]["limit"] == {
"context": 200000,
"output": 32768,
}
for model_name, model_config in free_models.items():
if model_config.get("reasoning") is True:
assert model_config["options"]["reasoningEffort"] == "high", model_name
Expand Down Expand Up @@ -240,6 +259,9 @@ def test_opencode_model_pool_sets_high_effort_for_capable_candidates():
assert '"apiKey": "{env:OPENAI_API_KEY}"' in workflow
assert '"openrouter": {' in workflow
assert '"apiKey": "{env:OPENROUTER_API_KEY}"' in workflow
assert '"omniroute": {' in workflow
assert '"baseURL": "{env:OMNIROUTE_API_BASE_URL}"' in workflow
assert '"apiKey": "{env:OMNIROUTE_API_KEY}"' in workflow
for model_name in direct_openai_models + openrouter_models + github_candidate_models:
assert f'"{model_name}": {{' in workflow

Expand Down Expand Up @@ -1338,7 +1360,8 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
"opencode-free/laguna-s-2.1-free "
"opencode-free/ling-3.0-flash-free "
"opencode-free/big-pickle "
"opencode-free/mimo-v2.5-free ' || ''"
"opencode-free/mimo-v2.5-free "
"omniroute/combo ' || ''"
) in workflow
assert (
"github-models/deepseek/deepseek-v3-0324 "
Expand Down Expand Up @@ -1440,6 +1463,7 @@ def test_workflow_provisions_sandbox_tool_and_reviewer_agent():
assert "is_low_sensitivity_candidate" in model_pool_runner
assert "mini/nano review models are disabled" in model_pool_runner
assert "OPENAI_API_KEY is not configured" in model_pool_runner
assert "OMNIROUTE_API_BASE_URL is not configured" in model_pool_runner
assert "configured max cycle count" in model_pool_runner
assert (
"OpenCode dynamic review cadence selected %ss per attempt" in model_pool_runner
Expand Down
38 changes: 38 additions & 0 deletions tests/test_opencode_model_pool_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,44 @@ def test_free_provider_runtime_cap_preserves_queue_budget(tmp_path: Path) -> Non
) in result.stdout


def test_omniroute_candidate_requires_base_url(tmp_path: Path) -> None:
"""The keyless OmniRoute gateway is skipped when its base URL is not configured."""
result = run_failed_model(
tmp_path,
model_candidates="omniroute/combo",
extra_env={"OMNIROUTE_API_BASE_URL": ""},
)

assert result.returncode == 1
assert (
"Skipping OpenCode omniroute/combo because OMNIROUTE_API_BASE_URL is not "
"configured; falling back to the next provider-qualified candidate."
) in result.stdout
assert (
"OpenCode model pool exhausted before producing a valid control conclusion."
in result.stdout
)

Comment on lines +799 to +816

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

다음 후보 실행을 검증하세요.

현재 후보 목록에는 omniroute/combo만 있습니다. 따라서 러너가 건너뛴 후 즉시 종료해도 이 테스트는 통과합니다.

omniroute/combo 뒤에 실행 가능한 후보를 추가하세요. 그 후보의 시도가 로그에 기록되는지 확인하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_opencode_model_pool_runner.py` around lines 799 - 816, Update
test_omniroute_candidate_requires_base_url to include an additional runnable
model candidate after omniroute/combo, while keeping OMNIROUTE_API_BASE_URL
empty. Assert that the fallback candidate is attempted and recorded in the
output, and retain the existing assertion that the keyless OmniRoute candidate
is skipped.


def test_omniroute_runtime_cap_preserves_queue_budget(tmp_path: Path) -> None:
"""A stalled keyless OmniRoute gateway cannot consume a full paid-provider slot."""
result = run_failed_model(
tmp_path,
extra_env={
"OMNIROUTE_API_BASE_URL": "https://omniroute.example/v1",
"OPENCODE_FREE_RUN_TIMEOUT_SECONDS": "3",
"OPENCODE_RUN_TIMEOUT_SECONDS": "9",
},
model_candidates="omniroute/combo",
)

assert result.returncode == 1
assert (
"OpenCode omniroute/combo runtime cap selected 3s "
"instead of 9s because this provider has a bounded failover window."
) in result.stdout
Comment on lines +818 to +834

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

실제 타임아웃을 발생시키세요.

이 테스트는 FAKE_OPENCODE_HANG_SECONDS를 설정하지 않습니다. 가짜 opencode는 즉시 실패하므로 3초 제한이 실제 실행을 중단하는지 검증하지 않습니다.

가짜 제공자를 3초보다 길게 대기시키세요. 3초 타임아웃 결과와 다음 후보로의 진행을 확인하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_opencode_model_pool_runner.py` around lines 818 - 834, Update
test_omniroute_runtime_cap_preserves_queue_budget to set
FAKE_OPENCODE_HANG_SECONDS to a value longer than the configured 3-second cap,
ensuring the fake OpenCode process is actually interrupted by that cap. Preserve
the existing return-code and runtime-cap assertions, and verify the run proceeds
to the next candidate as expected.



def test_github_models_openai_prompt_references_evidence_without_inlining(
tmp_path: Path,
) -> None:
Expand Down
Loading